用 Security 实现 RBAC 权限管理

什么是RBAC

RBAC是Role Based Access Control的缩写,是基于角色的访问控制。一般都是分为用(user),角色(role),权限(permission)三个实体,角色(role)和权限(permission)是多对多的关系,用户(user)和角色(role)也是多对多的关系。用户(user)和权限(permission)之间没有直接的关系,都是通过角色作为代理,才能获取到用户(user)拥有的权限。一般情况下,使用5张表就够了,3个实体表,2个关系表。具体的sql清参照项目示例。

  • Spring Security的会话机制是基于session的,做集群时对会话会产生影响。我们在这里使用Spring Session做分布式Session的管理。

SpringSecurity过滤器链

  • WebAsyncManagerIntegrationFilter: 将 Security 上下文与 Spring Web 中用于处理异步请求映射的 WebAsyncManager 进行集成
  • SecurityContextPersistenceFilter: 主要是使用SecurityContextRepository在session中保存或更新一个SecurityContext,并将SecurityContext给以后的过滤器使用,来为后续filter建立所需的上下文。SecurityContext中存储了当前用户的认证以及权限信息
  • HeaderWriterFilter: 用于将头信息加入响应中
  • CsrfFilter: csrf又称跨域请求伪造,SpringSecurity会对所有post请求验证是否包含系统生成的csrf的token信息,如果不包含,则报错。起到防止csrf攻击的效果
  • LogoutFilter: 匹配URL为/logout的请求,实现用户退出,清除认证信息
  • UsernamePasswordAuthenticationFilter: 认证操作全靠这个过滤器,默认匹配URL为/login且必须为POST请求。
  • DefaultLoginPageGeneratingFilter: 如果没有配置登录页面,那系统初始化时就会配置这个过滤器,并且用于在需要进行登录时生成一个登录表单页面。
  • BasicAuthenticationFilter: 此过滤器会自动解析HTTP请求中头部名字为Authentication,且以Basic开头的头信息。
  • RequestCacheAwareFilter: 通过HttpSessionRequestCache内部维护了一个RequestCache,用于缓存HttpServletRequest
  • SecurityContextHolderAwareRequestFilter: 主要是包装请求对象request
  • AnonymousAuthenticationFilter:检测 SecurityContextHolder 中是否存在 Authentication 对象,当SecurityContextHolder中认证信息为空,则会创建一个匿名用户存入到SecurityContextHolder中。spring security为了兼容未登录的访问,也走了一套认证流程,只不过是一个匿名的身份。
  • SessionManagementFilter: SecurityContextRepository限制同一用户开启多个会话的数量
  • ExceptionTranslationFilter: 处理 AccessDeniedException 和 AuthenticationException 异常
  • FilterSecurityInterceptor: 获取所配置资源访问的授权信息,根据SecurityContextHolder中存储的用户信息来决定其是否有权限。可以看做过滤器链的出口。

以下是认证部分的流程需要经过的过滤器 :
image.png

我们自己的登录请求
image.png

如果认证通过了,存到redisimage.png

登陆完了还要校验认证,认证的时候我们就可以去redis里面查
image.png

授权

经过了认证操作,认证这个用户名和密码正确。
此时还要判断当前用户有没有访问该连接的权限。
授权模型要明确两个要素:系统配置信息和用户权限信息。

  • 系统配置信息: 记录每个URL连接和每一个连接需要的权限
  • 用户权限信息:记录某个用户拥有的权限。

总结起来就是:

  1. 平台设置好角色的权限字符或者菜单的权限标识
  2. 接口添加权限注解
  3. 前端按钮等添加注解

权限有两种添加的方式:

  1. // 属于user角色,相当于user这个角色可以访问该注解修饰的方法
  2. @PreAuthorize(hasRole = "user")
  3. // 符合system:user:list权限要求,相当于菜单标识加了这个字段的才能访问
  4. @PreAuthorize(hasPermi = "system:user:list")

前端权限标识

  1. <el-button
  2. size="mini"
  3. type="text"
  4. icon="el-icon-delete"
  5. @click="handleDelete(scope.row)"
  6. v-hasRole="['admin', 'diviceManage']" //角色权限标识
  7. //v-hasPermi="['terminal:para:edit']" //这个注解表示菜单的字段权限标识
  8. >删除</el-button>

如果不需要这么细的颗粒度,只用角色权限就可以了。