JWT在身份认证方面的应用

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。(推荐)

推荐阅读更多精彩内容