如何关闭360主动防御通过JWT防御CSRF

使用JSON Web Tokens和Spring实现微服务 - zzm - ITeye技术网站
博客分类:
 JSON Web Token (JWT) 是一个自我认证的记号,能够包含用户标识、角色和用户权限等信息,能够被任何人方便解析和使用安全的key实现验证,具体可参考。
结合JWT的微服务能够避免会话Session开销,同时方便跨多机分布式系统,这样不需要每个服务都要调用专门的授权服务以确认用户操作本服务的权限。另外一个优点是JWT足够小,能够被序列化附加在请求头部内。
第一个带有用户名和密码的请求提交POST到一个未受保护的登录授权REST端点,一旦用户和密码通过授权,响应中将包含一个JWT,以后的请求都会把这个JWT记号带在HTTP头部中,形式像: Authorization: xxxxx.yyyyy.zzzzz
任何服务到服务的请求会一路传递这个请求头部,这样授权信息都可以应用在一路传递的服务中,这些服务都可以检查这个JWT,以决定是否接受访问。
下面以Spring cloud代码为案例,使用JWT的Java实现:
public class JsonWebTokenUtility {
private SignatureAlgorithm signatureA
private Key secretK
public JsonWebTokenUtility() {
// 这里不是真正安全的实践
// 为了简单,我们存储一个静态key在这里,
// 在真正微服务环境,这个key将会被保留在配置服务器
signatureAlgorithm = SignatureAlgorithm.HS512;
String encodedKey =
"L7A/6zARSkK1j7Vd5SDD9pSSqZlqF7mAhiOgRbgv9Smce6tf4cJnvKOjtKPxNNnWQj+2lQEScm3XIUjhW+YVZg==";
secretKey = deserializeKey(encodedKey);
public String createJsonWebToken(AuthTokenDetailsDTO authTokenDetailsDTO)
String token =
Jwts.builder().setSubject(authTokenDetailsDTO.userId).claim("email",
authTokenDetailsDTO.email)
.claim("roles", authTokenDetailsDTO.roleNames)
.setExpiration(authTokenDetailsDTO.expirationDate)
.signWith(getSignatureAlgorithm(),
getSecretKey()).compact();
private Key deserializeKey(String encodedKey) {
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
new SecretKeySpec(decodedKey, getSignatureAlgorithm().getJcaName());
private Key getSecretKey() {
return secretK
public SignatureAlgorithm getSignatureAlgorithm() {
return signatureA
public AuthTokenDetailsDTO parseAndValidate(String token) {
AuthTokenDetailsDTO authTokenDetailsDTO =
Claims claims =
Jwts.parser().setSigningKey(getSecretKey()).parseClaimsJws(token).getBody();
String userId = claims.getSubject();
String email = (String) claims.get("email");
List roleNames = (List) claims.get("roles");
Date expirationDate = claims.getExpiration();
authTokenDetailsDTO = new AuthTokenDetailsDTO();
authTokenDetailsDTO.userId = userId;
authTokenDetailsDTO.email =
authTokenDetailsDTO.roleNames = roleN
authTokenDetailsDTO.expirationDate = expirationD
} catch (JwtException ex) {
System.out.println(ex);
return authTokenDetailsDTO;
private String serializeKey(Key key) {
String encodedKey =
Base64.getEncoder().encodeToString(key.getEncoded());
return encodedK
现在有了这个工具类,我就可以在每个微服务中设置Spring Security。
现在我们需要一个定制授权过滤器,将能读取请求头部信息,在Spring中已经有一个这样的授权Filter称为:RequestHeaderAuthenticationFilter,我们只要扩展继承即可:
public class JsonWebTokenAuthenticationFilter extends RequestHeaderAuthenticationFilter {
public JsonWebTokenAuthenticationFilter() {
// Don't throw exceptions if the header is missing
this.setExceptionIfHeaderMissing(false);
// This is the request header it will look for
this.setPrincipalRequestHeader("Authorization");
@Autowired
public void setAuthenticationManager(
AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
在这里,头部信息将被转换为Spring Authentication 对象,名称为PreAuthenticatedAuthenticationToken
我们需要一个授权提供者读取这个记号,然偶验证它,然后转换为我们自己的定制授权对象:
public class JsonWebTokenAuthenticationProvider implements AuthenticationProvider {
private JsonWebTokenUtility tokenService = new JsonWebTokenUtility();
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Authentication authenticatedUser =
// Only process the PreAuthenticatedAuthenticationToken
if (authentication.getClass().
isAssignableFrom(PreAuthenticatedAuthenticationToken.class)
&& authentication.getPrincipal() != null) {
String tokenHeader = (String) authentication.getPrincipal();
UserDetails userDetails = parseToken(tokenHeader);
if (userDetails != null) {
authenticatedUser =
new JsonWebTokenAuthentication(userDetails, tokenHeader);
// It is already a JsonWebTokenAuthentication
authenticatedUser =
return authenticatedU
private UserDetails parseToken(String tokenHeader) {
UserDetails principal =
AuthTokenDetailsDTO authTokenDetails =
tokenService.parseAndValidate(tokenHeader);
if (authTokenDetails != null) {
List&GrantedAuthority& authorities =
authTokenDetails.roleNames.stream()
.map(roleName -& new
SimpleGrantedAuthority(roleName)).collect(Collectors.toList());
principal = new User(authTokenDetails.email, "",
authorities);
public boolean supports(Class&?& authentication) {
authentication.isAssignableFrom(
PreAuthenticatedAuthenticationToken.class)||
authentication.isAssignableFrom(
JsonWebTokenAuthentication.class);
使用这些组件,我们现在就可以有使用JWT的标准Spring Securtiy安全机制,当我们进行服务对服务调用时,我们需要一路传输JWT。
这里虚构一个客户端,将JWT作为参数传递:
@FeignClient("user-management-service")
public interface UserManagementServiceAPI {
@RequestMapping(value = "/authenticate", method = RequestMethod.POST)
AuthTokenDTO authenticateUser(@RequestBody AuthenticationDTO authenticationDTO);
@RequestMapping(method = RequestMethod.POST, value = "/roles")
RoleDTO createRole(@RequestHeader("Authorization") String authorizationToken, @RequestBody RoleDTO roleDTO);
@RequestMapping(method = RequestMethod.POST, value = "/users")
UserDTO createUser(@RequestHeader("Authorization") String authorizationToken, @RequestBody UserDTO userDTO);
@RequestMapping(method = RequestMethod.DELETE, value = "/roles/{id}")
void deleteRole(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id);
@RequestMapping(method = RequestMethod.DELETE, value = "/users/{id}")
void deleteUser(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id);
@RequestMapping(method = RequestMethod.GET, value = "/roles")
Collection&RoleDTO& findAllRoles(@RequestHeader("Authorization") String authorizationToken);
@RequestMapping(method = RequestMethod.GET, value = "/users")
Collection&UserDTO& findAllUsers(@RequestHeader("Authorization") String authorizationToken);
@RequestMapping(method = RequestMethod.GET, value = "/roles/{id}", produces = "application/json", consumes = "application/json")
RoleDTO findRoleById(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id);
@RequestMapping(method = RequestMethod.GET, value = "/users/{id}", produces = "application/json", consumes = "application/json")
UserDTO findUserById(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id);
@RequestMapping(method = RequestMethod.GET, value = "/users/{id}/roles")
Collection&RoleDTO& findUserRoles(@RequestHeader("Authorization") String authorizationToken,
@PathVariable("id") int id);
@RequestMapping(method = RequestMethod.PUT, value = "/roles/{id}")
void updateRole(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id,
@RequestBody RoleDTO roleDTO);
@RequestMapping(method = RequestMethod.PUT, value = "/users/{id}")
void updateUser(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id,
@RequestBody UserDTO userDTO);
为了一路传递JWT,在我们自己的控制器中模仿Spring Security实现如下:
private String getAuthorizationToken() {
String token =
Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();
if (authentication != null &&
authentication.getClass().
isAssignableFrom(JsonWebTokenAuthentication.class)) {
JsonWebTokenAuthentication jwtAuthentication =
(JsonWebTokenAuthentication)
token = jwtAuthentication.getJsonWebToken();
正如你看到,在一个分布式微服务环境中,JWT提供了灵活的授权。
项目源码:
/dl/best/json-web-tokens-spring-cloud-microservices.html
浏览: 990810 次
来自: 南京
zhedongxiao 写道用 hashmap 计数有线程问题 ...
用 hashmap 计数有线程问题吗?
解压出来文件不能直接覆盖\indexer\central-ct ...> csrf和xss
Facebook和Dropbox中的CSRF漏洞分析。Facebook给用户提供了一个非常方便的功能,而用户可以通过
Web安全之XSS、CSRF和JWT。跨站脚本(Cross-site scripting,通常简称为XSS)是一种网站应用程序的安
程序猿必读-防范CSRF跨站请求伪造。CSRF(Cross-site request forgery,中文为 跨站请求伪造 )是一种
网站安全之常见攻击方式防止。跨站脚本攻击(Cross Site Script为了区别于CSS简称为XSS)指的是
TOKEN验证防止CSRF攻击的原理。CSRF中文名是跨站请求伪造攻击,黑客可以通过CSRF攻击来伪造
有漏洞?在线教程里的PHP代码被直接复制到程序中,在线教程中的示例代码经常会被人直接
ASP NET MVC 页面使用富文本控件的XSS漏洞问题,目前在做的项目存在XSS安全漏洞!
Web安全之XSS、CSRF和JWT。跨站脚本(Cross-site scripting,通常简称为XSS)是一种网站应用程序的安
一种结合了点击劫持、Self-XSS、复制粘贴劫持的新型XSS攻击。XSS劫持(XSSJacking)是由Dylan Ayr
本文讲述的是XSS平台中keepsession分析,旨在服务社会,供安全研究人员学习使用,请勿用于
互联网安全之如何防范XSS和CSRF两种跨站攻击。差不多刚开始接触前端的时候,经常能看到
Github & CSRF 组合入企业内网的案例,突破点之Github信息泄漏,根据目标企业的一些信息 搜索
近日有网友暴漏了Redis的CSRF漏洞,同时Redis作者在最新发布的3 2 7也进行了修复,本文将对CS
CSRF攻击原理及预防手段。 CSRF全程 Cross Site Request Forgery, 跨站域请求伪造 这种攻击方式相对
新浪微博CSRF事件解析(攻击脚本和流程分析)。最近sina 微博受到了大规模的xss攻击事件
0CTF-WEB Complicated XSS 反思记录。已知条件 前台提交的XSS代码未经过滤,在 http:
government vip
Roundcube 邮件正文存储型XSS(CVE-)。Roundcube 是一款被广泛使用的开源的电子邮件程序,
WordPress audio playlist XSS 漏洞分析。近日 Wordpress 发布了 4 7 3 ,在此次更新中修复了一个 利用
一个有意思的APPLE XSS(CVE-)的 分析与思考。
网站安全之常见攻击方式防止。跨站脚本攻击(Cross Site Script为了区别于CSS简称为XSS)指的是
很多Web应用会有防止跨站请求伪造的策略,比如通过request请求传一个当前页面有效或者当
CSRF防御之验证码
目前,验证码被认为是对抗CSRF攻击最简单有效的防御措施。用到验证码
关于 CSRF 的攻击防御,网上已经给出了很多答案,我推荐 《CSRF 攻击的应对之道》。文章列
经常看到在项目中ajax post提交数据到服务器不加防伪标记,造成CSRF攻击,在ajax提交数据的
提交表单报400错误,提示 “您提交的数据无法验证”。原来是csrf验证的问题,因为表单是
米斯特白帽培训讲义 漏洞篇 XSS。跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Ca
KindEditor开源富文本编辑框架XSS漏洞。KindEditor 是一套开源的在线HTML编辑器,主要用于让用
使用脚本隔离技术对抗XSS攻击,在圣诞节之后,我与@mikewest和@fgrx讨论了关于使用“隔离”
互联网安全之如何防范XSS和CSRF两种跨站攻击。差不多刚开始接触前端的时候,经常能看到
美国DHS发布《“灰熊草原”网络攻击活动深入分析报告》。GRIZZLY STEPPE Activity),与上次的
热门文章热门标签
06月26日 |
06月26日 |
06月26日 |
06月26日 |
06月26日 |
06月26日 |
06月26日 |
06月26日 |前端,产品,管理
单点登录是我比较喜欢的一个技术解决方案,一方面他能够提高产品使用的便利性,另一方面他分离了各个应用都需要的登录服务,对性能以及工作量都有好处。自从上次研究过JWT如何应用于会话管理,加之以前的项目中也一直在使用这个比较流行的单点登录框架,所以就一直在琢磨如何能够把JWT跟单点登录结合起来一起使用,尽量能把两种技术的优势都集成到项目中来。本文介绍我从CAS思考得出的SSO的实现方案。
其实CAS这个方案很好,非常强大,它最新的版本已经集成JWT了,所以要是不想自己开发单点登录的服务的话,完全可以考虑使用CAS。但是我认为,我们在做项目的时候,也许一开始并不需要这么强大的产品,CAS提供的登录形式有很多,而我们只需要应用其中的一种;而且它这个框架正是因为强大,所以也会比较复杂,简单上手容易,但是遇到一些特殊的需求,比如我们想在CAS里面加入微信登录,那就需要对它的原理以及API有比较深入的了解才行。综合考虑,还是弄清楚CAS的原理,自己来实现一个基本的SSO服务比较放心。
本文的内容需要对JWT和SSO有一个基本的了解,你可以从这两篇文章来了解JWT的用途:,还可以从下面的资料来了解SSO的内容:。
本文主要是通过时序图的方式来介绍JWT SSO的实现原理,具体的技术实现暂时还没有,不过当你理解了这个方案的原理后,你会觉得最终的实现并不会特别复杂,你可以用任意的平台语言来实现它。下面的时序图,模拟了三个服务,分别是CAS、系统A、系统B,它们分别部署在,和;CAS这个服务用来管理SSO的会话;系统A和系统B代表着实际的业务系统。我从五个场景分别来说明这个SSO方案的实现细节。下面先来看第一个。
场景一:用户发起对业务系统的第一次访问,假设他第一次访问的是系统A的some/page这个页面,它最终成功访问到这个页面的过程是:
在这个过程里面,我认为理解的关键点在于:
1. 它用到了两个cookie(jwt和sid)和三次重定向来完成会话的创建和会话的传递;
1. jwt的cookie是写在这个域下的,所以每次重定向到的时候,jwt这个cookie只要有就会带过去;
2. sid的cookie是写在这个域下的,所以每次重定向到的时候,sid这个cookie只要有就会带过去;
3. 在验证jwt的时候,如何知道当前用户已经创建了sso的会话?因为jwt的payload里面存储了之前创建的sso 会话的session id,所以当cas拿到jwt,就相当于拿到了session id,然后用这个session id去判断有没有的对应的session对象即可。
还要注意的是:CAS服务里面的session属于服务端创建的对象,所以要考虑session id唯一性以及session共享(假如CAS采用集群部署的话)的问题。session id的唯一性可以通过用户名密码加随机数然后用hash算法如md5简单处理;session共享,可以用memcached或者redis这种专门的支持集群部署的缓存服务器管理session来处理。
由于服务端session具有生命周期的特点,到期需自动销毁,所以不要自己去写session的管理,免得引发其它问题,到github里找开源的缓存管理中间件来处理即可。存储session对象的时候,只要用session id作为key,session对象本身作为value,存入缓存即可。session对象里面除了session id,还可以存放登录之后获取的用户信息等业务数据,方便业务系统调用的时候,从session里面返回会话数据。
场景二:用户登录之后,继续访问系统A的其它页面,如some/page2,它的处理过程是:
从这一步可以看出,即使登录之后,也要每次跟CAS校验jwt的有效性以及会话的有效性,其实jwt的有效性也可以放在业务系统里面处理的,但是会话的有效性就必须到CAS那边才能完成了。当CAS拿到jwt里面的session id之后,就能到session 缓存服务器里面去验证该session id对应的session对象是否存在,不存在,就说明会话已经销毁了(退出)。
场景三:用户登录了系统A之后,再去访问其他系统如系统B的资源,比如系统B的some/page,它最终能访问到系统B的some/page的流程是:
这个过程的关键在于第一次重定向的时候,它会把sid这个cookie带回给CAS服务器,所以CAS服务器能够判断出会话是否已经建立,如果已经建立就跳过登录页的逻辑。
场景四:用户继续访问系统B的其它资源,如系统B的some/page2:
这个场景的逻辑跟场景二完全一致。
场景五:退出登录,假如它从系统B发起退出,最终的流程是:
最重要的是要清除sid的cookie,jwt的cookie可能业务系统都有创建,所以不可能在退出的时候还挨个去清除那些系统的cookie,只要sid一清除,那么即使那些jwt的cookie在下次访问的时候还会被传递到业务系统的服务端,由于jwt里面的sid已经无效,所以最后还是会被重定向到CAS登录页进行处理。
以上方案两个关键的前提:
1. 整个会话管理其实还是基于服务端的session来做的,只不过这个session只存在于CAS服务里面;
2. CAS之所以信任业务系统的jwt,是因为这个jwt是CAS签发的,理论上只要认证通过,就可以认为这个jwt是合法的。
jwt本身是不可伪造,不可篡改的,但是不代表非法用户冒充正常用法发起请求,所以常规的几个安全策略在实际项目中都应该使用:
1. 使用https
2. 使用http-only的cookie,针对sid和jwt
3. 管理好密钥
4. 防范CSRF攻击。
尤其是CSRF攻击形式,很多都是钻代码的漏洞发生的,所以一旦出现CSRF漏洞,并且被人利用,那么别人就能用获得的jwt,冒充正常用户访问所有业务系统,这个安全问题的后果还是很严重的。考虑到这一点,为了在即使有漏洞的情况将损害减至最小,可以在jwt里面加入一个系统标识,添加一个验证,只有传过来的jwt内的系统标识与发起jwt验证请求的服务一致的情况下,才允许验证通过。这样的话,一个非法用户拿到某个系统的jwt,就不能用来访问其它业务系统了。
在业务系统跟CAS发起attach/validate请求的时候,也可以在CAS端做些处理,因为这个请求,在一次SSO过程中,一个系统只应该发一次,所以只要之前已经给这个系统签发过jwt了,那么后续 同一系统的attach/validate请求都可以忽略掉。
总的来说,这个方案的好处有:
1. 完全分布式,跨平台,CAS以及业务系统均可采用不同的语言来开发;
2. 业务系统如系统A和系统B,可实现服务端无状态
3. 假如是自己来实现,那么可以轻易的在CAS里面集成用户注册服务以及第三方登录服务,如微信登录等。
它的缺陷是:
1. 第一次登录某个系统,需要三次重定向(不过可以优化成两次);
2. 登录后的后续请求,每次都需要跟CAS进行会话验证,所以CAS的性能负载会比较大
3. 登陆后的后续请求,每次都跟CAS交互,也会增加请求响应时间,影响用户体验。
本文从理论层面介绍了结合jwt来实现SSO的方案原理,希望它能帮助一些朋友更好的理解SSO以及它的实现方法。本文方案参考自CAS的实现流程,你可以从下面这个资料了解CAS的单点登录实现过程:
它的流程跟我这个差别不是特别大,但是从清晰层面来说,我写的还是要更明了一些,所以对比起来阅读,可能理解会更透彻些。
另外,这个方案考虑地不一定很全面,所以要是您发现了其中的问题,还请您帮忙指正,非常感谢:)
阅读(...) 评论()JWT 在前后端分离中的应用与实践 - CNode技术社区
这家伙很懒,什么个性签名都没有留下。
本文主要介绍JWT()授权机制在前后端分离中的应用与实践,包括以下三部分:
JWT原理介绍
JWT的安全性
React.js+Flux架构下的实践()
0 关于前后端分离
前后端分离是一个很有趣的议题,它不仅仅是指前后端工程师之间的相互独立的合作分工方式,更是前后端之间开发模式与交互模式的模块化、解耦化。计算机世界的经验告诉我们,对于复杂的事物,模块化总是好的,无论是后端API开发中越来越成为规范的风格,还是Web前端越来越多的模板、框架(参见),包括移动应用中前后端天然分离的特质,都证实了前后端分离的重要性与必要性(更生动的细节与实例说明可以参看赫门分享的主题)。
实现前后端分离,对于后端开发人员来说是一件很幸福的事情,因为不需要再考虑怎样在HTML中套入数据,只关心数据逻辑的处理;而前端则需要承担接收数据之后界面呈现、用户交互、数据传递等所有任务。虽然这看起来加重了前端的工作量,但实际上有越来越多丰富多样的前端框架可供选择,这让前端开发变得越来越结构化、系统化,前端工程师也不再只是“套版的”。
在所有前端框架中,Facebook推出的无疑是当下最热门(之一),然而React只负责界面渲染层面,相当于MVC中的V(View),因此只靠React无法完成一个完整的单页应用()。Facebook另外推出与之配套的架构,主要为了避免Angular.js之类MVC的架构模式,规避数据双向绑定而采用单向绑定的数据传递方式。实际上React无论是学习还是使用都是非常简单的,而Flux则需要花更多时间去理解消化,本文第3部分我采用Flux架构的一种实现,做了一个基于JWT授权机制的登入、登出的例子,顺便介绍Flux架构的细节。
1 JWT 介绍及其原理
JWT是我之前做Android应用的时候了解到的一种用户授权机制,虽然原生的移动手机应用与基于浏览器的Web应用之间存在很多差异,但很多情况下后端往往还是沿用已有的架构跟代码,所以用户授权往往还是采用Cookie+Session的方式,也就是需要原生应用中模拟浏览器对Cookie的操作。
Cookie+Session的存在主要是为了解决HTTP这一无状态协议下服务器如何识别用户的问题,其原理就是在用户登录通过验证后,服务端将数据加密后保存到客户端浏览器的Cookie中,同时服务器保留相对应的Session(文件或DB)。用户之后发起的请求都会携带Cookie信息,服务端需要根据Cookie寻回对应的Session,从而完成验证,确认这是之前登陆过的用户。其工作原理如下图所示:
JWT是提出的通过对JSON进行加密签名来实现授权验证的方案,编码之后的JWT看起来是这样的一串字符:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
由.分为三段,通过解码可以得到:
// 1. Headers
// 包括类别(typ)、加密算法(alg);
&alg&: &HS256&,
&typ&: &JWT&
// 2. Claims
// 包括需要传递的用户信息;
&sub&: &&,
&name&: &John Doe&,
&admin&: true
// 3. Signature
// 根据alg算法与私有秘钥进行加密得到的签名字串;
// 这一段是最重要的敏感信息,只能在服务端解密;
HMACSHA256(
base64UrlEncode(header) + &.& +
base64UrlEncode(payload),
SECREATE_KEY
在使用过程中,服务端通过用户登录验证之后,将Header+Claim信息加密后得到第三段签名,然后将签名返回给客户端,在后续请求中,服务端只需要对用户请求中包含的JWT进行解码,即可验证是否可以授权用户获取相应信息,其原理如下图所示:
通过比较可以看出,使用JWT可以省去服务端读取Session的步骤,这样更符合RESTful的规范。但是对于客户端(或App端)来说,为了保存用户授权信息,仍然需要通过Cookie或类似的机制进行本地保存。因此JWT是用来取代服务端的Session而非客户端Cookie的方案,当然对于客户端本地存储,HTML5提供了Cookie之外更多的解决方案(localStorage/sessionStorage),究竟采用哪种存储方式,其实从Js操作上来看没有本质上的差异,不同的选择更多是出于安全性的考虑。
2 JWT 安全性
用户授权这样敏感的信息,安全性当然是首先需要考虑的因素。这里主要讨论在使用JWT时如何防止XSS和XSRF两种攻击。
XSS是Web中最常见的一种漏洞(我们的**学报官网就存在这个漏洞这件事我就不说了=.=),其主要原因是对用户输入信息不加过滤,导致用户(被误导)恶意输入的Js代码在访问该网页时被执行,而Js可以读取当前网站域名下保存的Cookie信息。针对这种攻击,无论是Cookie还是localStorage中的信息都有可能被窃取,但防止XSS也相对简单一些,对用户输入的所有信息进行过滤即可。另外,现在越来越多的CDN服务,让我们可以节省服务器流量,但同时也有可能引入不安全的Js脚本,例如前段时间Github被Great Cannon轰击的案例,则需要提高对某度之类服务的警惕。
另外一种更加棘手的XSRF漏洞主要利用Cookie是按照域名存储,同时访问某域名时浏览器会自动携带该域名所保存的Cookie信息这一特征。如果执意要将JWT存储在Cookie中,服务端则需要额外验证请求来源,或者在提交表单中加入随机签名并在处理表单时进行验证。
我在后面的实例中采用将JWT保存在localStorage中的方案,请求时将JWT放入Request Header中的Authorization位。对JWT安全性问题想要了解更多可以参考下面几篇文章:
3 React-jwt Example
本节源码可见。
前面提到的React.js框架学习成本其实非常低,只要跟着官方教程走一遍,搞清楚props、states、virtual DOM几个概念,就可以开始用了。但是只有View层什么都做不了,Facebook推出配套的Flux架构,一开始看到下面这张架构图,当时我就懵逼了。
好在Flux只是一种理论架构,虽然官方也提供了实现方案,但是我更倾向于的实现方式,如下图所示:
其中View Components即视图层由React负责,Stores用于存储数据,Actions则用于监听所有动作,所有数据的传递都是单向绑定的,在分割不同模块时,可以清楚地看到数据的流动方向。
我尝试写了一个简单的登录、登出以及获取用户个人数据的例子,除了Reflux之外,还用到如下模块:
: SPA路由;
: React化的Bootstrap,UI样式;
: Ajax请求;
: 客户端的JWT解码;
另外服务端API采用框架,依赖于。代码目录结构如下:
tree -I 'node_modules|.git'
├── README.md
├── gulpfile.js
├── index.html
├── package.json
├── scripts
├── actions
└── actions.js
├── app.js
├── build
└── dist.js
├── components
└── HelloWorld.js
├── stores
├── loginStore.js
└── userStore.js
└── views
├── home.js
├── login.js
└── profile.js
└── server.go
完整的页面放在view中,可复用的组件放在components,用户的动作包括login、logout以及getBalance,因此需要创建相应的action来监听这些动作:
// actions.js
var actions = Reflux.createActions({
&login&: {},
&updateProfile&: {}, // login成功更新用户数据
&loginError&: {}, // login失败错误信息
&logout&: {},
&getBalance&: {asyncResult: true}
actions.login.listen(function(data){});
用户点击view中的Submit Button时,将表单信息提交给login action:
// views/login.js
var Login = React.createClass({
login: function (e) {
e.preventDefault();
actions.login({
name: this.refs.name.getValue(),
pass: this.refs.pass.getValue(),
// actions.js
= require('reqwest');
actions.login.listen(function(data){
url: HOST+&/user/token&,
method: &post&,
data: JSON.stringify(data),
type: 'json',
contentType: 'application/json',
headers: {'X-Requested-With': 'XMLHttpRequest'},
success: function (resp) {
if(resp.code == 200){
actions.updateProfile(resp.jwt)
actions.updateProfile(resp.msg)
根据API返回结果,将再次触发updateProfile或updateProfile action,而分别由userStore和loginStore接收:
// stores/userStore.js
var userStore = Reflux.createStore({
listenables: actions, // 声明userStore所监听的action
updateProfile: function(jwt){
// 注册监听actions.updateProfile
localStorage.setItem('jwt', jwt);
this.user = jwt_decode(jwt);
this.user.logd =
this.trigger(this.user);
// stores/loginStore.js
var loginStore = Reflux.createStore({
listenables: actions,
loginError: function(msg){
this.trigger(msg);
store接收action数据后,通过this.trigger(msg)将处理过后的数据重新传递会view:
var Login = React.createClass({
mixins : [
Router.Navigation,
Reflux.listenTo(userStore, 'onLoginSucc'),
Reflux.listenTo(loginStore, 'onLoginErr')
onLoginSucc: function(){
// 登录成功,跳转回首页
this.transitionTo('home');
onLoginErr: function (msg) {
// 登录失败,显示错误信息
this.setState({
errorMsg: msg,
至此,从用户点击登录到登录结果传回,整个流程数据在View-&Action-&Store-&View中完成单向传递,这就是Flux架构的基本概念。
在完成登录后,API会将验证通过的JWT传回:
// server.go
token := jwt.New(jwt.SigningMethodHS256)
// Headers
token.Header[&alg&] = &HS256&
token.Header[&typ&] = &JWT&
// Claims
token.Claims[&name&] = validUser.Name
token.Claims[&mail&] = validUser.Mail
token.Claims[&exp&] = time.Now().Add(time.Hour * 72).Unix()
tokenString, err := token.SignedString([]byte(mySigningKey))
if err != nil {
c.JSON(200, gin.H{&code&: 500, &msg&: &Server error!&})
c.JSON(200, gin.H{&code&: 200, &msg&: &OK&, &jwt&: tokenString})
当登录之后的用户在profile页面发起getBalance请求时,存储于本地的jwt将一起传递,我这里采用Header的方式传递,具体取决于API端的协议:
// actions.js
actions.getBalance.listen(function(){
var jwt = localStorage.getItem('jwt');
url: HOST+&/user/balance&,
method: &post&,
type: &json&,
headers: {
'Authorization': &Bearer &+jwt,
success: function (resp) {
if (resp.code == 200) {
actions.updateProfile(resp.jwt);
actions.loginError(resp.msg);
而服务端面对任何需要验证权限的请求需要通过Token验证:
//server.go
token, err := jwt.ParseFromRequest(c.Request, func(token *jwt.Token) (interface{}, error) {
b := ([]byte(mySigningKey))
return b, nil
有两个问题想问一下,本人web开发新人,如果有概念性的错误也请指教。
1 localStorage.setItem(‘jwt’, jwt); 所以jwt是存在localStorge里了。那么如果我通过浏览器的一些功能,如chrome的developer tools,就可以拿到jwt(尽管payload可能已经加密过)。然后在这个JWT没有过期前,我把它放在另外一个client的请求中,验证是还可以通过的。这样我的系统就不再安全了。如何解决这个问题呢?
2 如何实现一个客户端自动刷新token的功能,毕竟如果一个用户一直在浏览我的站点,他的token是应该自动延长过期时间的,否则就需要重新登录。我也Google了一些资料,有说用refresh token机制的,还有说每个请求都更新token过期时间的,哪个更好?有没有demo的相关code、
CNode 社区为国内最专业的 Node.js 开源技术社区,致力于 Node.js 的技术研究。
服务器赞助商为
,存储赞助商为
,由提供应用性能服务。
新手搭建 Node.js 服务器,推荐使用无需备案的

我要回帖

更多关于 如何防御ddos攻击 的文章

 

随机推荐