使用Json Web Token设计Passport系统

1、Token Auth机制

基于Token的身份验证是无状态的,咱们不将用户信息存在服务器或Session中。html

相比原始的Cookie+Session方式,更适合分布式系统的用户认证,绕开了传统的分布式Session一致性等问题。前端

基于Token的身份验证的主流程以下:java

用户经过用户名和密码发送请求;
程序验证;
程序返回一个签名的token 给客户端;
客户端储存token,而且每次用于每次发送请求。git

2、相比Cookie认证的优点

支持跨域跨站点访问:web

Cookie是不容许垮域访问的,能够经过设置顶级域名的方式实现部分跨域,可是跨站点的访问仍然不支持,
若是使用Token机制,就能够经过HTTP头传输用户认证信息,从而更好的实现跨域跨站点。算法

无状态:json

Token机制在服务端不须要存储session信息,Token自身包含了登陆用户的信息,只须要在客户端的cookie或本地介质存储状态信息;跨域

去耦:不须要绑定到一个特定的身份验证方案。Token能够在任何地方生成,只要在你的API被调用的时候,你能够进行Token生成调用便可;浏览器

更适用于移动应用:缓存

当客户端是原生应用时,Cookie是不被支持的,虽然目前Webview的方式能够解决Cookie问题,

可是显然采用Token认证机制会简单得多;

安全性更强:

由于再也不依赖于Cookie,因此你就不须要考虑对CSRF(跨站请求伪造)的防范;

标准化易扩展:

能够采用标准化的 JSON Web Token (JWT),对之后系统接入Node等纯前端开发更便捷;

相比Session一致性提升性能:

相比服务端保存Session一致性信息,并查询用户登陆状态,通常来讲Token的验证过程(包含加密和解密),性能开销会更小。

3、JSON Web Token标准的设计

JWT 标准的 Token 有三个部分:

header.payload.signature

三个部分中间用点分隔开,而且都使用 Base64 编码,因此生成的 Token 相似这样:

ewogICJ0eXAiOiAiSldUIiwKICAiYWxnIjogIkhTMjU2Igp9.ewogImlzcyI6ICJjaGJsb2dzLmNvbSIsCiAiZXhwIjogIjE0NzA3MzAxODIiLAogInVpZCI6ICIxMjM0NWFiY2RlIiwKfQ.9q2eq8sa374ao2uq9607r6qu6

(1)Header报头

header 部分主要包括两部分,一个是 Token 的类型,另外一个是使用的算法,
好比下面类型就是 JWT,使用的算法是 HS256。

{
"typ": "JWT",
"alg": "HS256"
}

Header内容要用 Base64 的形式编码,因此就变成这样:
ewogICJ0eXAiOiAiSldUIiwKICAiYWxnIjogIkhTMjU2Igp9

(2)Payload载荷部分

Payload 里面是 Token 的具体内容,这部份内容能够自定义,JWT有标准字段,也能够添加其它须要的内容。
标准字段:
iss:Issuer,发行者
sub:Subject,主题
aud:Audience,观众
exp:Expiration time,过时时间
nbf:Not before
iat:Issued at,发行时间
jti:JWT ID

这是一个典型的payload信息,包含了发行者(网站)、过时时间和用户id:
{
"iss": "chblogs.com",
"exp": "1470730182",
"uid": "12345abcde",
}

这部份内容一样要用Base64 编码,生成编码相似以下格式:

ewogImlzcyI6ICJjaGJsb2dzLmNvbSIsCiAiZXhwIjogIjE0NzA3MzAxODIiLAogInVpZCI6ICIxMjM0NWFiY2RlIiwKfQ==

(3)Signature签名部分

签名部分主要和token的安全性有关,Signature的生成依赖前面两部分。
首先将Base64编码后的Header和Payload用.链接在一块儿,

ewogICJ0eXAiOiAiSldUIiwKICAiYWxnIjogIkhTMjU2Igp9.ewogImlzcyI6ICJjaGJsb2dzLmNvbSIsCiAiZXhwIjogIjE0NzA3MzAxODIiLAogInVpZCI6ICIxMjM0NWFiY2RlIiwKfQ


对这个字符串使用HmacSHA256算法进行加密,这个密钥secret存储在服务端,前端不可见,

String str="ewogICJ0eXAiOiAiSldUIiwKICAiYWxnIjogIkhTMjU2Igp9."
				+ "ewogImlzcyI6ICJjaGJsb2dzLmNvbSIsCiAiZXhwIjogIjE0NzA3MzAxODIiLAogInVpZCI6ICIxMjM0NWFiY2RlIiwKfQ";
    	byte[] inputData = str.getBytes();  
		String key = Coder.initMacKey();  
		BigInteger sha = new BigInteger(Coder.encryptHMAC(inputData, key));  
		System.out.println("HS256加密后——"+sha.toString(32));

  


下面使用密钥 THISSHA 进行加密:
9q2eq8sa374ao2uq9607r6qu6

而后将Signature和前面两部分拼接起来,获得最后的token:

ewogICJ0eXAiOiAiSldUIiwKICAiYWxnIjogIkhTMjU2Igp9.ewogImlzcyI6ICJjaGJsb2dzLmNvbSIsCiAiZXhwIjogIjE0NzA3MzAxODIiLAogInVpZCI6ICIxMjM0NWFiY2RlIiwKfQ.9q2eq8sa374ao2uq9607r6qu6

4、JWT认证的实现

常规的token保存在sessionStorage或者localStorage中,每次请求时将token加在http请求的Header中,

下面是典型的token认证方式:

1.客户端登陆时经过帐号和密码到服务端进行认证,认证经过后,服务端经过持有的密钥生成Token,Token中通常包含失效时长和用户惟一标识,如用户ID,服务端返回Token给客户端;
2.客户端保存服务端返回的Token;
3.客户端进行业务请求时在Head的Authorization字段里面放置Token,如: 
Authorization: Bearer Token 
4.服务端对请求的Token进行校验,若是Token不是存放在Cookie中,须要解决用户主动注销,但设置的过时时间并未过时问题。

用户注销时能够把还在失效内的Token储存在Redis等缓存中,验证时查找Token是否存在,若是Token在Redis中存在,则说明用户已注销;若是Token不存在,则校验经过。 
5.服务端能够经过从Token取得的用户惟一标识进行相关权限的校验,并把此用户标识赋予到请求参数中,业务可经过此用户标识进行业务处理; 

还有一种方式是把token保存在Cookie中,这时就不须要在服务端保存token的值,用户注销时直接清除Cookie就能够,

这种方式不须要在服务端储存token的值,认证过程以下:


5、JWT标准的安全性

(1)如何访问CSRF攻击

CSRF (Cross Site Request Forgery),指在一个浏览器中打开了两个标签页,其中一个页面经过窃取另外一个页面的 cookie 来发送伪造的请求,由于 cookie 是随着请求自动发送到服务端的。

(2)如何保证token的安全性

客户端不须要持有密钥,由服务端经过密钥生成Token;

在JWT中,不该该在Payload里面加入任何敏感的数据,如用户密码等信息,由于payload并无作加密,只是一个Base64的编码,
攻击者拿到token之后就能够获得用户敏感信息;

 

参考资料:

基于 Token 的身份验证

JSON Web Token - 在Web应用间安全地传递信息

相关文章
相关标签/搜索