基于自定义注解的简单方法权限管理
技术概述
通过拦截器和自定义注解实现权限管理
技术详述
通过为方法加上注解的方式决定什么什么方法是需要token才能使用的,什么方法是不需要token才能使用了,即区分了什么接口是登录后才能使用的,什么接口时不需要登录才能使用的。原理是为方法加上注解,然后在拦截器中拦截所有请求,判断请求的方法加上了什么注解,再进行后续处理。并且具有后续的拓展性,能够通过添加自定义注解的方式创建新的角色,来提供无需修改代码的权限管理。
//编写两个自定义注解 一个用于标记不需要token验证的方法 一个用于标注需要token验证的方法
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
boolean required() default true;
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
boolean required() default true;
}
添加拦截器配置
public class InterceptorConfig implements WebMvcConfigurer {
@Autowired
AuthenticationInterceptor authenticationInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor)//包含权限验证逻辑的拦截器
.addPathPatterns("/**");//拦截所有请求
}
}
只实现了prehandle方法,其他两个方法放空 有需要再实现。
流程图:
首先先检测请求是不是映射到方法
if(!(object instanceof HandlerMethod)){
return true;//不是映射到某个方法就直接通过该请求
}
然后获取方法对象并且检查注解的种类
HandlerMethod handlerMethod=(HandlerMethod)object;
Method method=handlerMethod.getMethod();
//检查是否有passtoken注释,有则跳过认证
if (method.isAnnotationPresent(PassToken.class)) {
PassToken passToken = method.getAnnotation(PassToken.class);
if (passToken.required()) {
return true;
}
}
如果是需要token的场合则需要验证token
token是否存在
if (userLoginToken.required()) {
// 检查是否有token
if (token == null) {
throw new CustomException(401,"无token,请重新登录");
}
token中包含的用户id在数据库中是否存在
// 获取 token 中的 user id
long userId=-1;
try
{
userId= TokenUtils.getIdInToken(token);
User user = userService.getUserById(userId);
}
catch (JWTDecodeException e) {
e.printStackTrace();
throw new CustomException(401,"token未通过验证,请重新登录");
}
//检查用户是否存在
User user=userService.getUserById(userId);
if (user == null) {
throw new CustomException(401,"用户不存在,请重新登录");
}
验证token正确性
//验证token正确性
try {
TokenUtils.checkToken(token,user.getUserPassword());
} catch (JWTVerificationException e) {
e.printStackTrace();
throw new CustomException(401,"toeken失效,请重新登录");
}
通过三重验证基本能确定token有效可用,就能通过该请求。
使用时仅仅需要在方法上加上注解就可以使用,实际方便。
@UserLoginToken
@PostMapping("/Project/Join/{ProjectId}")
public CommonResponse joinProject(@PathVariable long ProjectId, HttpSession httpSession)
{
projectService.joinProject(httpSession,ProjectId);
return new CommonResponse(200,"成功加入项目","");
}
问题和解决过程
1.没有把jwt的解密和加密过程打包成工具类,导致重复代码很多而且很麻烦。
解决:将jwt打包成工具类进行复用也很方便。
2.一开始将token验证设置的太过严格,甚至还设置了token过期时间,前端反馈体验不好。
解决:删除精简了部分逻辑
总结
方便了队友也方便了自己,队友在实现的时候完全不用考虑权限验证的逻辑,做到了模块的分离。
留出了拓展的空间,以应对需求的变化。