SpringCloud Alibaba 25 | Gateway Restful interface interception

Preface

Before integration RBAC authorization referred to in the article can SpringCloud "authorization based on the path matcher" user permissions check at the gateway layer, to achieve the principle of this approach is based on requests received after Springcloud Gateway ReactiveAuthorizationManager#check(Mono<Authentication> authenticationMono, AuthorizationContext authorizationContext)based method AntPathMatcherURL check the current visit Whether it is in the permission URL owned by the user, if it can be matched, it means that it has the access permission and is released to the back-end service, otherwise the user is prompted to have no access permission.

The specific implementation is described in the above article, if you are unclear, you can refer to it again. Article address:

http://javadaily.cn/articles/2020/08/07/1596772909329.html

However, the previous implementation has a problem, that is, it does not support restful style URL paths.

For example, a microservice has the following API
GET /v1/pb/user
POST /v1/pb/user
PUT /v1/pb/user

In this way the gateway by request.getURI().getPath()obtaining a path to user requests is the same address, to a method for the user to grant /v1/pb/userthe rights he owned GET, PUT, POSTthree different permissions, it is clear that this does not meet the fine access control. In this chapter, we will solve the problem of the Restful interface interception, so that it can support refined permission control.

Scene demonstration

Let's take a look at an actual case and demonstrate this scenario. In account-serviceadding a blog user management functions in the module, the interface methods are as follows:

 

Interface URL HTTP method Interface Description
/blog/user POST Save user
/blog/user/{id} GET Query user
/blog/user/{id} DELETE delete users
/blog/user/{id} PUT Update user information

Then we sys_permissionadd two user permissions table, then it granted to user roles


In the verification method of the gateway layer, you can see that 2 permissions have been added


Since DELETE and PUT correspond to the permission path /blog/user/{id}, this means that when the query permission is granted to the user, the user also has the permission to delete and update.

solution

Seeing this, most of the students should have thought that if you want to realize the refined authority management of Restful style, it is not enough to use the URL path alone. It needs to be used with Method.

The most critical point is "You need to add a method field to the permission table, and then when the gateway checks it, it will judge the request path and match the request method." The implementation steps are as follows:

  • Modify the permission table and add method fields

 
  • When the loadUserByUsername()method constructs the user authority, the method corresponding to the authority is also spliced ​​on the authority. The key code is as follows:

@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+"]不存在");
 }
}

The user permissions constructed by the above code are as follows:

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

[POST]/account-service/blog/user

You can view through code debugging:

 
  • Authorization verification method AccessManager#check(), verification [MEHOTD]RequestPathformat

@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));

}

Check method 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;
}
  • So when the Delete method is requested, it will prompt that there is no permission


There is another scheme here, the principle of implementation is similar to the above, just a brief mention.

First of all, it is necessary to add a METHOD field in the permission table.

Then the permission class used in the project is SimpleGrantedAuthoritythat this can only store one permission field. We can customize a permission entity class so that it can store url and 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;
    }
}

In UserDetailServiceImpluse when building a custom user permissionsMethodGrantedAuthority

The method of gateway layer verification still needs to be the same as above, which is to verify both Method and URL.

The above solves the problem of verifying Restful-style user permission verification at the gateway layer. I hope it will help you!

 

Guess you like

Origin blog.csdn.net/jianzhang11/article/details/112646022