spring security 可通过自定义注解(如@requirepermission、@requireownresource)与自定义methodsecurityexpressionhandler结合,封装语义化权限逻辑,避免spel硬编码;需继承defaultmethodsecurityexpressionhandler并注入customsecurityexpressionroot,委托permissionevaluator实现数据库级鉴权,提升可读性、复用性与安全性。

Spring Security 提供了基于方法的权限控制能力,但默认的 @PreAuthorize 等注解表达式写法耦合性强、可读性差、复用性低。通过自定义注解 + 自定义 MethodSecurityExpressionHandler,可以将鉴权逻辑封装成语义清晰、业务内聚的注解(如 @RequireOwnResource、@RequirePermission("order:delete")),同时统一处理权限判断逻辑,避免在 SpEL 表达式中硬编码或重复调用服务。
定义可复用的自定义注解
注解本身不包含逻辑,只用于标记和传递元数据。建议使用 @Target({ElementType.METHOD}) 和 @Retention(RetentionPolicy.RUNTIME),并支持属性传参:
-
@RequirePermission(value = "user:read", scope = "tenant"):携带权限码与作用域 -
@RequireOwnResource(resourceId = "id", resourceType = "order"):声明需校验“当前用户是否拥有指定资源” - 所有注解应加上
@Documented和@SupportedOptions(非必需但利于 IDE 支持)
扩展 MethodSecurityExpressionHandler 实现表达式解析
核心在于重写 createEvaluationContext() 或注册自定义 SecurityExpressionRoot 子类。更推荐方式是继承 DefaultMethodSecurityExpressionHandler,并覆盖 createEvaluationContext() 方法,在上下文中注入自定义的 ExpressionRoot:
- 自定义
CustomSecurityExpressionRoot继承SecurityExpressionRoot,添加如hasPermission(String perm)、isOwner(String resourceId, String type)等方法 - 在
createEvaluationContext()中将当前方法参数、目标对象、注解元数据等注入上下文(可通过MethodInvocation获取) - 确保 Spring 容器中注册该 handler 并启用
@EnableGlobalMethodSecurity(prePostEnabled = true)
在注解上绑定表达式逻辑(无需 SpEL 写法)
关键技巧:利用 Spring AOP 或 MethodSecurityInterceptor 的前置拦截机制,把自定义注解自动翻译为对应 SpEL 表达式。例如:
- 扫描目标方法上的
@RequirePermission,生成@PreAuthorize("hasPermission(#p0)")等等效表达式 - 更优雅的方式是实现
MethodSecurityMetadataSource,在运行时动态构建MethodSecurityConfigAttribute,交由原有授权流程处理 - 这样开发者只需写
@RequirePermission("user:update"),底层自动调用customSecurityExpressionRoot.hasPermission("user:update")
配合 PermissionEvaluator 实现数据库级权限判定
若权限判断需查库(如 RBAC 模型或 ACL),应避免在表达式根中直接写 DAO 调用。正确做法是:
- 实现
PermissionEvaluator接口,提供hasPermission(Authentication auth, Object targetDomainObject, Object permission)方法 - 在
CustomSecurityExpressionRoot中委托调用permissionEvaluator.hasPermission(...) - 确保
PermissionEvaluatorBean 被注入到DefaultMethodSecurityExpressionHandler中
这套机制让权限控制从“写表达式”变成“用注解”,既保持 Spring Security 的标准流程,又提升可维护性和领域表达力。实际落地时注意注解解析顺序、参数绑定准确性以及异常统一处理,避免因反射失败或空指针导致鉴权绕过。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











