这里写目录标题

    • 资源,权限,角色,用户
      • RBAC
    • 数据表设计
    • SpringSecurity配合RBAC补全权限验证
      • 背景:
      • 基本实现步骤
      • 实现记住我功能
      • 实现SpringSecurity版验证码登录
    • 权限管理的使用

资源,权限,角色,用户

权限管理是为了管理用户行为,保护目标系统的安全,权限 = 权利 + 限制

1 资源的定义:

资源就是系统需要保护起来的功能,有url,handler方法,service方法,页面元素…

2 权限的定义
因为一个功能的完成可能涉及到成千上万的资源,如果以资源做为权限分配的基本单位那就很不痛快,我们将n个相关操作的资源封装为一个"权限",再将这个权限分配给有需要的人,这可以看出是对权限操作的简化,资源可以看成一个handler
项目权限管理大纲(RBAC+SpringSecurity)-编程知识网

项目权限管理大纲(RBAC+SpringSecurity)-编程知识网

3 角色
将资源包装为权限是对资源管理的简化,而用户的数量同样不是小数目,一个个的管理是不现实的,我们可以将用户划分不同的角色,即角色就是用户的分类分组。资源封装为权限,权限分配给角色,再给用户指定角色,这也是RABC模型的大纲

4 用户
具有系统使用权限和登录账号的一类人

权限 => 资源
数据库层面n:n,java层面1:n(每个权限类封装一个资源列表,但是资源类中不需要考虑权限这个属性,即不需要知道这个资源对应那些权限)

角色 => 权限
数据库层面n:n,java层面1:n(角色类封装一个权限列表,但权限类不需要考虑角色这个属性,即不需要考虑权限对应对应那些角色)

用户 => 角色
数据库层面n:n,java层面n:n(用户类封装一个角色列表来表示这个用户所具有的角色,也可以查看这个这个角色下的所有用户)

RBAC

(Role-Base-Access Controller) 用户通过角色获得相关的权限,一个用户可以有多种角色,一个角色对于多种权限,一个权限代表一个功能

1 互斥角色:不能同时给用户分配到一起的角色,如会计师和审计师角色
2 约束角色:角色的访问权限和旗下的用户数量是受限的,一个用户拥有的角色数量也受限
先决条件就是,角色拥有A权限的条件是拥有B权限,如用户升到vip6级的条件是要达到vip5级
3 动态分离级别:马云在阿里巴巴内部激活创始人身份,在企业论坛上激活演讲嘉宾角色

数据表设计

五张基础表
项目权限管理大纲(RBAC+SpringSecurity)-编程知识网
用户(admin)表
项目权限管理大纲(RBAC+SpringSecurity)-编程知识网
角色表role
项目权限管理大纲(RBAC+SpringSecurity)-编程知识网
权限表
name命名规范如user:save,表示具有保存角色的权限
项目权限管理大纲(RBAC+SpringSecurity)-编程知识网
用户-角色表
项目权限管理大纲(RBAC+SpringSecurity)-编程知识网
角色-权限表
项目权限管理大纲(RBAC+SpringSecurity)-编程知识网

SpringSecurity配合RBAC补全权限验证

背景:

使用SpringSecurity前,我们已经将auth分配给了role,将role分配给了admin,形参了一条完整的admin-role-auth链,之前的拦截器代码或配置的放行资源信息都注释掉决不能共存
项目权限管理大纲(RBAC+SpringSecurity)-编程知识网

基本实现步骤

1 在springsecurity的配置类中配置放行的资源 ,通过继承WebSecurityConfigurerAdapter然后重写configure(HttpSecurity security),这里面配置以下内容

  • 定制放行资源,有登录的首页,退出登录,全部的静态资源img/jquery/css…,除了这些放行到的其它都要进行验证
  • 开启form表单登录功能、指定登录的页面、指定处理登录请求的handler、指定登录成功后的跳转页面, 指定from表单的登录框和密码框的name值。注意:此时已经没有真正的登录处理handler方法了,有的只是在configure中进行的登录验证。
  • 指定资源访问权限,例如admin主页面只有经理采用访问权限,也可以采用在handler方法上加注解的形式控制
  • 开发阶段禁用csrf
  • 搭建springsecurity独立的异常处理体系
  • 开启退出登录功能、处理方法、退出后的跳转页面
  • 配置7天内记住我功能①绑定数据源②绑定登录handler③

