权限校验的方式有很多,比如在方法内部校验,比如使用拦截器等等,这次尝试使用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