基于 AOP 和 JWT 实现的 Token 身份认证组件
- 原理
- 服务端使用方式
- 使用自定义的缓存
- 客户端使用
- 测试
- 源码实现
原理
基于 AOP 面向切面编程,在执行前后插入身份认证的逻辑。
原理细节:
-
登录过程:这个过程比较简单,将用户 id、用户名、过期时间等属性结合 jwt 工具生成 token,并将用户的信息存入到缓存中,以供后期使用。
-
验证过程:前端通过 Header 头信息的 Authorization 属性得到 Token,先进行 token 验证,再结合缓存验证,验证成功的话,将用户 id 和用户名等信息存入
ThreadLocal
中,这样在执行切面逻辑的时候。就可以从ThreadLocal
中获取数据了,如UserManager.getUserId()
;执行完成后需要清除 ThreadLocal 中的数据;代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class ValidateLoginAspect { @Around("pointCutMethod()") public Object preHandle(ProceedingJoinPoint pjp) { // ...... UserContextHolder.getInstance().setContext(userMap); final Object proceed; try { proceed = pjp.proceed(); } finally { UserContextHolder.getInstance().clear(); } return proceed; } }
- 认证接口的范围:给
BaseTokenController
这个基类添加@ValidateLogin
,
可以实现一个效果,只要自己的Controller
继承了BaseTokenController
,那么就不用再声明@ValidateLogin
注解,自定义Controller
中的 mapping 都需要身份认证。
(PS:这样就免去了繁琐配置:如在拦截器中通过通配符的方式配置哪些接口需要拦截,哪些接口需要放行)
服务端使用方式
添加依赖
1
2
3
4
5
6
7<dependency> <groupId>com.lyloou</groupId> <artifactId>component-security-loginvalidator-starter</artifactId> <version>${lyloou.component.version}</version> </dependency>
- 继承
BaseTokenController
类。 因为这个类被@ValidateLogin
标记,所以其下的所有子类都需要身份认证(具体实现细节,查看ValidateLoginAspect
)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14@RestController public class UserController extends BaseTokenController { // 从父类继承了ValidateLogin,需要身份验证 @GetMapping("/ping") public String ping() { final Integer userId = currentUserId(); System.out.println(userId); return "pong"; } }
- 如果继承了
BaseTokenController
类,又希望其中的某个方法不要被拦截,可以在方法上标记@IgnoreValidateLogin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22@RestController public class UserController extends BaseTokenController { @Autowired TokenService tokenService; // 手动忽略身份验证 @IgnoreValidateLogin @GetMapping("/login") public String login(String userId, String username) { Map<String, String> map = new HashMap<>(); map.put("userId", userId); map.put("userName", username); map.put("userAvatar", "http://cdn.lyloou.com/a.jpg"); final String token = tokenService.createToken(userId, username, JSONUtil.toJsonStr(map)); return token; } }
- 如果没有继承
BaseTokenController
,又希望在其中某个方法中做身份认证,获取用户 id,可以在方法上标记@ValidateLogin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18@RestController public class UserController { // 手动添加身份验证 @ValidateLogin @GetMapping("userinfo") public Map<String, String> userInfo() { Map<String, String> map = new HashMap<>(); map.put(UserManager.X_USER_ID, UserManager.getUserId() + ""); map.put(UserManager.X_USER_IP, UserManager.getUserIP()); map.put(UserManager.X_USER_NAME, UserManager.getUserName()); map.put(UserManager.X_USER_INFO, UserManager.getUserInfo()); return map; } }
使用自定义的缓存
默认使用了内存缓存ConcurrentHashMap
(单机版本的)
也可以自定义缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38/** * @author lilou * @since 2021/7/14 */ @Service public class RedisCodeCache implements DataCache { @Autowired private RedisService redisService; @Autowired private TokenProperties tokenProperties @Override public void set(String key, String value) { set(key, value, tokenProperties.getExpireSecond()); } @Override public void set(String key, String value, long timeout) { redisService.set(key, value, (int) timeout); } @Override public String get(String key) { return redisService.get(key); } @Override public void remove(String key) { redisService.del(key); } @Override public boolean containsKey(String key) { return redisService.exists(key); } }
客户端使用
在 Header 中配置身份认证 Token 的信息:
如:Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2Mjg4MzQ3MjQsImV4cCI6MTYyOTQzOTUyNCwieC11c2VyLW5hbWUiOiJhYmNkZSIsIngtdXNlci1pZCI6IjEifQ.x0nIhSUPfxC5FlnzJ-MmJvLnJv7w5ZvFzGlNphdSByE
测试
登录接口:
获取用户信息接口:
源码实现
component/component-security-loginvalidator-starter at master · lyloou/component
最后
以上就是要减肥小蝴蝶最近收集整理的关于基于 AOP 和 JWT 实现的 Token 身份认证组件的全部内容,更多相关基于内容请搜索靠谱客的其他文章。
发表评论 取消回复