简介
在 Win10-安装-Redis 和 微服务-SpringBoot-集成-Redis 分别介绍了如何安装和使用 Redis,今天继续结合 Redis,聊聊 token 授权登录的事情。
今天聊的主角是 JWT,聊完 JWT 之后再结合实例实现用户 token 登录。
JWT 介绍
JWT,JSON Web Token 的缩写,基于 RFC 7519 标准。
下面内容来自 jwd.io,如下:
1 | JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties. |
JWT 定义了一种紧凑的、自包含的方式,用于作为 JSON 对象在各方之间安全地传输信息。该信息可以被验证和信任(因为它是数字签名的)。
JWT 可应用于但不仅限于下面的几种场景:
1、跨域认证
JWT 是一种比较流行的跨域认证解决方案,JWT 的诞生并不是解决 CSRF 跨域攻击,而是解决跨域认证的难题。
A 网站和 B 网站是同一家公司的关联服务,现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,这应该如何实现呢?客户端保存 Token,每次请求都发回给服务器即可。
2、授权(Authorization)
用户一旦登录成功后,后续用户的每个请求都将包含 JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的 JWT 的一个特性,因为它的开销很小,并且可以轻松地跨域使用。授权,是使用 JWT 的最常见的场景之一。
3、信息交换(Information Exchange)
对于安全的在各方之间传输信息而言,JWT 是一种很好的方式。JWT 可以被签名,例如,用公钥/私钥对,可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,还可以验证内容没有被篡改。
可以参考阮一峰老师的 JSON Web Token 入门教程,更多详细的介绍可以参考 jwd.io 的相关资料。
使用 JWT
Spring Boot 集成 jjwt
本文以集成 https://github.com/jwtk/jjwt 为例。如果你有兴趣也可以试着去使用 https://github.com/auth0/java-jwt,它是 JWT 的另一个 Java 实现。
截止到该文发布,在 maven repository 仓库中 jjwt 最新版本是 0.9.1
1 | <dependency> |
修改了哪些文件
本次涉及修改和新增的文件如下:
- 【修改】
MSUserSigninService.java
:登录服务的接口; - 【修改】
MSUserSigninServiceImpl.java
:登录服务的接口实现; - 【修改】
MSSigninController.java
:登录的Controller; - 【新增】
MSAuthTokenUtil.java
:token工具类; - 【新增】
MSAuthConfigurer.java
:token配置管理; - 【新增】
MSAuthInterceptor.java
:自定义拦截器;
具体的实现步骤为:
- 写 token 工具类,实现 token 的生成,校验等工作即
MSAuthTokenUtil.java
; - 写自定义拦截器,即
MSAuthInterceptor.java
,该类实现了HandlerInterceptor
接口;- 拦截客户端相关的 API 请求,对相关的接口进行token的校验;
- 有了统一的拦截器不需要在每个 Controller 或者对应的 Service 中去做 token 的判断;
- 写自定义拦截器的配置管理类即
MSAuthConfigurer.java
,该类实现了WebMvcConfigurer
接口; - 增加 token 登录的 API,并实现 Redis 缓存 token 的逻辑;
实例演练
用户登录完成后,根据 userID 生成 token,将 token 保存到 Redis 中按照 userID 为 key 来进行存储的。
MSAuthInterceptor.java
是自定义的拦截器,在该拦截器中获取请求的 token 并进行相关的校验。核心代码如下:
1 |
|
拦截器的配置在 MSAuthConfigurer.java
中进行管理,关键代码如下:
1 |
|
接下来重点说一下 MSAuthTokenUtil.java
里面如何生成 token 的,MSAuthTokenUtil.java
主要是完成生成、检验、刷新 token 等工作。
1 | public static String generateToken(String userID) { |
根据用户ID 生成 token,其中 claim(CLAIMS_USERID, userID)
是用于自定义字段的,便于解析 token 时获取相关的信息。
当我们调用用户名+密码登录的时候,会生成对应的 token,然后将该 token 保存到 Redis 中。下次调用 token 登录的接口时,会从 Redis 中取出对应的 token 信息进行校对,校对通过就返回成功,否则返回失败无法登录。
在 MSSigninController.java
分别实现了获取 token、刷新 token,token 登录三个接口,如下:
1 |
|
为了方便使用了 GET 方式进行网络请求。后续可以改为 POST 请求。
登录逻辑都在 MSUserSigninServiceImpl.java
中,大家可以自行去看源码,这里不再赘述。
API 调用效果
启动 MySQL,启动 Redis,再启动项目即可。
用户登录成功后,调用 /get/token
API,如下:
调用 /token
进行登录的 API,如下:
调用 refresh/token
API 如下:
待办事项
- token 配置信息放置到配置文件中;2021.01.17 Done 微服务-Token-相关的重构;
- Redis 中设置 token 的过期时间;
- 调用刷新 token 的 API 后更新 Redis 中 token 的有效时间;
- 刷新 token、使用 token 登录的 API 修改为 POST 方式;
- Token 的加密,减少 Token 登录的数据库查询次数;
只有弱者才去争取公平,这句话虽然残忍但很现实~