什么是 JWT?
JWT (JSON Web Token) :目前最流行的跨域认证解决方案,是一种基于 Token 的认证授权机制。
JWT 自身包含了身份验证所需要的所有信息,因此,我们的服务器不需要存储 Session 信息。这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力。
JWT 本质上就是一组字串(如xxxxx.yyyyy.zzzzz),通过(.)切分成三个为 Base64 编码的部分:
Header : 描述 JWT 的元数据,定义了生成签名的算法以及
Token的类型。typ(Type):令牌类型,也就是 JWT。alg(Algorithm):签名算法,比如 HS256。
Payload : 用来存放实际需要传递的数据:默认不加密,所以不建议存放私密信息(也可以服务端进行加密)
Signature(签名):服务器通过 Payload、Header 和一个密钥(Secret)使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。
你可以在 jwt.io 这个网站上对其 JWT 进行解码,解码之后得到的就是 Header、Payload、Signature 这三部分。
Header 和 Payload 都是 JSON 格式的数据,Signature 由 Payload、Header 和 Secret(密钥)通过特定的计算公式和加密算法得到。
如何基于 JWT 进行身份验证?
在基于 JWT 进行身份验证的的应用程序中,服务器通过 Payload、Header 和 Secret(密钥)创建 JWT 并将 JWT 发送给客户端。客户端接收到 JWT 之后,会将其保存在 Cookie 或者 localStorage 里面,以后客户端发出的所有请求都会携带这个令牌。
简化后的步骤如下:
- 用户登陆系统成功。服务端会返回已经签名的 Token,也就是 JWT。
- 用户以后每次向后端发请求都在 Header 中带上这个 JWT 。
- 服务端检查 JWT 并从中获取用户相关信息。
两点建议:
- 建议将 JWT 存放在 localStorage 中,放在 Cookie 中会有 CSRF 风险。
- 请求服务端并携带 JWT 的常见做法是将其放在 HTTP Header 的
Authorization字段中(Authorization: Bearer Token)。
JWT优势
- 无状态
- JWT 自身包含了身份验证所需要的所有信息,因此,我们的服务器不需要存储 Session 信息。这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力。
- 我们想要在 JWT 有效期内废弃一个 JWT 或者更改它的权限的话,并不会立即生效,当用户 Logout 的话,JWT 也还有效。
- 可以将JWT存入Redis中,避免这个问题,但是违背了无状态,但是最有效
- 或者设置有效期较短,但是体验不好
- JWT 续签问题也可以通过Redis实现(其实这些直接使用普通的Token生成配合Redis即可)
- 有效避免了 CSRF 攻击
- JWT 在前端一般选择存放在 localStorage 中,不会涉及 Cookie。因此,即使你点击了非法链接发送了请求到服务端,这个非法请求也是不会携带 JWT 的,所以这个请求将是非法的。
- 总结来说就一句话:使用 JWT 进行身份验证不需要依赖 Cookie ,因此可以避免 CSRF 攻击。
- 但是无法避免 XSS (Cross-Site Scripting,跨站脚本攻击)
- 在 Spring 项目中,我们一般是通过创建 XSS 过滤器来实现的。
- 适合移动端应用
- 移动端网络不稳定,用SessionId成本较高,且平台多,兼容性容易成为问题,
- 单点登录友好
如何防止 JWT 被篡改?
JWT 安全的核心在于签名,签名安全的核心在密钥
服务端拿到 JWT 之后,会解析出其中包含的 Header、Payload 以及 Signature 。服务端会根据 Header、Payload、密钥再次生成一个 Signature。拿新生成的 Signature 和 JWT 中的 Signature 作对比,如果一样就说明 Header 和 Payload 没有被修改。
黑客在没有密钥的情况下没办法同时篡改 Signature、Header、Payload。
所以密钥一定保管好,一定不要泄露出去。
CRSF
CSRF(Cross Site Request Forgery) 一般被翻译为 跨站请求伪造,属于网络攻击领域范围。相比于 SQL 脚本注入、XSS 等安全攻击方式,CSRF 的知名度并没有它们高。但是,它的确是我们开发系统时必须要考虑的安全隐患。就连业内技术标杆 Google 的产品 Gmail 也曾在 2007 年的时候爆出过 CSRF 漏洞,这给 Gmail 的用户造成了很大的损失。
那么究竟什么是跨站请求伪造呢? 简单来说就是用你的身份去做一些不好的事情(发送一些对你不友好的请求比如恶意转账)。
举个简单的例子:小壮登录了某网上银行,他来到了网上银行的帖子区,看到一个帖子下面有一个链接写着“科学理财,年盈利率过万”,小壮好奇的点开了这个链接,结果发现自己的账户少了 10000 元。这是这么回事呢?原来黑客在链接中藏了一个请求,这个请求直接利用小壮的身份给银行发送了一个转账请求,也就是通过你的 Cookie 向银行发出请求。
<a src="http://www.mybank.com/Transfer?bankId=11&money=10000"
>科学理财,年盈利率过万</a
>CSRF 攻击需要依赖 Cookie ,Session 认证中 Cookie 中的 SessionID 是由浏览器发送到服务端的,只要发出请求,Cookie 就会被携带。借助这个特性,即使黑客无法获取你的 SessionID,只要让你误点攻击链接,就可以达到攻击效果。
另外,并不是必须点击链接才可以达到攻击效果,很多时候,只要你打开了某个页面,CSRF 攻击就会发生。
<img src="http://www.mybank.com/Transfer?bankId=11&money=10000" />如何加强 JWT 的安全性?
- 使用安全系数高的加密算法。
- 使用成熟的开源库,没必要造轮子。
- JWT 存放在 localStorage 中而不是 Cookie 中,避免 CSRF 风险。
- 一定不要将隐私信息存放在 Payload 当中。
- 密钥一定保管好,一定不要泄露出去。JWT 安全的核心在于签名,签名安全的核心在密钥。
- Payload 要加入
exp(JWT 的过期时间),永久有效的 JWT 不合理。并且,JWT 的过期时间不易过长。 - ……

