#6解析@PreAuthorize以及其中的Spel

发布时间:2024年01月16日

目录

1、@PreAuthorize

1.1、寻找@ PreAuthorize

1.2、寻找SecurityExpressionRoot

1.3、用法示例1

2、Spel在@ PreAuthorize的其他用法

2.1、分析

2.2、用法示例2



往期精彩:

#1maven打包时指定profile过滤resources的一些总结-CSDN博客

#2Vite+Vue3+SpringMVC前后端分离 解决跨域问题和session每次请求不一致问题-CSDN博客

#3Jenkins(Windows环境)版本升级、迁移、负载均衡、双机器同步与备份-CSDN博客

#4详解@RequestParam、@RequestBody和@PathVariable-CSDN博客

#5解析filter为什么不能注入bean和解决办法以及filter、interceptor、aspect之间的执行顺序-CSDN博客

1、@PreAuthorize

1.1、寻找@ PreAuthorize

先看一下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

1.2、寻找SecurityExpressionRoot

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中的各种方法。

1.3、用法示例1

@PreAuthorize("hasAuthority('sys:menu:list')")

其中hasAuthority中的url路径是根据业务设计自己定义的

表格中是SecurityExpressionRoot常用方法:

方法用法
hasRole(String role)判断当前用户是否拥有指定角色
hasAuthority(String authority)判断当前用户是否拥有指定权限
isAuthenticated()判断当前用户是否已经通过身份验证

2、Spel在@ PreAuthorize的其他用法

2.1、分析

在PreAuthorizeAuthorizationManager的check方法执行时

boolean granted = 
ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx);

此处会对Spel表达式进行解析,若表达式中包含SecurityExpressionRoot中的方法则会调用,否则是将其解析为普通的Spel表达式

2.2、用法示例2

@PreAuthorize("@se.hasRole('sys:user:add')")

此处代表调用别名为se的类中的hasRole方法,此处的se为自定义一个bean,可以在其中实现自定义的权限判断逻辑。其中@为Spel表达式中调用bean的意思。

@PreAuthorize("#user.name.equals('haha')")
@PreAuthorize("#id<10")

此处使用方法中的参数进行权限控制,参数可以是实体也可以是基本和包装类型。其中#为Spel表达式中调用方法参数的意思(此处已经由解析器将方法参数注入到Spel上下文中)。

如果对你有帮助,点赞、收藏、关注是我更新的动力!

文章来源:https://blog.csdn.net/weixin_42718399/article/details/135558235
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。