需求是這樣的:項目採用的先後端分離的架構,且使用的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