目录
往期精彩:
#1maven打包时指定profile过滤resources的一些总结-CSDN博客
#2Vite+Vue3+SpringMVC前后端分离 解决跨域问题和session每次请求不一致问题-CSDN博客
#3Jenkins(Windows环境)版本升级、迁移、负载均衡、双机器同步与备份-CSDN博客
#4详解@RequestParam、@RequestBody和@PathVariable-CSDN博客
#5解析filter为什么不能注入bean和解决办法以及filter、interceptor、aspect之间的执行顺序-CSDN博客
先看一下AuthorizationManager:
AuthorizationManager 取代了 AccessDecisionManager 和 AccessDecisionVoter
@FunctionalInterface
public interface AuthorizationManager<T> {
default void verify(Supplier<Authentication> authentication, T object) {
AuthorizationDecision decision = this.check(authentication, object);
if (decision != null && !decision.isGranted()) {
throw new AccessDeniedException("Access Denied");
}
}
@Nullable
AuthorizationDecision check(Supplier<Authentication> authentication, T object);
}
找到实现类PreAuthorizeAuthorizationManager重写的check()方法:
public AuthorizationDecision check(Supplier<Authentication>
authentication, MethodInvocation mi) {
ExpressionAttribute attribute = this.registry.getAttribute(mi);
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
return null;
} else {
EvaluationContext ctx = this.expressionHandler.createEvaluationContext((Authentication)authentication.get(), mi);
boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx);
return new ExpressionAttributeAuthorizationDecision(granted, attribute);
}
}
其中还有该类中的注册类PreAuthorizeExpressionAttributeRegistry
@NonNull
ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) {
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
PreAuthorize preAuthorize = this.findPreAuthorizeAnnotation(specificMethod);
if (preAuthorize == null) {
return ExpressionAttribute.NULL_ATTRIBUTE;
} else {
Expression preAuthorizeExpression = PreAuthorizeAuthorizationManager.this.expressionHandler.getExpressionParser().parseExpression(preAuthorize.value());
return new ExpressionAttribute(preAuthorizeExpression);
}
}
private PreAuthorize findPreAuthorizeAnnotation(Method method) {
PreAuthorize preAuthorize = (PreAuthorize)AuthorizationAnnotationUtils.findUniqueAnnotation(method, PreAuthorize.class);
return preAuthorize != null ? preAuthorize : (PreAuthorize)AuthorizationAnnotationUtils.findUniqueAnnotation(method.getDeclaringClass(), PreAuthorize.class);
}
在此终于找到了我们的注解@PreAuthorize
createEvaluationContext时找到了AbstractSecurityExpressionHandler类:
public final EvaluationContext
createEvaluationContext(Authentication authentication, T invocation) {
SecurityExpressionOperations root = this.createSecurityExpressionRoot(authentication, invocation);
StandardEvaluationContext ctx = this.createEvaluationContextInternal(authentication, invocation);
ctx.setBeanResolver(this.beanResolver);
ctx.setRootObject(root);
return ctx;
}
createSecurityExpressionRoot来自DefaultMethodSecurityExpressionHandler类:
protected MethodSecurityExpressionOperations
createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
MethodSecurityExpressionRoot root = new MethodSecurityExpressionRoot(authentication);
root.setThis(invocation.getThis());
root.setPermissionEvaluator(this.getPermissionEvaluator());
root.setTrustResolver(this.getTrustResolver());
root.setRoleHierarchy(this.getRoleHierarchy());
root.setDefaultRolePrefix(this.getDefaultRolePrefix());
return root;
}
MethodSecurityExpressionRoot 继承自SecurityExpressionRoot
至此我们终于知道为什么可以在该注解中使用SecurityExpressionRoot中的各种方法。
@PreAuthorize("hasAuthority('sys:menu:list')")
其中hasAuthority中的url路径是根据业务设计自己定义的
表格中是SecurityExpressionRoot常用方法:
方法 | 用法 |
hasRole(String role) | 判断当前用户是否拥有指定角色 |
hasAuthority(String authority) | 判断当前用户是否拥有指定权限 |
isAuthenticated() | 判断当前用户是否已经通过身份验证 |
在PreAuthorizeAuthorizationManager的check方法执行时
boolean granted =
ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx);
此处会对Spel表达式进行解析,若表达式中包含SecurityExpressionRoot中的方法则会调用,否则是将其解析为普通的Spel表达式
@PreAuthorize("@se.hasRole('sys:user:add')")
此处代表调用别名为se的类中的hasRole方法,此处的se为自定义一个bean,可以在其中实现自定义的权限判断逻辑。其中@为Spel表达式中调用bean的意思。
@PreAuthorize("#user.name.equals('haha')")
@PreAuthorize("#id<10")
此处使用方法中的参数进行权限控制,参数可以是实体也可以是基本和包装类型。其中#为Spel表达式中调用方法参数的意思(此处已经由解析器将方法参数注入到Spel上下文中)。