然后我们在 sys_permission表中添加2个用户权限,再将其授予给用户角色


在网关层的校验方法中可以看到已经增加了2个权限


由于DELETE 和 PUT对应的权限路径都是 /blog/user/{id},这样就是当给用户授予了查询权限后此用户也拥有了删除和更新的权限。

解决方案

看到这里大部分同学应该想到了,要想实现Restful风格的精细化权限管理单单通过URL路径是不行的,需要搭配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

可以通过代码调试查看:

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



这里还有另外一种方案,实现的原理跟上面差不多,只简单提一下。

首先还是得在权限表中新增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风格的用户权限校验,希望对你有所帮助!


SpringCloud Alibaba二十五 | 网关Restful接口拦截-LMLPHP





本文分享自微信公众号 - JAVA日知录(javadaily)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

03-07 16:33