关键字:AOP、JWT、自定义注解、责任链模式
一、Spring Security
Spring Security 想必大家并不陌生,是 Spring 家族里的一个安全框架,特别完善,但学习成本比较大,不少开发者都觉得,这个框架“很重”
他的认证授权的过程时使用的 api 和组件比较复杂,但这不是主要的顾虑
并且,他是基于过滤器实现的,其过程抛出的异常并不会被全局异常处理器拦截,通过 SpringSecurity 认证授权的请求,才算进入我们的 Spring 环境下
但不得不说他确实有优点
- 对请求的一个 AOP 的统一处理(但是好像没有专门的后置处理器)
- 可以通过添加过滤器的方式增加认证过程,也对某些路由可添加授权过程
- 框架的一些注解,例如 @PreAuthorize,可以很方便的进行接口权限的设置
虽然它封装了很多现成的东西,但其实一些自己去写也没什么大不了的,还可以按照自己的想法去定制
我们可以学习它的优点去优化一下我们原本的认证授权架构
二、AOP + JWT
通过全局拦截器,拦截所有的请求,请求携带访问令牌 JWT,这里就不必多说了
三、自定义注解
自定义一个可以 Retention 为 Runtime,Target 为 TYPE 或 METHOD 的注解:
- 允许访问的角色
- 是否需要认证
- 是否需要授权
标注在 Controller 类才有用,若加在类上,则作用于所有方法,若标注在方法上,则以方法上的为准
意为此接口的拦截参数
java">/**
* Created With Intellij IDEA
* User: 马拉圈
* Date: 2024-08-08
* Time: 12:50
* <br />
* permit: 用户必须是列表中的角色才能访问 <br />
* authenticate: 用户需不需要通过认证 <br />
* authorize: 用户需不需要通过授权 <br />
* 注意: <br />
* permit 若为 {},则代表谁都不能访问,除非 authorize 为 false <br />
* 1. 不需要认证、不需要授权 ✅ <br />
* 2. 需要认证、需要授权 ✅ <br />
* 3. 需要认证、不需要授权 ✅ <br />
* 4. 不需要认证、需要授权 ❌(必然失败) <br />
*/
@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Intercept {
UserType[] permit() default {UserType.NORMAL_USER, UserType.MANAGER};
boolean authenticate() default true; // 默认需要认证
boolean authorize() default true; // 默认需要授权
}
java">@Slf4j
public class InterceptHelper {
public static Intercept getIntercept(Class<?> clazz) {
// 类上的 Intercept 为初步结果
return clazz.isAnnotationPresent(Intercept.class) ? clazz.getAnnotation(Intercept.class) : null;
}
public static Intercept getIntercept(Method targetMethod) {
// 获取目标方法所在的类
Class<?> declaringClass = targetMethod.getDeclaringClass();
// 类上的 Intercept 为初步结果
Intercept intercept = getIntercept(declaringClass);
// 方法上的 Intercept 为最终结果
return targetMethod.isAnnotationPresent(Intercept.class) ? targetMethod.getAnnotation(Intercept.class) : intercept;
}
}
四、责任链模式
责任链:
- 抽象处理者
- 具体处理者
- 客户端(我习惯是责任链的链头)
设计出以下架构:
- 前置初始化链
- 初始化日志变量、设置请求 id、请求 uri、请求方法,便于全链路追踪
- 根据配置文件对特定接口设置的拦截参数,判断当前请求的接口是否被设置了特殊的拦截参数
- 请求的接口如果存在目标方法,则判断其方法上或者类上是否存在 @Intercept 注解,若存在则初始化拦截参数
若不存在拦截参数,则代表该资源没有被开发者专门进行拦截的管理,直接返回错误:api 访问失败
- 前置认证链
- 是否忽略,若 authenticate 参数为 false,则直接忽略
- 若认证未成功且未获取 jwt,则尝试从 header 里获取 jwt
- 若认证未成功且未获取 jwt,则此时从 queryString 里获取 jwt
- 若认证未成功,则解析 jwt,若成功解析出用户信息,则存储到本地线程变量里(若令牌即将过期,则返回一个新的 jwt)
若未认证成功,则返回错误:认证失败
- 前置授权链
- 是否武略
- 是否强制拦截
- 若未授权成功,则获取当前用户的角色,判断是否存在于拦截参数的允许访问的角色列表,若存在则授权成功
若未授权成功,则返回错误:授权失败
- 后置链
- 清除日志变量、本地线程变量
在我们的架构中而一个责任链节点应该包含:
- 拦截的路由
- 执行的前置条件
- 执行的代码
具体代码参考:OKR-System4.0/src/main/java/cn/bitterfree/api/interceptor at main · CarefreeState/OKR-System4.0