千家信息网

如何实现网关Restful接口拦截

发表于:2025-02-06 作者:千家信息网编辑
千家信息网最后更新 2025年02月06日,这篇文章主要介绍"如何实现网关Restful接口拦截",在日常操作中,相信很多人在如何实现网关Restful接口拦截问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"如何实
千家信息网最后更新 2025年02月06日如何实现网关Restful接口拦截

这篇文章主要介绍"如何实现网关Restful接口拦截",在日常操作中,相信很多人在如何实现网关Restful接口拦截问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"如何实现网关Restful接口拦截"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

场景演示

我们看下实际的案例,演示下这种场景。在 account-service模块下增加一个博客用户管理功能,有如下的接口方法:

接口URLHTTP方法接口说明
/blog/userPOST保存用户
/blog/user/{id}GET查询用户
/blog/user/{id}DELETE删除用户
/blog/user/{id}PUT更新用户信息

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


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


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

解决方案

看到这里大部分同学应该想到了,要想实现Restful风格的精细化权限管理单单通过URL路径是不行的,需要搭配Method一起使用。

最关键的点就是「需要给权限表加上方法字段,然后在网关校验的时候即判断请求路径又匹配请求方法。」 实现步骤如下:

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

  • loadUserByUsername()方法构建用户权限的时候将权限对应的Method也拼接在权限上,关键代码如下:
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
//获取本地用户
SysUser sysUser = sysUserMapper.selectByUserName(userName);
if(sysUser != null){
//获取当前用户的所有角色
List roleList = sysRoleService.listRolesByUserId(sysUser.getId());
sysUser.setRoles(roleList.stream().map(SysRole::getRoleCode).collect(Collectors.toList()));
List roleIds = roleList.stream().map(SysRole::getId).collect(Collectors.toList());
//获取所有角色的权限
List permissionList = sysPermissionService.listPermissionsByRoles(roleIds);
//拼接method
List permissionUrlList = permissionList.stream()
.map(item -> "["+item.getMethod()+"]"+item.getUrl())
.collect(Collectors.toList());
sysUser.setPermissions(permissionUrlList);
//构建oauth3的用户
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 check(Mono 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 OAuth3Authentication){
OAuth3Authentication authentication = (OAuth3Authentication) auth;
String clientId = authentication.getOAuth3Request().getClientId();
log.info("clientId is {}",clientId);
//用户的权限集合
Collection 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接口拦截"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

0