RESTful 访问权限管理实现思路,采用路径匹配神器之 AntPathMatcher

简介: RESTful 访问权限管理实现思路,采用路径匹配神器之 AntPathMatcher

项目中经常在写程序时需要对路径进行匹配,比如说:资源的拦截与加载、RESTful访问控制、审计日志采集、等,伟大的SpringMVC在匹配Controller路径时是如何实现的?全都归功于ant匹配规则。

Spring源码之AntPathMatcher,这个工具类匹配很强大,采用的是ant匹配规则。

什么是ant匹配规则?

字符wildcard 描述
? 匹配一个字符(matches one character)
* 匹配0个及以上字符(matches zero or more characters )
** 匹配0个及以上目录directories(matches zero or more ‘directories’ in a path )

这个匹配规则很简单,采用简洁明了的方式来进行匹配解析,简化版本的正则。

结合官方的示例来理解一下

Pattern 匹配说明
com/t?st.jsp 匹配: com/test.jsp , com/tast.jsp , com/txst.jsp
com/*.jsp 匹配: com文件夹下的全部.jsp文件
com/**/test.jsp 匹配: com文件夹和子文件夹下的全部.jsp文件
org/springframework/*/.jsp 匹配: org/springframework文件夹和子文件夹下的全部.jsp文件
org/**/servlet/bla.jsp 匹配: org/springframework/servlet/bla.jsp , org/springframework/testing/servlet/bla.jsp , org/servlet/bla.jsp

如何实现RESTful访问权限管理?

在微服务和前后端分离的开发模式下,往往会使用RESTful来开发后端服务,那服务的访问权限控制就是一个问题,那下来我们就说一下如何实现RESTful访问权限管理。

权限资源类型

资源分为如下两种类型:

  • public(公有):public为不控制访问的资源
  • private(私有):private为需要被控制访问的资源

ps.这种方式资源管理的相对严格一些,如果想管理的粗矿一些,可以不需要public,只要在private中未找到的资源就是不控制访问的资源即可,实现时可以根据自己的业务场景来调整。

匹配原则

基础匹配规则:使用ant匹配规则

SpringMVC的路径匹配原则中有一个原则是:最长匹配原则(has more characters)

什么是最长匹配原则(has more characters)?

最长匹配原则(has more characters)简单的理解就是目标URL有多个pattern都可以匹配上就取最长的那个pattern

