背景
在做课程项目的时候,需要使用到权限认证,但是如果在代码中的每个Service层的业务方法中来进行权限的判断未免有点太不优雅
所以这个时候就开始想到,是否能够将鉴权放在注解中或者在拦截器中直接进行权限判断,于是开始找框架
首先找到的是Spring Security,一看使用方法感觉自己头都大了,用起来未免过于复杂
然后找到的是Sa-token,虽然这个框架功能齐全,而且使用方便,但是发现他的Token直接交由框架管理。而我已经做好了Token的生成和解析,如果要引入的话需要在框架下实现,不想做代码的改动。
而且这也只是一个单体课程作业,虽然想将一些功能进行更好的实现,并不需要框架内的绝大多数功能,所以自己编写一个简单的鉴权工具。
前景提要
因为是做一个简单的工单系统,所以并不需要多复杂的设计。
因为已经自己做了一个拦截器进行拦截Token并进行解析,并且把解析出来的 UserDTO 放入了持有类中
持有类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class UserHolder { private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();
public static void saveUser(UserDTO userDTO) { tl.set(userDTO); }
public static UserDTO getUserDTO() { return tl.get(); }
public static void removeUser() { tl.remove(); } }
|
Token生成使用jjwt,Token中含有DTO对象,在登录拦截器中解析Token后将其放入持有类中就能保证可以随时获取到DTO对象,并且非常安全。
鉴权Annotation实现
首先引入AOP的依赖
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
|
开启AOP
1 2 3 4 5 6 7 8 9 10
| @SpringBootApplication @MapperScan("work.wucheng.workordersystem.mapper") @EnableAspectJAutoProxy public class WorkOrderSystemApplication {
public static void main(String[] args) { SpringApplication.run(WorkOrderSystemApplication.class, args); }
}
|
然后创建一个Annotation
1 2 3 4 5 6 7
| @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) public @interface AuthFilter { int[] value() default USER_TYPE_USER; }
|
USER_TYPE_USER
是一个常量,因为用户类别不多,所以设计上简单,仅用0123表示,用户数量较多时可以考虑参考Linux的二进制权限表示设计
Aspect切面
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
| @Aspect @Component @Slf4j public class AuthCheckAspect {
@Around("@annotation(authFilter)") public Object before(ProceedingJoinPoint joinPoint, AuthFilter authFilter) throws Throwable { Signature signature = joinPoint.getSignature(); String className = joinPoint.getTarget().getClass().getSimpleName(); log.info("所执行的类与方法:{} | method:{}",className, signature.getName());
int[] value = authFilter.value(); Integer type = UserHolder.getUserDTO().getType();
for (int i : value) { if (type.intValue() <= i){ return joinPoint.proceed(); } } throw new RuntimeException("权限不足"); } }
|
在Method中使用即可进行鉴权
1 2 3 4 5
| @PostMapping("/add") @AuthFilter(USER_TYPE_ADMIN) public Result add(@RequestBody User user){ return userService.add(user); }
|
后
权限的设计进行了一波偷懒,并没有区分出权限和用户身份,考虑到只是单体作业,所以尽量避免过于复杂设计,但是记录保留一些比较好的通用方法的思路还是更为重要。