0%

基于Spring框架的权限控制系统

最近在做的项目需要实现一套权限控制方案

项目使用Spring + Vue.js构建前后端分离的系统,实现了基于RESTful API的权限控制。

权限控制系统包含两部分:接口访问控制以及数据实体访问控制。

后端可以分为三层:Controller Layer, Service Layer, 以及Data Persistence Layer

需求分析及方案设计

需求描述

甲方要求实现一套细粒度且可个性化配置的权限控制系统。

  1. 对前端展示的页面,不同用户有不同的操作界面。
  2. 不同用户对数据实体的操作权限不同。
  3. 对没有权限的操作,用户可以提出申请,审批通过之后自动执行(本次)操作。
  4. 不同用户能访问的数据实体(数据库表中的行)不同。
  5. 如果可能,需要实现对具体数据实体中的个别属性(数据库表中的列)的访问控制。
  • 因为此系统是前后端分离、通过RESTful API来操作数据的,所以权限控制只能在后端进行。而在具体实践中,涉及增删改功能的前端的按钮与后端的API大多是一一对应的,只需要在后端对Controller层进行控制就好了。
  • 对于数据实体的过滤,试图在数据从持久层取出后通过AOP做切面实现。
  • 申请/审核思路大概是先往数据库中写入冗余的新数据并添加标示位,标记为无效数据。当审批链完成后,重新标记为有效数据(并删除旧数据)。

方案设计

流程概述

对API进行控制的时序图如图所示。Spring Security提供了AbstractSecurityInterceptor基类来拦截所有的请求。并在此拦截器中调用AccessDecisionManager接口实现权限访问,根据结果返回401 Unauthorized或者403 Forbidden .

对实体数据进行控制的时序图如图所示。通过切面增强,在切点上根据RBAC模式移除用户无权限访问的数据。

基于角色的权限控制

为了实现细粒度的权限控制,决定采用Role_Based Access Control 1.0模式的权限控制方案。

权限相关的实体分别为UserRole以及Resource

  • Resouce为具体的资源,Role则为Resource的集合。
  • 关联表Role_Resouce表示了一个Role可以访问哪些资源,为一对多映射。
  • 关联表User_Role表示了一个User拥有哪些角色,从而有权访问对应的资源。
  • 权限控制时,只验证这个用户是否具有对应的角色。

通过需求可知,需要控制的资源实际为两类,API以及数据。故将资源表拆分为Operation_Resouce以及Data_Resource, 分别在不同的流程中使用进行验证。

接口访问控制

用户鉴权功能通过拦截器和切面实现。Spring Security提供了AbstractSecurityInterceptor基类来拦截所有的请求。若一个请求携带了有效的JWT令牌,则会继续由此拦截器处理。可在此拦截器中判断用户是否有权限访问该接口。

将需要控制的接口注册为资源,并通过Ant Pattern唯一地标识一个接口。之后便可依托RBAC模式来实现接口访问控制,不再赘述。

数据访问控制

将需要控制的数据注册为资源,并依托RBAC模式进行控制;只需将数据实体的Classname和Identifier录入即可唯一标识一个实体。

基本的思路是:

  • 在数据持久层设置切面,拦截返回值(从数据库装载的数据实体)。
  • 对每个实体,解析出他的Identifier与Classname,并与资源表中已注册的数据资源比对。
  • 若为注册的资源,进一步从关联表中获取可访问此资源的角色集合。
  • 从上下文SecurityContext中获取当前访问API用户的Authority,判断用户是否拥有此角色。若无,则将其从返回结果中移除。

需要解决的问题是,在切面获取的返回结果是Object,如何暴露出我们所需要的Identifier与Classname?可以考虑让被控制的实体类都实现同一个接口,例如一个提供getId()方法的AuthorityExposed接口,在切面拦截后可以直接转型为AuthorityExposed的实例并调用getId()获取标识符。