自己实现注解式权限校验(SpringBoot)
权限校验是很多情况都会用到的,结合Java注解和拦截器,直接在Controller层的方法上添加一个注解,可以
无侵入式
的进行权限校验。
一.Java注解
1.RequestMapping
我们打开一个最常用的Spring注解
可以看到,RequestMapping
注解上,还有几个注解,分别代表
①Target
:注解目标(如:可以在方法、类、参数中使用)
②Retention
:是注解的注解,称为元注解。 分为3类 :
- RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
- RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
- RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
③Documented
:表明这个注释是由 javadoc记录的。
④Mapping
:Spring的注解,不做过多说明
二.自定义一个判断权限的注解
1.Permission.java
在了解了注解的组成后,我们可以尝试着,自己编写一个注解。用来判断权限。
注意:此注解使用了Spring的AliasFor
注解,改注解成对出现,表示双方互为别名属性。(既设置了name
,则value
同样有该值),使用AliasFor
注解后,必须用Spring的AnnotationUtils
工具获取注解才可以达到上述效果。
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
/**
* @author litong
* @date 2019/11/29 16:18
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface Permission {
/**
* 权限
*/
@AliasFor("value")
PermissionEnum[] name() default {};
/**
* 权限
*/
@AliasFor("name")
PermissionEnum[] value() default {};
}
2.PermissionEnum.java
权限的枚举,使用枚举可以更好的说明参数,易读性高,且可以减少报错几率。
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author litong
* @date 2019/11/29 16:20
*/
@Getter
@AllArgsConstructor
public enum PermissionEnum {
/**
* 用户管理权限
*/
USER(1, "用户管理权限"),
/**
* 教师管理权限
*/
TEACHER(2, "教师管理权限"),
/**
* 无需校验,
*/
NO(-99999, "无需权限"),
;
/**
* 权限编码
*/
private Integer code;
/**
* 权限名称
*/
private String msg;
}
三.自定义拦截器
注:此拦截器,一般还会有未登录拦截,Token解析,用户注入、权限校验等功能,现只展示其中之一,权限校验。
1.AuthenticationInterceptor.java
/**
* @Author: litong
* @Date: 2019-09-20 11:50
* @Description: 拦截器
*/
@Slf4j
public class AuthenticationInterceptor implements HandlerInterceptor {
/**
* 在业务处理器处理请求之前被调用
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 如果不是映射到方法直接通过
if (!(handler instanceof HandlerMethod)) {
return true;
}
// 获取方法中的注解
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
// 省略判断是否需要登录的方法.....
// 省略Token解析的方法.....
// 此处根据自己的系统架构,通过Token或Cookie等获取用户信息。
UserInfo userInfo = userService.getUserByToken(token);
// 获取类注解
Permission permissionClass = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Permission.class);
// 获取方法注解
Permission permissionMethod = AnnotationUtils.findAnnotation(method,Permission.class);
// 判断是否需要权限校验
if (permissionClass == null && permissionMethod == null) {
// 不需要校验权限,直接放行
return true;
}
// 获取该方法注解,优先级:方法注解>类注解
PermissionEnum[] permissionEnums;
if (permissionClass != null && permissionMethod == null) {
// 类注解不为空,方法注解为空,使用类注解
permissionEnums = permissionClass.name();
} else if (permissionClass == null) {
// 类注解为空,使用方法注解
permissionEnums = permissionMethod.name();
} else {
// 都不为空,使用方法注解
permissionEnums = permissionMethod.name();
}
// 校验该用户是否有改权限
// 校验方法可自行实现,拿到permissionEnums中的参数进行比较
if(userService.checkPermissionForUser(userInfo,permissionEnums)){
// 拥有权限
return true;
} else {
// 抛出自定义异常,可在全局异常捕获后自行处理。
throw new AuthTokenException(CheckConstants.PERMISSION_ERROR);
}
}
/**
* 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
*
* @param httpServletRequest
* @param httpServletResponse
* @param o
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
/**
* 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
*
* @param httpServletRequest
* @param httpServletResponse
* @param o
* @param e
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
四.使用
1.类(该类中,所有方法校验权限)
/**
* @author litong
* @date 2019/9/20 13:26
*/
@RestController
@RequestMapping("/auth")
@AllArgsConstructor
@Slf4j
@Permission({PermissionEnum.USER})
public class AuthController {
}
2.方法(此方法校验权限,优先级大于类上的注解)
@PostMapping("/get")
@Permission({PermissionEnum.USER})
public R get() {
}