定义@Authorized注解
首先定义一个Authorized
注解,用于后面对需要鉴权的方法添加该注解,Authorzied
注解有Role
类型的roles
数组变量,表明能够调用被Authorized
注解的方法的角色有哪些,其默认值如下
1 |
|
- 元注解:注解的注解,如
@Retention
和@Target
@Retention
注解
- 生命周期:用来定义该注解在哪个级别可用,可选的有源代码中(SOURCE)、类文件中(CLASS)或者运行时(RUNTIME)
- 从
@Retention
注解源码可以看到拥有一个RetentionPolicy
类型的变量value
1 | package java.lang.annotation; |
- RetentionPolicy源码如下,可以看到它是个枚举类型,有**源代码中(SOURCE)、类文件中(CLASS)或者运行时(RUNTIME)**,对应的生命周期长度为RUNTIME>CLASS>SOURCE
- 一般如果需要在运行时去动态获取注解信息,用生命周期最长的 RUNTIME 标注
- 如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解
- 如果只是做一些检查性的操作,就用SOURCE标注,比如源码中的 @Override、@SuppressWarnings、@Native、@Generated 等
1 | package java.lang.annotation; |
@Target
- 注解目标:用来定义你的注解将应用于什么地方(例如是一个方法或者一个域)
- 从
@Target
注解源码可以看到拥有一个ElementType
类型的数组变量value
1 |
|
ElementType
源码如下,可以看到它是个枚举类型
1 | public enum ElementType { |
实现JwtConfig类
JwtCOnfig
类用来签发Jwt和解析Jwt,它的源码如下:
UserService.login
登录和数据库校验成功后调用createJwt()
,并把这个JWT返回给前端,以后前端每次请求都需要带上这个JWT- 切面获取前端发来的http请求,调用
parseJwt
解析JWT,验证是否拥有对应方法的权限
1 |
|
实现增强类
新建一个AuthAspect
切面,用于增强需要鉴权的方法,其定义如下
1 |
|
AuthAspect
类有三个注解@Aspect
:把当前类标识为一个切面供容器读取@Configuration
:表明它是一个配置类@Order(1)
:用于有多个增强类对同一个方法进行增强的时候表明其优先级,越小优先级越高
AuthAspect类除去构造方法外只有一个
authChech()
方法,它有一个@Before注解@Before
:标识一个前置增强方法,相当于BeforeAdvice的功能- 这个注解的值是检查所有
com.nju.edu.erp.web.controller
包下所有类public并且被Authorized注解所标注的方法
authCheck()
方法代码如下,主要操作包括:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void authCheck(JoinPoint joinPoint, Authorized authorized) {
try {
//1.获取当前http请求
HttpServletRequest httpServletRequest = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
//2.获得Authorized标头,没有则抛出异常
String token = Optional.ofNullable(httpServletRequest.getHeader("Authorization")).
orElseThrow(() -> new MyServiceException("A0002", "用户未获得第三方登录授权"));
//3.auth方法调用jwtConfig.parseJwt解析出UserVO信息
UserVO user = userService.auth(token);
//4.判断切面方法是否包含当前token对应的角色
if (!Arrays.stream(authorized.roles()).collect(Collectors.toList()).contains(user.getRole())) {
//5.如果不在就抛出访问未授权的异常
throw new MyServiceException("A0003", "访问未授权");
}
}catch (MyServiceException e) {
throw new MyServiceException("A0004", "认证失败");
}
}
需要鉴权的接口添加@Authorized注解
controller层对前端来的路由添加Authorized
注解进行鉴权,如:
1 |
|
添加登录方法
UserService.login
登录功能,后端从数据库根据用户名和邮箱进行验证,验证成功调用jwtConfig.createJWT
生成JWT,并把这个JWT返回给前端,以后前端每次请求都需要带上这个JWT
1 |
|
自定义注解+AOP+JWT验证流程总结
- 调用
UserService.login
登录功能,后端从数据库根据用户名和邮箱进行验证,验证成功调用jwtConfig.createJWT
生成JWT,并把这个JWT返回给前端,以后前端每次请求都需要带上这个JWT - controller层对前端来的路由添加
@Authorized
注解进行鉴权 AuthAspect
类对Authorized
注解进行增强,@Order
注解用于有多个增强类对同一个方法进行增强的时候表明其优先级,越小优先级越高- 添加
Before
方法,用于拦截controller包中所有public并且有@Authorized
注解的方法 - 拦截http请求并获得
Authorized
标头,调用jwtConfig.parseJwt
解析出UserVO
信息,判断该UserVO
的角色是否在Authorized
注解的roles集合中,如果不在就抛出访问未授权的异常
JoinPoint常用方法
- Object[] getArgs:获取目标方法的参数数组
- Signature getSignature:获取目标方法的签名
- Object getTarget:获取被织入增强处理的目标对象
- Object getThis:获取AOP框架为目标对象生成的代理对象