注解与AOP实战:实现权限控制
在现代Java开发中,注解(Annotation)和面向切面编程(AOP)是两种强大的技术,它们能够帮助我们实现代码的解耦,提高代码的可读性和可维护性。本文将通过一个实际的例子,展示如何结合自定义注解和AOP来实现权限控制。
1. 什么是注解?
注解是Java提供的一种元数据机制,能够为代码添加额外的信息。注解本身不会影响代码的执行,但可以通过反射等机制在运行时读取这些信息,进而实现特定的功能。
1.1 自定义注解
以下是一个自定义注解 @AuthCheck
的示例,用于标记需要权限控制的方法:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) // 注解只能用于方法
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时保留
public @interface AuthCheck {
String mustRole() default ""; // 必须具有的角色
}
在这个注解中,mustRole
属性用于指定调用该方法所需的角色。
2. 什么是AOP?
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,它允许我们将横切关注点(如日志记录、权限控制、事务管理等)从业务逻辑中分离出来。通过AOP,我们可以在不修改原有代码的情况下,动态地为方法添加额外的功能。
2.1 AOP的核心概念
- 切面(Aspect):包含横切关注点的模块。
- 连接点(Join Point):程序执行的某个点,例如方法调用。
- 通知(Advice):在连接点上执行的动作,如前置通知、后置通知等。
- 切入点(Pointcut):定义在哪些连接点上应用通知。
3. 实战:使用注解和AOP实现权限控制
3.1 定义权限控制注解
我们已经在前面定义了 @AuthCheck
注解,用于标记需要权限控制的方法。
3.2 实现权限拦截器
以下是一个基于Spring AOP的权限拦截器 AuthInterceptor
,它会拦截带有 @AuthCheck
注解的方法,并根据注解中的角色要求进行权限校验:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@Aspect // 标记为切面
@Component // 注册为Spring Bean
public class AuthInterceptor {
@Resource
private UserApplicationService userApplicationService;
/**
* 拦截带有 @AuthCheck 注解的方法
*
* @param joinPoint 切入点
* @param authCheck 权限校验注解
* @return 执行结果
* @throws Throwable 异常
*/
@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 = userApplicationService.getLoginUser(request);
UserRoleEnum mustRoleEnum = UserRoleEnum.getEnumByValue(mustRole);
// 如果不需要权限,直接放行
if (mustRoleEnum == null) {
return joinPoint.proceed();
}
// 检查用户角色
UserRoleEnum userRoleEnum = UserRoleEnum.getEnumByValue(loginUser.getUserRole());
if (userRoleEnum == null) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR); // 用户角色为空,抛出无权限异常
}
// 如果要求管理员权限,但用户不是管理员,抛出无权限异常
if (UserRoleEnum.ADMIN.equals(mustRoleEnum) && !UserRoleEnum.ADMIN.equals(userRoleEnum)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
// 通过权限校验,放行
return joinPoint.proceed();
}
}
3.3 在业务方法中使用注解
以下是一个使用 @AuthCheck
注解的业务方法示例,只有管理员角色才能调用该方法:
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@PostMapping("/delete")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> deleteUser(@RequestBody DeleteRequest deleteRequest) {
if (deleteRequest == null || deleteRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR); // 参数校验
}
boolean b = userApplicationService.deleteUser(deleteRequest); // 删除用户
return ResultUtils.success(b); // 返回结果
}
}