分析JWT的用法,根据其优缺点找出一种合适的实战方案,并将其引入到Spring Security中。
一、传统的session认证
在服务器存储一份用户登录信息,这份登录信息会在响应时传递给浏览器,告诉其保存cookie,以便下次请求时发送给我们的应用。
Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。
CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。
一、简析JWT
1.JWT简介
定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。
1)简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快
2)自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
2.JWT的结构
JWT包含了使用.分割的三部分:Header(头部)、Payload(载荷)、Signature(签名)。其结构看起来是这样的:
xxx.yyy.zzz
Header
在header中包含两部分:token类型和采用的加密算法,对这部分内容使用Base64Url编码后作为JWT结构的第一部分。
{
"alg": "HS256",
"typ": "JWT"
}
Payload
载荷包含三种类型的claim:reserved、public和private。将载荷经过Base64Url编码后作为JWT结构的第二部分。
Reserved claims: 这些claim是JWT预先定义的,在JWT中并不会强制使用它们,而是推荐使用,常用的有 iss(Issuser,签发者), exp(Expiration time,过期时间戳), sub(Subject,面向的用户), aud(Audience,接收方), iat(Issued at,签发时间),nbf(Not Before):是一个时间戳,代表这个JWT生效的开始时间,意味着在这个时间之前验证JWT是会失败的。
Public claims:根据需要定义自己的字段,注意应该避免冲突
Private claims:这些是自定义的字段,可以用来在双方之间交换信息
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
Signature
创建签名需要使用编码后的header、payload以及一个秘钥,使用header中指定签名算法进行签名。如下使用HMAC SHA256算法:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
签名用于验证消息的发送者以及消息是没有经过篡改的。
在身份鉴定的实现中,传统方法是在服务端存储一个session,给客户端返回一个cookie,而使用JWT之后,当用户使用它的认证信息登陆系统之后,会返回给用户一个JWT,用户只需要本地保存该token(通常使用local storage,也可以使用cookie)即可。
当用户希望访问一个受保护的路由或者资源的时候,通常应该在Authorization头部使用Bearer模式添加JWT,其内容看起来是下面这样:
Authorization: Bearer <token>
因为用户的状态在服务端的内存中是不存储的,所以这是一种无状态的认证机制。服务端的保护路由将会检查请求头Authorization中的JWT信息,如果合法,则允许用户的行为。由于JWT是自包含的,因此减少了需要查询数据库的需要。
JWT的这些特性使得我们可以完全依赖其无状态的特性提供数据API服务,甚至是创建一个下载流服务。因为JWT并不使用Cookie的,所以你可以使用任何域名提供你的API服务而不需要担心跨域资源共享问题(CORS)。
3.JWT优缺点
主要应用场景
身份认证
在这种场景下,一旦用户完成了登陆,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录(SSO)中比较广泛的使用了该技术。
信息交换
在通信的双方之间使用JWT对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的。
要保证私钥的安全性,来保障签名的安全。
有人提到jwt暴露签名算法(alg),而且会有None(无签名),所以建议隐去alg。一般的协议制定都会考虑扩展性和普适性。但是我们在应用中可以采用我们默认的算法,而不是根据alg去处理。
不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。
4.解决方案
二、
参考文献:
https://segmentfault.com/a/1190000005047525?utm_source=tuicool&utm_medium=referral
https://www.jianshu.com/p/fcc1a6482143?from=timeline