例如:请求的URL/app/dir/file.jsp,有两个pattern/**/*.jsp/app/dir/*.jsp都可以匹配成功,那么会根据pattern的长度来控制是否采用哪一个,这里使用/app/dir/*.jsp来匹配。

为什么要使用最长匹配原则?我的理解是长的pattern更符合目标URL格式,短的pattern往往是范围较广的,匹配取最适合的pattern也是比较符合预期的。

根据服务名分类

在做资源访问权限时往往会有多个服务可能会出现相同的资源路径,因此增加一级服务名来对资源进行分类。

例如:GET /v1/service1/product/1GET /v1/service2/product/1,根据二级目录service名称来对服务进行模块化分割。/v1为RESTful版本号

ps.服务名就是为了做资源分类

权限验证逻辑

  • 验证

public

  • 资源
  • 去除末尾"/"
  • 验证service服务名,服务名为空返回没有权限
  • 获取服务名下enabled=true的资源表,结果进行cache,结果为空没有权限
  • 根据pattern长度倒序
  • 匹配method,匹配成功进行下一步匹配
  • 匹配请求的url,匹配成功返回有权限,反之返回没有权限
  • 验证

private

  • 资源
  • 去除末尾"/"
  • 验证service服务名,服务名为空返回没有权限
  • 获取服务名下用户角色对应的资源列表聚合结果,结果进行cache,结果为空返回没有权限
  • 根据pattern长度倒序
  • 匹配method,匹配成功进行下一步匹配,反之continue
  • 匹配请求的url,匹配成功进行下一步匹配,反之continue
  • 检查匹配成功的url是否为禁用状态,如果禁用返回无权限,反之进行下一步匹配
  • 匹配成功的url对应的角色列表进行登录用户的角色匹配
  • 角色匹配成功返回有权限,反之返回没有权限

ps.method是GETPOSTPUTPATCHDELETEservice是服务模块名

缓存结构

  • private资源数据
  • 结构:hash
  • cache key=${APPNAME}.METADATA.RESOURCEfield=${RESOURCE_ID}value=Resource对象
  • public资源数据
  • 结构:hash
  • cache key=${APPNAME}.METADATA.RESOURCE.PUBLICfield=${SERVICE}value=List<Resource>
  • 用户关联角色数据
  • 结构:hash
  • cache key=${APPNAME}.METADATA.ROLEfield=${USER_ID}value=List<ROLE_ID>
  • 角色关联的资源数据
  • 结构:hash
  • cache key=${APPNAME}.METADATA.MAPPINGfield=${SERVICE}value=List<Metadata<Resource,List<ROLE_ID>>>
  • 这里存储的数据结构是反向的,获取服务下的资源列表,每个资源数据中会有拥有这个资源的角色列表。

ps.缓存可以使用分布式的redisredisson、如果单机可以使用jvm cache

缓存控制

  • private资源数据发生变更时
  • 调用MetadataCache.invalidResources(),失效cache key=${APPNAME}.METADATA.RESOURCE下所有数据
  • public资源数据发生变更时
  • 调用MetadataCache.invalidPublicResource(service)失效服务名下的public资源集合,失效cache key=${APPNAME}.METADATA.RESOURCE.PUBLIC下的某个${SERVICE}数据
  • 调用MetadataCache.invalidPublicResource()失效所有服务名下的public资源集合,失效cache key=${APPNAME}.METADATA.RESOURCE.PUBLIC下所有数据
  • 用户关联角色数据发生变更时
  • 调用MetadataCache.invalidUserRoles(userId)失效用户下的角色集合,失效cache key=${APPNAME}.METADATA.ROLE下所有数据
  • 角色关联的资源数据发生变更时
  • 调用MetadataCache.invalidMetadata(service)失效服务名下的资源角色聚合对象,失效cache key=${APPNAME}.METADATA.MAPPING下的某个${SERVICE}数据
  • 调用MetadataCache.invalidMetadata()失效所有服务名下的资源角色聚合对象,失效cache key=${APPNAME}.METADATA.MAPPING下所有数据

ps.在以上触发点上对缓存数据进行更新,这里采用失效再加载方式

缓存加载

  • private资源数据,在系统启动加载,加载所有私有资源,如果失效了,会在private匹配的时再进行加载
  • public资源数据,在public匹配时加载,通过服务名加载,如果失效了,会在public匹配时再进行加载
  • 用户关联角色数据,在private匹配时加载,如果失效了,会在private匹配时再进行加载
  • 角色关联的资源数据,在private匹配时加载,如果失效了,会在private匹配时再进行加载

ps.资源数据加载触发点

pattern配置建议

  • 配置资源时,将不需要配置权限的url配置为public资源
  • 每个服务名下建议配置一个**(双星)通配符给超级管理员使用,例如:/v1/products/**
  • 每个url的第二级目录要与服务名一致,例如:/v1/products/{pid},服务名为products
  • url的目录结构必须大于两级目录,例如:/v1/products/{pid},不允许为:/v1/{pid}

url

  • 与权限通配符映射关系,前面

url

  • ,后面

pattern

  • 例如:/v1/products/{pid} -> /v1/products/*
  • 例如:/v1/products/{pid}/skus/{sid} -> /v1/products/*/skus/*
  • 例如:/v1/products/enabled -> /v1/products/enabled
  • 例如:/v1/products/**,匹配products目录下所有目录

以上就是一种RESTful资源管理的实现思路,能控制到RESTful的方法级别,在前后端分离的项目可以使用这种方式来控制访问权限。

source ://ningyu1.github.io/site/post/62-ant-path-matcher/
相关文章
开发指南036-排除类
底层集成了很多类,例如对微信支付的支持
|
4月前
|
Java
自主定义访问路径-----SpringBoot自主定义静态资源访问路径的方法
自主定义访问路径-----SpringBoot自主定义静态资源访问路径的方法
|
4月前
|
前端开发 JavaScript Linux
若依修改之后,无法访问前端项目如何解决,只能访问后端的接口,我的接口8083,端不显示咋解决?在vue.config.js文件中的映射路径要跟后端匹配,到软件商店里找到Ngnix配置代理,设80不用加
若依修改之后,无法访问前端项目如何解决,只能访问后端的接口,我的接口8083,端不显示咋解决?在vue.config.js文件中的映射路径要跟后端匹配,到软件商店里找到Ngnix配置代理,设80不用加
|
4月前
|
前端开发 JavaScript Java
文本----简单编写文章的方法(中),后端接口的编写,自己编写好页面就上传到自己的服务器上,使用富文本编辑器进行编辑,想写好一个项目,先分析一下需求,再理一下实现思路,再搞几层,配好参数校验,lomb
文本----简单编写文章的方法(中),后端接口的编写,自己编写好页面就上传到自己的服务器上,使用富文本编辑器进行编辑,想写好一个项目,先分析一下需求,再理一下实现思路,再搞几层,配好参数校验,lomb
|
5月前
|
前端开发 Java 数据库连接
项目API借口的根路径怎样设置
项目API借口的根路径怎样设置
|
6月前
|
前端开发
【前端学习】—ES6新增的方法有哪些(十五)
【前端学习】—ES6新增的方法有哪些(十五)
|
文件存储
Yii2.0框架提供了内置的文件访问组件,可以通过配置只允许访问指定的目录,防止非法文件的包含。这个如何使用?
Yii2.0框架提供了内置的文件访问组件,可以通过配置只允许访问指定的目录,防止非法文件的包含。这个如何使用?
146 0
|
搜索推荐 JavaScript 前端开发
python接口自动化(十八)--重定向(Location)(详解)
在实际工作中,有些接口请求完以后会重定向到别的url,而你却需要重定向前的url。URL主要是针对虚拟空间而言,因为不是自己独立管理的服务器,所以无法正常进行常规的操作。但是自己又不希望通过主域名的二级目录进行访问,而 是希望通过主域名的二级域名进行访问。所以这个时候就会用到URL重定向。
225 0
python接口自动化(十八)--重定向(Location)(详解)
|
JavaScript Apache 开发者
通过 express 模拟 Apache 实现静态资源托管服务(补充)|学习笔记
快速学习通过 express 模拟 Apache 实现静态资源托管服务(补充)
|
Java Spring
Springboot 同一次调用日志怎么用ID串起来,方便最终查找
Springboot 同一次调用日志怎么用ID串起来,方便最终查找
452 0
Springboot 同一次调用日志怎么用ID串起来,方便最终查找