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 AntPathMatcher
URL 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/user
the rights he owned GET
, PUT
, POST
three 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-service
adding 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_permission
add 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]RequestPath
format
@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 SimpleGrantedAuthority
that 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 UserDetailServiceImpl
use 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!