权限校验的方式有很多,比如在方法内部校验,比如使用拦截器等等,这次尝试使用AOP+注解完成了权限校验。
假设我们需要判断三种权限:未登录、已登录的普通用户、管理员。
首先开发一个注解:
/**
* 权限校验
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthCheck {
String mustRole() default "";
}
可以看到,这个注解是用在方法上的,在编译时被保留在class文件中,在运行时可以通过反射访问。
然后定义一个枚举类:
public enum UserRoleEnum {
USER("用户", "user"),
ADMIN("管理员", "admin"),
BAN("被封号", "ban");
private final String text;
private final String value;
//此处省略......
}
接下来就是AOP了:
@Aspect
@Component
public class AuthInterceptor {
@Resource
private UserService userService;
@Around("@annotation(authCheck)")
public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable {
String mustRole = authCheck.mustRole();
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
// 当前登录用户
User loginUser = userService.getLoginUser(request);
// 如果需要登录但用户未登录
if (StringUtils.isNotBlank(mustRole) && loginUser == null) {
throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
}
// 必须有该权限才通过
if (StringUtils.isNotBlank(mustRole)) {
UserRoleEnum mustUserRoleEnum = UserRoleEnum.getEnumByValue(mustRole);
if (mustUserRoleEnum == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "权限不合法");
}
String userRole = loginUser.getUserRole();
// 如果用户被封号,直接拒绝
if (UserRoleEnum.BAN.getValue().equals(userRole)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "用户已被封禁");
}
// 如果需要管理员权限但不是管理员
if (UserRoleEnum.ADMIN.equals(mustUserRoleEnum)
&& !UserRoleEnum.ADMIN.getValue().equals(userRole)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "需要管理员权限");
}
// 可以添加其他角色的判断...
}
// 通过权限校验,放行
return joinPoint.proceed();
}
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
//这两句其实使用了ThreadLocal技术,可以在任何地方(不仅仅是在控制器中)获取当前请求的信息。
接下来调用server中的方法查询出该用户,最后就可以进行权限判断了,如果被封号,直接报无权限,然后再对是否需要管理员权限进行校验。
使用方式:
@AuthCheck(mustRole ="admin")
public void deleteById(){
}
这样就优雅的完成了权限校验。
进阶与优化
思考了后,觉得可以向支持多角色方向改进,下面是实现思路:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthCheck {
String[] hasAnyRole() default {}; // 具有任意一个角色即可
String[] hasAllRoles() default {}; // 必须同时具有所有角色
boolean allowAnonymous() default false; // 是否允许匿名访问
}
还有一个点是可以考虑使用框架,例如Spring Security。