2 定义SecurityAdmin,这个类作为SpringSecurity权限处理后的封装结果集来使用,来将admin对象和该对象所具有的权限(角色+权限)绑定,绑定admin对象的username、password、authorites这三类信息,用户认证成功信息会被SecurityAdmin封装到principle变量并被存储在xxx类,调用父类的构造器验证登录用户的合法性并擦除对象密码,进一步提高安全性,这样方便前台打印(前端代码中不要忘记引入springsecurity标签库)。

3 重写configure(AuthenticationManagerBuilder builder)

  • 可以在临时在内存中存储登录信息测试代码,项目初始化测试和部署到服务器上的时候必须考虑这一点。 易错点:如果前端引入了springsecurity标签库并且用principle进行了前端参数回显会产生报错,因为principle这个参数是从secuityAdmin中获取到的,内存帮登录信息不涉及securityAdmin
  • 测试完毕将内存登录改装成数据库版登录测试
    ①定义UserDetailsService实现类,步骤如下:
    注入adminService/RoleService/AuthService这三个对象 => 接收username(参数)=> 获取对应的admin =>取出adminId => 根据adminId获取该角色的权限集合和角色集合 => 遍历权限和角色集合(角色集合名前面加ROLE_来区分角色与权限)将其封装为SimpleGrantedAuthority加入已创建的权限列表=>将获取的admin对象及它的authorities封装为SecurityAdmin对象并返回
    项目权限管理大纲(RBAC+SpringSecurity)-编程知识网

密码加密
1) 装配BCryptPasswordEncoder组件,configure方法中使用该组件进行密码加密
2)淘汰之前写的md5加密方法,并在之前写的保存用户方法中更换掉md5加密方法

4 前端
①form填写handler路径
②前端重点参数

${SPRING_SECURITY_LAST_EXCEPTION } #springsecurity前端错误信息提示,用于登录页面回显
<security:authentication property="principal.originalAdmin.userName"/> #公共区域侧边栏,显示已登录用户的名称信息

③注意,表单的用户名和密码文本框推荐自定义取name名,但要到配置类配置,否则springsecurity无法识别
④引入springsecurity标签库来表示已登录的信息

5 搭建异常处理体系
由下图可知springsecurity的异常是无法被springmvc的异常处理机制处理的,所以分为两部分,一部分是专门处理SpringMVC的dispacherServlet拦截的异常路径,另一部分是处理SpringSecurity拦截的
项目权限管理大纲(RBAC+SpringSecurity)-编程知识网
方法是在configure中搭建springsecurity独立的异常处理体系

总结:SpringSecurity中角色和权限近乎等价,也就是handler处理器所对应的资源除了和auth挂钩也可以和role挂钩,而且也可以将一份资源的访问权限设计为具有xxx角色或xxx权限,例如

.access("hasRole('经理') or hasAnyAuthority('user:get')") #具备经理或用户保存权限可以访问该资源的权限(管理admin信息的权限)

实现记住我功能

基本原理
项目权限管理大纲(RBAC+SpringSecurity)-编程知识网
具体实现方法
项目权限管理大纲(RBAC+SpringSecurity)-编程知识网
用户登录 => 第二步的登录过滤器验证 => 验证成功后相关组件生成Token => 该组件一方面将token写入浏览器的Cookie(cookie名就是前端checkbot的name)另一方面存入数据库中 => 用户下次登录时相关组件一方面查找浏览器cookie另一方面去数据库(persist_login)进行查找 =>对比两个值,相等则跳过springsecurity的登录验证,实现步骤如下

  • 建立数据表persistent_ogin
    项目权限管理大纲(RBAC+SpringSecurity)-编程知识网
  • configure端配置七天内记住我功能,绑定数据源操作组件persistentTokenRepository和用户登录验证组件userDetailsService,配置连接保存时长为七天
  • 前端配置记住我的cehckbox组件,name必须是固定的

