溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何實(shí)現(xiàn)網(wǎng)關(guān)Restful接口攔截

發(fā)布時(shí)間:2021-10-20 11:51:17 來源:億速云 閱讀:477 作者:iii 欄目:編程語言

這篇文章主要介紹“如何實(shí)現(xiàn)網(wǎng)關(guān)Restful接口攔截”,在日常操作中,相信很多人在如何實(shí)現(xiàn)網(wǎng)關(guān)Restful接口攔截問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何實(shí)現(xiàn)網(wǎng)關(guān)Restful接口攔截”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

場景演示

我們看下實(shí)際的案例,演示下這種場景。在 account-service模塊下增加一個博客用戶管理功能,有如下的接口方法:

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

然后我們在 sys_permission表中添加2個用戶權(quán)限,再將其授予給用戶角色

如何實(shí)現(xiàn)網(wǎng)關(guān)Restful接口攔截      


在網(wǎng)關(guān)層的校驗(yàn)方法中可以看到已經(jīng)增加了2個權(quán)限

如何實(shí)現(xiàn)網(wǎng)關(guān)Restful接口攔截      


由于DELETE 和 PUT對應(yīng)的權(quán)限路徑都是 /blog/user/{id},這樣就是當(dāng)給用戶授予了查詢權(quán)限后此用戶也擁有了刪除和更新的權(quán)限。     

解決方案

看到這里大部分同學(xué)應(yīng)該想到了,要想實(shí)現(xiàn)Restful風(fēng)格的精細(xì)化權(quán)限管理單單通過URL路徑是不行的,需要搭配Method一起使用。

最關(guān)鍵的點(diǎn)就是「需要給權(quán)限表加上方法字段,然后在網(wǎng)關(guān)校驗(yàn)的時(shí)候即判斷請求路徑又匹配請求方法。」 實(shí)現(xiàn)步驟如下:

  • 修改權(quán)限表,新增方法字段
如何實(shí)現(xiàn)網(wǎng)關(guān)Restful接口攔截      

     
  • 在         loadUserByUsername()方法構(gòu)建用戶權(quán)限的時(shí)候?qū)?quán)限對應(yīng)的Method也拼接在權(quán)限上,關(guān)鍵代碼如下:
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
 //獲取本地用戶
 SysUser sysUser = sysUserMapper.selectByUserName(userName);
 if(sysUser != null){
  //獲取當(dāng)前用戶的所有角色
  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());
  //獲取所有角色的權(quán)限
  List<SysPermission> permissionList = sysPermissionService.listPermissionsByRoles(roleIds);
  //拼接method
  List<String> permissionUrlList = permissionList.stream()
                    .map(item -> "["+item.getMethod()+"]"+item.getUrl())
                    .collect(Collectors.toList());
  sysUser.setPermissions(permissionUrlList);
  //構(gòu)建oauth3的用戶
  return buildUserDetails(sysUser);
 }else{
  throw  new UsernameNotFoundException("用戶["+userName+"]不存在");
 }
}
     

通過上面的代碼構(gòu)建的用戶權(quán)限如下:

[GET]/account-service/blog/user/{id}

[POST]/account-service/blog/user

可以通過代碼調(diào)試查看:

如何實(shí)現(xiàn)網(wǎng)關(guān)Restful接口攔截      
  • 權(quán)限校驗(yàn)方法         AccessManager#check(),校驗(yàn)         [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. 對應(yīng)跨域的預(yù)檢請求直接放行
 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));

}
     

校驗(yàn)方法 checkAuthorities()

private boolean checkAuthorities(Authentication auth, String requestPath) {
 if(auth instanceof OAuth3Authentication){
  OAuth3Authentication authentication = (OAuth3Authentication) auth;
  String clientId = authentication.getOAuth3Request().getClientId();
  log.info("clientId is {}",clientId);
  //用戶的權(quán)限集合
  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;
}
        
  • 這樣當(dāng)請求Delete方法時(shí)就會提示沒有權(quán)限
如何實(shí)現(xiàn)網(wǎng)關(guān)Restful接口攔截      
如何實(shí)現(xiàn)網(wǎng)關(guān)Restful接口攔截      

這里還有另外一種方案,實(shí)現(xiàn)的原理跟上面差不多,只簡單提一下。

首先還是得在權(quán)限表中新增METHOD字段,這是必須的。

然后項(xiàng)目中使用的權(quán)限類是 SimpleGrantedAuthority,這個只能存儲一個權(quán)限字段,我們可以自定義一個權(quán)限實(shí)體類,讓其可以存儲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中構(gòu)建用戶權(quán)限時(shí)使用自定義的 MethodGrantedAuthority

網(wǎng)關(guān)層校驗(yàn)的方法還是需要跟上面一樣,既校驗(yàn)Method 又 校驗(yàn) URL。

到此,關(guān)于“如何實(shí)現(xiàn)網(wǎng)關(guān)Restful接口攔截”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI