Spring Security基于方法级别的自定义表达式(能够完成任何权限判断)

背景

需求是这样的:项目采用的先后端分离的架构,且使用的RESTFUL风格API,同一个资源的相关请求是同样的url,可是http method不同。java

若是要容许一我的获取某个资源,可是不能建立它,显然基于url的权限设计显然是没法知足需求的。express

当我查阅到了能够基于方法的权限控制以后,我认为这应该是个最佳方案。可是却存在这样一个问题,一个方法针对不一样的入参可能会触发不一样的权限。好比说,一个用户拥有查看A目录的权限,可是没有查看B目录的权限。而这两个动做都是调用的同一个Controller方法,只是根据入参来区分查看不一样的目录。后端

默认的hasAuthorityhasRole表达式都没法知足需求,由于它们只能判断一个硬编码的权限或者角色字符串。因此咱们须要用到自定义表达式来高度自定义权限判断以知足需求。下面咱们就来具体介绍如何使用它。安全

示例

咱们将建立一个canRead的表达式。当入参为"A"时,将判断当前用户是否有查看A的权限;当入参为"B"时,将判断当前用户是否有查看B的权限。bash

配置

为了建立自定义表达式,咱们首先须要实现root表达式:架构

public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

    private Object filterObject;
    private Object returnObject;

    public CustomMethodSecurityExpressionRoot(Authentication authentication) {
        super(authentication);
    }

    // 咱们的自定义表达式
    public boolean canRead(String foo) {
        if (foo.equals("A") && !this.hasAuthority("CAN_READ_A")) {
            return false;
        }

        if (foo.equals("B") && !this.hasAuthority("CAN_READ_B")) {
            return false;
        }
        
        return true;
    }

    @Override
    public Object getFilterObject() {
        return this.filterObject;
    }

    @Override
    public Object getReturnObject() {
        return this.returnObject;
    }

    @Override
    public Object getThis() {
        return this;
    }

    @Override
    public void setFilterObject(Object obj) {
        this.filterObject = obj;
    }

    @Override
    public void setReturnObject(Object obj) {
        this.returnObject = obj;
    }
}
复制代码

接下来,咱们须要把CustomMethodSecurityExpressionRoot注入到表达式处理器内:app

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
    private AuthenticationTrustResolver trustResolver = 
      new AuthenticationTrustResolverImpl();
 
    @Override
    protected MethodSecurityExpressionOperations createSecurityExpressionRoot( Authentication authentication, MethodInvocation invocation) {
        CustomMethodSecurityExpressionRoot root = 
          new CustomMethodSecurityExpressionRoot(authentication);
        root.setPermissionEvaluator(getPermissionEvaluator());
        root.setTrustResolver(this.trustResolver);
        root.setRoleHierarchy(getRoleHierarchy());
        return root;
    }
}
复制代码

而后须要把CustomMethodSecurityExpressionHandler写到方法安全配置里面:前后端分离

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        CustomMethodSecurityExpressionHandler expressionHandler = 
          new CustomMethodSecurityExpressionHandler();
        expressionHandler.setPermissionEvaluator(new CustomPermissionEvaluator());
        return expressionHandler;
    }
}
复制代码

使用

@PreAuthorize("canRead(#foo)")
@GetMapping("/")
public Foo getFoo(@RequestParam("foo") String foo) {
    return fooService.findAll(foo);
}
复制代码

若是用户访问A,可是没有CAN_READ_A权限,接口将会返回403。ide

相关文章
相关标签/搜索