目录
一、核心思想
在现代软件开发中,权限认证是保障系统安全与数据隐私的关键环节。使用注解和 AOP(面向切面编程)实现权限认证,背后蕴含着高效、解耦且可复用的设计理念。
其核心在于将权限验证逻辑从业务方法主体中抽离出来,通过自定义注解作为 “标记”,精准地标注在那些需要进行权限管控的类或方法上。这些注解就像是一个个小巧的 “信号灯”,直观地告知系统此处存在权限要求。而 AOP 则扮演着 “交通警察” 的角色,基于切面机制,在系统运行期间 “拦截” 到带有特定注解标识的方法执行点。一旦拦截成功,切面类便会利用反射等技术获取注解中的权限信息,并结合当前用户的权限数据(往往通过如会话、数据库查询等方式取得)展开细致入微的比对校验。若用户具备相应权限,业务流程顺畅无阻,方法正常执行;反之,当权限缺失,立刻阻断执行并抛出异常或返回特定错误提示,以此确保系统操作严格遵循权限规则,避免非法访问,维护系统的 “秩序井然”。
二、目录
- 基础概念剖析
- 注解(Annotation)简介与应用场景
- AOP 原理与常见实现框架(如 AspectJ)概览
- 权限认证基础模型与关键要素
- 技术选型与环境搭建
- 基于 Spring Boot 的项目搭建(引入必要依赖)
- 配置 AOP 相关组件与注解处理器
- 自定义权限注解设计
- 定义注解结构(元注解配置、属性设定)
- 注解在业务代码中的应用实例
- AOP 切面实现权限验证逻辑
- 切面类搭建(标注
@Aspect
等关键注解) - 环绕通知(
Around
)拦截方法并校验权限流程 - 异常处理与错误反馈机制
- 切面类搭建(标注
- 用户权限数据获取与管理
- 数据库设计思路(存储用户、角色、权限关系)
- 从数据源(数据库、缓存等)获取用户权限集合
- 测试与验证
- 编写单元测试用例验证权限认证机制有效性
- 集成测试确保在系统整体流程中权限管控无误
- 优化与拓展
- 性能优化考量(缓存权限数据、优化切面逻辑)
- 拓展权限模型适应复杂业务场景(多级权限、动态权限)
三、代码示例
(一)项目搭建与依赖引入(基于 Spring Boot)
创建一个 Spring Boot 项目,在pom.xml
文件中引入以下核心依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
这里spring-boot-starter-web
用于构建 Web 应用,spring-boot-starter-aop
提供 AOP 功能支持,lombok
简化代码编写(例如自动生成日志对象等)。
(二)自定义权限注解
package com.example.auth.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface HasPermission {
String value();
}
这个注解通过@Target
指定能作用于类和方法,@Retention
设定运行时保留,内部value
属性用来承载具体权限标识字符串,比如"user:edit"
代表用户编辑权限。
(三)AOP 切面类实现
package com.example.auth.aspect;
import java.lang.reflect.Method;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.example.auth.annotation.HasPermission;
import com.example.common.constant.Constants;
import com.example.common.exception.ForbiddenException;
import com.example.common.utils.ServletUtils;
import com.example.system.feign.RemotePermissionService;
import lombok.extern.slf4j.Slf4j;
@Aspect
@Component
@Slf4j
public class PermissionAspect {
@Autowired
private RemotePermissionService permissionClient;
@Around("@annotation(com.example.auth.annotation.HasPermission)")
public Object around(ProceedingJoinPoint point) throws Throwable {
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
HasPermission annotation = method.getAnnotation(HasPermission.class);
if (annotation == null) {
return point.proceed();
}
String requiredPermission = new StringBuilder(annotation.value()).toString();
if (hasPermission(requiredPermission)) {
return point.proceed();
} else {
throw new ForbiddenException();
}
}
private boolean hasPermission(String requiredPermission) {
HttpServletRequest request = ServletUtils.getRequest();
String tmpUserKey = request.getHeader(Constants.CURRENT_USER_ID);
if (Optional.ofNullable(tmpUserKey).isPresent()) {
Long userId = Long.valueOf(tmpUserKey);
log.debug("userid:{}", userId);
if (userId == 1L) {
return true;
}
return permissionClient.getPermissionsByUserId(userId).stream().anyMatch(requiredPermission::equals);
}
return false;
}
}
此切面类标注@Aspect
表明其切面身份,@Component
纳入 Spring 管理。around
方法作为环绕通知,拦截带@HasPermission
注解的方法,先提取注解权限标识,再通过hasPermission
方法校验当前用户(从请求头获取用户 ID,借助RemotePermissionService
远程查询权限集)是否具备对应权限,有权限则放行方法执行,否则抛异常阻断。
(四)远程权限服务接口(Feign 示例)
package com.example.system.feign;
import java.util.Set;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import com.example.common.constant.ServiceNameConstants;
import com.example.system.feign.factory.RemotePermissionFallbackFactory;
@FeignClient(name = ServiceNameConstants.PERMISSION_SERVICE, fallbackFactory = RemotePermissionFallbackFactory.class)
public interface RemotePermissionService {
@GetMapping("permission/perms/{userId}")
public Set<String> getPermissionsByUserId(@PathVariable("userId") Long userId);
}
定义 Feign 客户端接口,用于与名为ServiceNameConstants.PERMISSION_SERVICE
的远程服务交互,按用户 ID 获取权限集,搭配回退工厂实现容错。
(五)在业务方法中应用注解
package com.example.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.auth.annotation.HasPermission;
@RestController
public class UserController {
@HasPermission("user:list")
@GetMapping("/users")
public String listUsers() {
return "用户列表信息";
}
@HasPermission("user:edit")
@GetMapping("/user/edit")
public String editUser() {
return "用户编辑操作执行成功";
}
}
在UserController
的listUsers
和editUser
方法上分别标注@HasPermission
注解,赋予不同权限要求,当请求到达对应方法,便触发上述 AOP 权限校验流程。
上述代码完整展示了从基础环境搭建,经自定义注解、切面逻辑编写,到业务应用的整套利用注解和 AOP 实现权限认证的方案,在实际场景中可按需调整优化,保障系统权限管控严密高效。
标签:org,AOP,example,import,注解,权限,annotation From: https://blog.csdn.net/m0_57836225/article/details/144177293