需求是这样的:项目采用的先后端分离的架构,且使用的RESTFUL风格API,同一个资源的相关请求是同样的url,可是http method不同。java
若是要容许一我的获取某个资源,可是不能建立它,显然基于url的权限设计显然是没法知足需求的。express
当我查阅到了能够基于方法的权限控制以后,我认为这应该是个最佳方案。可是却存在这样一个问题,一个方法针对不一样的入参可能会触发不一样的权限。好比说,一个用户拥有查看A目录的权限,可是没有查看B目录的权限。而这两个动做都是调用的同一个Controller方法,只是根据入参来区分查看不一样的目录。后端
默认的hasAuthority
和hasRole
表达式都没法知足需求,由于它们只能判断一个硬编码的权限或者角色字符串。因此咱们须要用到自定义表达式来高度自定义权限判断以知足需求。下面咱们就来具体介绍如何使用它。安全
咱们将建立一个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