用 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-buttonsize="mini"type="text"icon="el-icon-delete"@click="handleDelete(scope.row)"v-hasRole="['admin', 'diviceManage']" //角色权限标识//v-hasPermi="['terminal:para:edit']" //这个注解表示菜单的字段权限标识>删除</el-button>
如果不需要这么细的颗粒度,只用角色权限就可以了。
