MethodExpressionAuthorizationManager 是 Spring Security 6.1 中用于基于表达式的方法授权管理器,它允许你使用 SpEL(Spring Expression Language)表达式定义复杂的访问控制规则。通过该管理器,你可以在方法调用前或调用后进行动态权限验证,支持访问用户信息、方法参数、返回值等上下文数据。
表达式支持,使用 SpEL 表达式定义授权规则,例如:
hasRole('admin'):验证用户角色
hasPermission(#user, 'delete'):基于对象实例的权限验证
@securityService.checkAccess(#user):调用自定义服务进行验证
注解驱动,支持 Spring Security 的注解:
@PreAuthorize:方法调用前验证
@PostAuthorize:方法调用后验证
@PreFilter:过滤方法参数
@PostFilter:过滤方法返回值
上下文访问,在表达式中访问:
authentication:当前用户认证信息
principal:用户主体对象
方法参数(通过名称或索引)
方法返回值(仅 @PostAuthorize)
下面是 Spring Security 中 SpEL 的核心表达式,如下表:
表达式 | 描述 |
---|---|
hasRole('ROLE') | 验证用户是否拥有指定角色(自动添加 ROLE_ 前缀) |
hasAuthority('AUTH') | 验证用户是否拥有指定权限(不添加前缀) |
principal | 访问当前用户主体对象 |
authentication | 访问完整的 Authentication 对象 |
permitAll | 允许所有访问 |
denyAll | 拒绝所有访问 |
isAnonymous() | 验证用户是否为匿名用户 |
isAuthenticated() | 验证用户是否已认证 |
isFullyAuthenticated() | 验证用户是否已完全认证(非记住我登录) |
hasPermission(target, permission) | 使用 PermissionEvaluator 验证对象权限 |
下面是 MethodExpressionAuthorizationManager 类的源码:
package org.springframework.security.authorization.method; import java.util.function.Supplier; import org.aopalliance.intercept.MethodInvocation; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.security.access.expression.ExpressionUtils; import org.springframework.security.access.expression.SecurityExpressionHandler; import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.authorization.ExpressionAuthorizationDecision; import org.springframework.security.core.Authentication; import org.springframework.util.Assert; /** * 基于表达式的方法授权管理器 * 用于通过评估 SpEL 表达式来决定是否允许对方法的访问 * 支持在方法调用前进行权限检查 */ public final class MethodExpressionAuthorizationManager implements AuthorizationManager<MethodInvocation> { // 安全表达式处理器,用于解析和计算安全表达式 private SecurityExpressionHandler<MethodInvocation> expressionHandler = new DefaultMethodSecurityExpressionHandler(); // 待评估的 SpEL 表达式 private Expression expression; /** * 创建一个方法表达式授权管理器实例 * @param expressionString 要解析的原始表达式字符串,例如 "hasRole('ADMIN')" */ public MethodExpressionAuthorizationManager(String expressionString) { Assert.hasText(expressionString, "expressionString cannot be empty"); // 使用表达式处理器解析表达式字符串 this.expression = this.expressionHandler.getExpressionParser().parseExpression(expressionString); } /** * 设置用于解析和计算表达式的安全表达式处理器 * 默认使用 DefaultMethodSecurityExpressionHandler * @param expressionHandler 要使用的表达式处理器 */ public void setExpressionHandler(SecurityExpressionHandler<MethodInvocation> expressionHandler) { Assert.notNull(expressionHandler, "expressionHandler cannot be null"); this.expressionHandler = expressionHandler; // 使用新的表达式处理器重新解析表达式 this.expression = expressionHandler.getExpressionParser() .parseExpression(this.expression.getExpressionString()); } /** * 通过评估表达式来决定是否允许访问 * @param authentication 当前用户的认证信息提供者 * @param context 方法调用上下文,包含方法信息和参数 * @return 基于表达式评估结果的授权决策 */ @Override public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation context) { // 创建表达式评估上下文,将认证信息和方法调用上下文注入 EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, context); // 评估表达式并获取布尔结果 boolean granted = ExpressionUtils.evaluateAsBoolean(this.expression, ctx); // 创建包含表达式和评估结果的授权决策 return new ExpressionAuthorizationDecision(granted, this.expression); } @Override public String toString() { return "WebExpressionAuthorizationManager[expression='" + this.expression + "']"; } }
该注解方法调用前验证,如下:
// 仅 normal 角色可调用,且用户 ID 必须与当前登录用户 ID 相同或为 lisi @PreAuthorize("hasRole('admin') || #username == authentication.principal.username") @GetMapping("/hello2") public String hello2(@RequestParam("username") String username) { System.out.println("hello2() username=" + username); return "Hello, Spring Security!"; }
运行效果如下图:
该注解在方法调用后验证,如下:
// 返回后验证:仅用户所有者或 ADMIN 可查看敏感内容 @PostAuthorize("returnObject.username == authentication.principal.username || hasRole('admin')") @GetMapping("/postAuthorize") public UserModel postAuthorize() { System.out.println("postAuthorize()"); UserModel userModel = new UserModel(); userModel.setId(2); userModel.setUsername("lisi"); return userModel; }
上述代码运行效果如下图:
该注解用来参数值过滤,如下:
// 过滤输入参数:仅允许用户修改自己的消息 @PreFilter("filterObject.username == authentication.principal.username") @PostMapping("/updateUsers") public String updateMessages(@RequestBody List<UserModel> users) { return JSONObject.toJSONString(users, JSONWriter.Feature.PrettyFormat); }
对应的 JS 代码如下:
function updateUsers() { $.ajax({ url: '/updateUsers', type: 'POST', contentType: 'application/json', data: JSON.stringify([ { "id": 1, "username": "zhangsan", "authorities":["menu:user:add", "menu:user:delete"] }, { "id": 2, "username": "lisi", "authorities":["menu:user:add", "menu:user:delete"] } ]), success: function(response) { console.log('请求成功:', response); }, error: function(xhr, status, error) { console.error('请求失败:', error); } }); }
运行上述示例,输出如下图:
该注解用来返回值过滤,如下:
// 过滤返回值:仅返回当前用户可见的消息 @PostFilter("filterObject.username == authentication.principal.username") @GetMapping("/getUsers") public List<UserModel> getMessages() { return new ArrayList<>(){{ UserModel userModel1 = new UserModel(); userModel1.setId(1); userModel1.setUsername("zhangsan"); userModel1.setAuthorities(Arrays.asList("menu:user:add", "menu:user:delete")); add(userModel1); UserModel userModel2 = new UserModel(); userModel2.setId(2); userModel2.setUsername("lisi"); userModel2.setAuthorities(Arrays.asList("menu:user:add", "menu:user:delete")); add(userModel2); }}; }
运行上述代码,效果如下图:
UserModel 类到定义如下:
package com.hxstrive.spring_security.model; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.List; import java.util.Set; /** * 用户实体 * @author hxstrive.com */ public class UserModel implements UserDetails { // 用户ID private int id; // 用户名 private String username; // 密码 private String password; // 权限列表 private Collection<? extends GrantedAuthority> authorities; // 主要是这个 @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } public void setAuthorities(List<String> authorities) { this.authorities = AuthorityUtils.createAuthorityList(authorities); } //... }
注意,MethodExpressionAuthorizationManager 提供了强大的基于表达式的方法级授权能力,通过 SpEL 表达式可以实现复杂的动态访问控制。它是 Spring Security 中最灵活的授权方式,适合需要细粒度权限控制的企业级应用。