JWT,Json web token。因为在项目中准备应用,总结下,理清思路,希望对团队有帮助。除简介外,都融合了我个人的理解,不希望对别人产生误导。网络让自媒体很容易,一定加以分析,全盘照收是很危险的;即使是读书,也要分析,尽信书不如无书。
简介
参考《jwt简介》,我认为很基础,原文翻译,比较客观,就不重复了。
JWT应用场景?
简介中提到两个场景,我认为主要第一种--身份认证。为什么采用这种方式呢?我总结了下
1、json格式简单,相比xml,我更喜欢json;Self-contained,一般都翻译成自包含,意思是jwt中已经有了你需要的全部信息,拿出来用就行。
2、同session相比,性能更好一些,省去了处理分布session的问题;对于大行其道的restful来讲,支持得很好;浏览器+app+pc,这种多终端开发,是很好的选择。
如果可以,新系统不再使用session保存用户信息。对于我们自己的platina开发平台,可以平滑过渡到jwet,让开发人员感觉不到。
JWT有什么好处?
1、支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输.
2、无状态(也称:服务端可扩展行):Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息.
4、更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),而你的服务端只要提供API即可.
5、去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可.
6、更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。
7、CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。
8、性能: 一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算 的Token验证和解析要费时得多.
9、不需要为登录页面做特殊处理: 如果你使用Protractor 做功能测试的时候,不再需要为登录页面做特殊处理.
10、基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft).
流程
1、用户认证。认证方式可能很多,自己认证或者sso。
2、认证后,服务器构造JWT。
3、把JWT返回客户端,客户端存储。
4、客户端访问服务器,带上JWT。
5、服务器端判断JWT是否正确并且没有超时,正常,向下流转;否则,转到授权。
服务器端
服务器端主要有2件事,我结合java说明下,j wt.io上介绍了几个java类库,根据个人喜好选择。之前使用一个sso,用Nimbus,也支持jwt,但是他却依赖net.minidev.json.JSONObject,就放弃了;后来采用jjwt,依赖jackson。
1、生成jwt
1.1、生成jwt的时机,认证之后,返回认证结果之前。
1.2、Payload(很多翻译是直译,对于理解没有任何帮助)中如何设计属性,这个对于可读性比较重要。
根据JWT的标准,这些claims可以分为以下三种类型:
a. Reserved claims(保留),它的含义就像是编程语言的保留字一样,属于JWT标准里面规定的一些claim。JWT标准里面定好的claim有:
iss(Issuser):代表这个JWT的签发主体;
sub(Subject):代表这个JWT的主体,即它的所有人;
aud(Audience):代表这个JWT的接收对象;
exp(Expiration time):是一个时间戳,代表这个JWT的过期时间;
nbf(Not Before):是一个时间戳,代表这个JWT生效的开始时间,意味着在这个时间之前验证JWT是会失败的;
iat(Issued at):是一个时间戳,代表这个JWT的签发时间;
jti(JWT ID):是JWT的唯一标识。
b. Public claims,略(不重要)
c. Private claims,这个指的就是自定义的claim。比如前面那个结构举例中的admin和name都属于自定的claim。这些claim跟JWT标准规定的claim区别在于:JWT规定的claim,JWT的接收方在拿到JWT之后,都知道怎么对这些标准的claim进行验证;而private claims不会验证,除非明确告诉接收方要对这些claim进行验证以及规则才行。
1.3、生成jwt的代码
SecretKeySpec key = getKey();//getKey自己处理
Map claims = new HashMap();
claims.put("uid", userInfo.getAccountId()+"");
claims.put("user_name", userInfo.getUserName());
claims.put("nick_name",userInfo.getUserAttribute(PlatinaUser.NICK_NAME));
JwtBuilder builder = Jwts.builder()
.setClaims(claims)----一定先于下面的set方法,否则覆盖。
.setIssuer("bmtech.com")
.setSubject(userInfo.getUserNum())
.setNotBefore(now)
.signWith(signatureAlgorithm, key);
return builder.compact();
2、处理jwt
Jwtjwt = Jwts.parser().setSigningKey(getKey()).parse(token);
Claims claims = jwt.getBody();
客户端
jwt存储方式自己灵活掌握。
web:cookie/localStorage/sessionStorage/;
app:内存
安全
JWT是否安全?
既然jwt被很多大型公司采用,安全性一定是有保证的。
要保证私钥的安全性,来保障签名的安全。
有人提到jwt暴露签名算法(alg),而且会有None(无签名),所以建议隐去alg。一般的协议制定都会考虑扩展性和普适性。但是我们在应用中可以采用我们默认的算法,而不是根据alg去处理。
常见安全问题及处理?
1、XSS(Cross Site Script)
把token存储在cookie中,同时设置httpOnly。
2、CSRF(cross-site request forgery)
2.1、判断reffer。系统改动最小,通过filter就可以完成。
2.2、在参数中传token。
2.3、通过header传递token。(推荐)