结论

  • 数据库端生成
    项目权限管理大纲(RBAC+SpringSecurity)-编程知识网
  • 浏览器端也有相应的生成
    项目权限管理大纲(RBAC+SpringSecurity)-编程知识网
  • 再次登录主页发现不需要验证(我们的设计是将http://ip:port/projectName/admin/to/main/page.html作为主页,用户选择记住我就不需要以登录的形式进行springsecurity验证而是直接就能跳转。反正,用户没有实现记住我,直接进入主页会被springsecurity拦截,造成权限不够而被强制以跳转到登录页的形式进行springsecurity验证)

实现SpringSecurity版验证码登录

1 导入spring-social-config依赖
2 entity编写验证码实体类IamgeCode

private BufferedImage image; //验证码图片
private String code; //code验证码
private LocalDateTime expireTime; //过期时间 单位秒

3 componet的mvc下编写验证码生成处理器ValidateCodeController

  • createImageCode:工具方法,用于生成ImageCode类型的验证码信息
  • sessionStrategy属性:使用sessionStrategy将生成的验证码对象存储到Session中
  • createCode:将生成的验证码对象存储到Session中,并通过IO流将生成的图片输出到登录页面上,在前端页面通过该方法的访问路径加载生成的图片,并通过点击进行看不清刷新
<img src="code/image.html"/>

4 在springsecurity配置configure中放行生成的验证码handler路径资源的访问

5 编写过滤器ValidateCodeFilter进行登录时验证码的校验

  • 继承OncePerRequestFilter,重写doFilterInternal
  • doFilterInternal方法①如果拦截的页面是post请求的登录页面,那么就拦截下来有待进一步验证,其余静态资源全部放行②合法性验证:判断是否合法,不合法要返回相应的错误信息③验证通过后立即删除session中原先的验证码,防止影响下一次验证码校验
  • 将该过滤器注入到WebAppSecurityConfig(相当于将该过滤器组件加入到了springsecurity容器中)并在configure中将该过滤器的执行顺序配置到验证登录的过滤器前面
 security.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);

权限管理的使用

在WebAppSecurityConfig这个springsecurity配置类上使用注解

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled=true,jsr250Enabled=true)

在配置类的configure中配置资源权限

 .antMatchers("/admin/get/page.html").access("hasRole('经理') or hasAnyAuthority('user:get','user:save','user:delete','user:update')").antMatchers("/admin/remove/{adminId}/{pageNum}/{keyword}.html").access("hasRole('经理') or hasAuthority('user:delete')")

使用注解,案例如下

//管理员主页面,分页和查询功能在一块的@RequestMapping("/admin/get/page.html")@PreAuthorize("hasRole('经理') or hasAnyAuthority('user:get','user:save','user:delete','user:update')")public String getPageInfo(){.....}//删除管理员功能@PreAuthorize("hasRole('经理') or hasAuthority('user:delete')")@GetMapping("/admin/remove/{adminId}/{pageNum}/{keyword}.html")public String remove(){....}

经测试无效,原因是我们设置开启springsecurity注解是在WebAppSecurityConfig即springsecurity的ioc容器中配置的注解生效,但是controller是由springmvc的dispatcherServlet负责拦截的,所以最终controller逻辑注解被动态扫描到spring-mvc-web容器中,如果仅仅是在WebAppSecurityConfig开启注解生效是没有效果的,需要在spring-mvc-web也开启注解生效,配置内容如下,记得声明头部引用

 <security:global-method-security secured-annotations="enabled"pre-post-annotations="enabled" jsr250-annotations="enabled"/>