SpringCloud Alibaba二十五 | 網關Restful接口攔截

前言

以前在 集成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權限後他就擁有了 GETPUTPOST三種不一樣權限,很顯然這樣不能知足精細權限控制。本章內容咱們就來解決這個Restful接口攔截的問題,使其能支持精細化的權限控制。後端

場景演示

咱們看下實際的案例,演示下這種場景。在 account-service模塊下增長一個博客用戶管理功能,有以下的接口方法:跨域

接口URL HTTP方法 接口說明
/blog/user POST 保存用戶
/blog/user/{id} GET 查詢用戶
/blog/user/{id} DELETE 刪除用戶
/blog/user/{id} PUT 更新用戶信息

而後咱們在 sys_permission表中添加2個用戶權限,再將其授予給用戶角色服務器


在網關層的校驗方法中能夠看到已經增長了2個權限微信


因爲DELETE 和 PUT對應的權限路徑都是 /blog/user/{id},這樣就是當給用戶授予了查詢權限後此用戶也擁有了刪除和更新的權限。restful

解決方案

看到這裏大部分同窗應該想到了,要想實現Restful風格的精細化權限管理單單經過URL路徑是不行的,須要搭配Method一塊兒使用。架構

最關鍵的點就是「須要給權限表加上方法字段,而後在網關校驗的時候即判斷請求路徑又匹配請求方法。」 實現步驟以下:

  • 修改權限表,新增方法字段

  • 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

能夠經過代碼調試查看:

  • 權限校驗方法 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方法時就會提示沒有權限

這裏還有另一種方案,實現的原理跟上面差很少,只簡單提一下。

首先仍是得在權限表中新增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風格的用戶權限校驗,但願對你有所幫助!


End




乾貨分享



這裏爲你們準備了一份小小的禮物,關注公衆號,輸入以下代碼,便可得到百度網盤地址,無套路領取!

001:《程序員必讀書籍》
002:《從無到有搭建中小型互聯網公司後臺服務架構與運維架構》
003:《互聯網企業高併發解決方案》
004:《互聯網架構教學視頻》
006:《SpringBoot實現點餐系統》
007:《SpringSecurity實戰視頻》
008:《Hadoop實戰教學視頻》
009:《騰訊2019Techo開發者大會PPT》

010: 微信交流羣






近期熱文top



一、關於JWT Token 自動續期的解決方案

二、SpringBoot開發祕籍-事件異步處理

三、架構師之路-服務器硬件掃盲

四、基於Prometheus和Grafana的監控平臺 - 環境搭建

五、RocketMQ進階 - 事務消息



我就知道你「在看」





本文分享自微信公衆號 - JAVA日知錄(javadaily)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索