用 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,用于缓存HttpServletRequestSecurityContextHolderAwareRequestFilter
: 主要是包装请求对象requestAnonymousAuthenticationFilter
:检测 SecurityContextHolder 中是否存在 Authentication 对象,当SecurityContextHolder中认证信息为空,则会创建一个匿名用户存入到SecurityContextHolder中。spring security为了兼容未登录的访问,也走了一套认证流程,只不过是一个匿名的身份。SessionManagementFilter
: SecurityContextRepository限制同一用户开启多个会话的数量ExceptionTranslationFilter
: 处理 AccessDeniedException 和 AuthenticationException 异常FilterSecurityInterceptor
: 获取所配置资源访问的授权信息,根据SecurityContextHolder中存储的用户信息来决定其是否有权限。可以看做过滤器链的出口。
以下是认证部分的流程需要经过的过滤器 :
我们自己的登录请求
如果认证通过了,存到redis
登陆完了还要校验认证,认证的时候我们就可以去redis里面查
授权
经过了认证操作,认证这个用户名和密码正确。
此时还要判断当前用户有没有访问该连接的权限。
授权模型要明确两个要素:系统配置信息和用户权限信息。
- 系统配置信息: 记录每个URL连接和每一个连接需要的权限
- 用户权限信息:记录某个用户拥有的权限。
总结起来就是:
- 平台设置好角色的权限字符或者菜单的权限标识
- 接口添加权限注解
- 前端按钮等添加注解
权限有两种添加的方式:
// 属于user角色,相当于user这个角色可以访问该注解修饰的方法
@PreAuthorize(hasRole = "user")
// 符合system:user:list权限要求,相当于菜单标识加了这个字段的才能访问
@PreAuthorize(hasPermi = "system:user:list")
前端权限标识
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasRole="['admin', 'diviceManage']" //角色权限标识
//v-hasPermi="['terminal:para:edit']" //这个注解表示菜单的字段权限标识
>删除</el-button>
如果不需要这么细的颗粒度,只用角色权限就可以了。