前言
以前在 集成RBAC受權 的文章中提到了SpringCloud能夠「基於路徑匹配器受權」在網關層進行用戶權限校驗,這種方式的實現原理是Springcloud Gateway接受到請求後根據 ReactiveAuthorizationManager#check(Mono<Authentication> authenticationMono, AuthorizationContext authorizationContext)
方法基於 AntPathMatcher
校驗當前訪問的URL是否在用戶擁有的權限URL中,若是能匹配上則說明擁有訪問權限並放行到後端服務,不然提示用戶無訪問權限。html
具體實現方式在上面文章中有闡述,若是有不清楚的能夠再次查閱。 文章地址:http://javadaily.cn/articles/2020/08/07/1596772909329.htmljava
不過以前的實現方式有個問題,就是不支持restful風格的url路徑。程序員
例如一個微服務有以下API
GET /v1/pb/user
POST /v1/pb/user
PUT /v1/pb/userweb
這樣在網關經過 request.getURI().getPath()
方法獲取到用戶請求路徑的時候都是同一個地址,給一個用戶授予 /v1/pb/user
權限後他就擁有了 GET
、PUT
、POST
三種不一樣權限,很顯然這樣不能知足精細權限控制。本章內容咱們就來解決這個Restful接口攔截的問題,使其能支持精細化的權限控制。後端
場景演示
咱們看下實際的案例,演示下這種場景。在 account-service
模塊下增長一個博客用戶管理功能,有以下的接口方法:跨域
接口URL | HTTP方法 | 接口說明 |
---|---|---|
/blog/user | POST | 保存用戶 |
/blog/user/{id} | GET | 查詢用戶 |
/blog/user/{id} | DELETE | 刪除用戶 |
/blog/user/{id} | PUT | 更新用戶信息 |
而後咱們在 sys_permission
表中添加2個用戶權限,再將其授予給用戶角色服務器
![](http://static.javashuo.com/static/loading.gif)
在網關層的校驗方法中能夠看到已經增長了2個權限微信
![](http://static.javashuo.com/static/loading.gif)
因爲DELETE 和 PUT對應的權限路徑都是 /blog/user/{id}
,這樣就是當給用戶授予了查詢權限後此用戶也擁有了刪除和更新的權限。restful
解決方案
看到這裏大部分同窗應該想到了,要想實現Restful風格的精細化權限管理單單經過URL路徑是不行的,須要搭配Method一塊兒使用。架構
最關鍵的點就是「須要給權限表加上方法字段,而後在網關校驗的時候即判斷請求路徑又匹配請求方法。」 實現步驟以下:
-
修改權限表,新增方法字段
![](http://static.javashuo.com/static/loading.gif)
-
在 loadUserByUsername()
方法構建用戶權限的時候將權限對應的Method也拼接在權限上,關鍵代碼以下:
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
//獲取本地用戶
SysUser sysUser = sysUserMapper.selectByUserName(userName);
if(sysUser != null){
//獲取當前用戶的全部角色
List<SysRole> roleList = sysRoleService.listRolesByUserId(sysUser.getId());
sysUser.setRoles(roleList.stream().map(SysRole::getRoleCode).collect(Collectors.toList()));
List<Integer> roleIds = roleList.stream().map(SysRole::getId).collect(Collectors.toList());
//獲取全部角色的權限
List<SysPermission> permissionList = sysPermissionService.listPermissionsByRoles(roleIds);
//拼接method
List<String> permissionUrlList = permissionList.stream()
.map(item -> "["+item.getMethod()+"]"+item.getUrl())
.collect(Collectors.toList());
sysUser.setPermissions(permissionUrlList);
//構建oauth2的用戶
return buildUserDetails(sysUser);
}else{
throw new UsernameNotFoundException("用戶["+userName+"]不存在");
}
}
經過上面的代碼構建的用戶權限以下:
[GET]/account-service/blog/user/{id}
[POST]/account-service/blog/user
能夠經過代碼調試查看:
![](http://static.javashuo.com/static/loading.gif)
-
權限校驗方法 AccessManager#check()
,校驗[MEHOTD]RequestPath
格式
@Override
public Mono<AuthorizationDecision> check(Mono<Authentication> authenticationMono, AuthorizationContext authorizationContext) {
ServerWebExchange exchange = authorizationContext.getExchange();
ServerHttpRequest request = exchange.getRequest();
//請求資源
String requestPath = request.getURI().getPath();
//拼接method
String methodPath = "["+request.getMethod()+"]" + requestPath;
// 1. 對應跨域的預檢請求直接放行
if(request.getMethod() == HttpMethod.OPTIONS){
return Mono.just(new AuthorizationDecision(true));
}
// 是否直接放行
if (permitAll(requestPath)) {
return Mono.just(new AuthorizationDecision(true));
}
return authenticationMono.map(auth -> new AuthorizationDecision(checkAuthorities(auth, methodPath)))
.defaultIfEmpty(new AuthorizationDecision(false));
}
校驗方法 checkAuthorities()
:
private boolean checkAuthorities(Authentication auth, String requestPath) {
if(auth instanceof OAuth2Authentication){
OAuth2Authentication authentication = (OAuth2Authentication) auth;
String clientId = authentication.getOAuth2Request().getClientId();
log.info("clientId is {}",clientId);
//用戶的權限集合
Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
return authorities.stream()
.map(GrantedAuthority::getAuthority)
//ROLE_開頭的爲角色,須要過濾掉
.filter(item -> !item.startsWith(CloudConstant.ROLE_PREFIX))
.anyMatch(permission -> ANT_PATH_MATCHER.match(permission, requestPath));
}
return true;
}
-
這樣當請求Delete方法時就會提示沒有權限
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
這裏還有另一種方案,實現的原理跟上面差很少,只簡單提一下。
首先仍是得在權限表中新增METHOD字段,這是必須的。
而後項目中使用的權限類是 SimpleGrantedAuthority
,這個只能存儲一個權限字段,咱們能夠自定義一個權限實體類,讓其能夠存儲url 和 method。
@Data
public class MethodGrantedAuthority implements GrantedAuthority {
private String method;
private String url;
public MethodGrantedAuthority(String method, String url){
this.method = method;
this.url = url;
}
@Override
public String getAuthority() {
return "["+method+"]" + url;
}
}
在 UserDetailServiceImpl
中構建用戶權限時使用自定義的 MethodGrantedAuthority
網關層校驗的方法仍是須要跟上面同樣,既校驗Method 又 校驗 URL。
以上就解決了在網關層校驗Restful風格的用戶權限校驗,但願對你有所幫助!
乾貨分享
這裏爲你們準備了一份小小的禮物,關注公衆號,輸入以下代碼,便可得到百度網盤地址,無套路領取!
001:《程序員必讀書籍》
002:《從無到有搭建中小型互聯網公司後臺服務架構與運維架構》
003:《互聯網企業高併發解決方案》
004:《互聯網架構教學視頻》
006:《SpringBoot實現點餐系統》
007:《SpringSecurity實戰視頻》
008:《Hadoop實戰教學視頻》
009:《騰訊2019Techo開發者大會PPT》
010: 微信交流羣
近期熱文top
我就知道你「在看」
![](http://static.javashuo.com/static/loading.gif)
本文分享自微信公衆號 - JAVA日知錄(javadaily)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。