lukazan

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

1、背景       

  相对于 Shiro,在 SSM 中整合 Spring Security 都是比较麻烦的操作,所以, SpringSecurity 虽然功能比 Shiro 强大,但是使用反而没有 Shiro 多( Shiro 虽然功能没有Spring Security 多,但是对于大部分项目而言, Shiro 也够用了)。自从有了 Spring Boot 之后, Spring Boot 对于 Spring Security 提供了自动化配置方案,可以使用更少的配置来使用 Spring Security。
因此,一般来说,常见的安全管理技术栈的组合是这样的:
• SSM + Shiro
• Spring Boot/Spring Cloud + Spring Security
 

2、权限管理中的相关概念

(1)主体 principal

使用系统的用户或设备或从其他系统远程登录的用户等等。简单说就是谁使用系统谁就是主体。

(2)认证 authentication

权限管理系统确认一个主体的身份,允许主体进入系统。简单说就是“主体”证明自己是谁。笼统的认为就是以前所做的登录操作。

(3)授权 authorization

将操作系统的“权力”“授予”“主体”,这样主体就具备了操作系统中特定功能的能力。所以简单来说,授权就是给用户分配权限。
 

3、依赖

<!-- SpringSecurity 对 Web 应用进行权限管理 -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>4.2.10.RELEASE</version>
</dependency>
<!-- SpringSecurity 配置 -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>4.2.10.RELEASE</version>
</dependency>
<!-- SpringSecurity 标签库 -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
    <version>4.2.10.RELEASE</version>
</dependency>

4、在web.xml配置SpringSecurity 控制权限的 Filter

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

5、创建WebAppSecurityConfig配置类

(1) 添加注解

  • @Configuration:表示当前类是一个配置类,
  • @EnableWebSecurity:启用Web环境下权限控制功能,
  • @EnableGlobalMethodSecurity(prePostEnabled = true):启用全局方法权限控制功能

(2) 自定义WebAppSecurityConfig配置类

  配置类要继承WebSecurityConfigurerAdapter父类,重写两个方法 configure(HttpSecurity security) 和 configure(AuthenticationManagerBuilder builder)

(3) 定制请求的授权规则方法:configure(HttpSecurity security)

  1. 对资源进行权限控制

security.authorizeRequests()                //对请求进行授权
.antMatchers("/layui/**", "/index.jsp")     //针对/layui/**", "/index.jsp路径设置访问要求
.permitAll()                                //可以无条件访问
.antMatchers("/level1/**").hasRole("学徒")
.antMatchers("/level2/**").hasAuthority("大师")
.antMatchers("/level3/**").hasRole("宗师")
.anyRequest()                               //其他未设置的全部请求
.authenticated();                           //需要认证
  1. 开启自动配置的登录功能

security
.csrf().disable()                           // 禁用防跨站请求伪造功能
.formLogin().loginPage("/index.jsp")        //指定登录页面(如果没有指定会访问SpringSecurity自带的登陆页面)
.loginProcessingUrl("/do/login.html")       // loginProcessingUrl()方法指定了登录地址, 就会覆盖 loginPage()方法中设置的默认值/index.jsp POST
.permitAll()                                //登陆地址本身也需要permitAll()放行
.usernameParameter("loginAcct")             //自定义登陆账号的请求参数名
.passwordParameter("userPswd")              //自定义登陆密码的请求参数名
.defaultSuccessUrl("/main.html");           //设置登陆成功后前往的地址
      页面成功跳转有两种方式
    • defaultSuccessUrl       重定向方式
    • successForwardUrl     转发方式
  1. 处理注销登陆

//处理注销登陆
security.logout().logoutUrl("/do/logout.html")      //自定义处理退出请求的url,自己不需要在controller实现
        .logoutSuccessUrl("/index.jsp");            //退出成功后前往的页面
  1. 指定异常处理器

security.exceptionHandling()                        //指定异常处理器
.accessDeniedPage("/to/no/auth/page.html") //前往被拒绝时前往的页面,方法得自己去实现
.accessDeniedHandler(new AccessDeniedHandler() {    //或通过匿名内部类实现访问拒绝之后的处理
     @Override
     public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
          request.setAttribute("exception", new Exception(CrowdConstant.MESSAGE_ACCESS_DENIED));
          request.getRequestDispatcher("/WEB-INF/system-error.jsp").forward(request, response);
     }
});
  1. 记住我

JdbcTokenRepositoryImpl repository = new JdbcTokenRepositoryImpl();
repository.setDataSource(dataSource);
security.rememberMe().tokenRepository(repository);
 

(4) 定义认证规则方法:configure(AuthenticationManagerBuilder builder)

  1. 以内存的方式登陆

builder.inMemoryAuthentication()
.withUser("tom").password("123123").roles("ADMIN") //设置账号、密码、角色
.and()
.withUser("jerry").password("456456").authorities("SAVE","EDIT"); //设置另一个账号密码、权限
  1. 以JDBCTemplate的方式登陆

  1. 实际项目中的使用流程,基于数据库的认证

    1. 考虑到User对象中仅仅包含账号和密码,为了能够获取到原始的 Admin对象,专门创建这个类对 User 类进行扩展。声明SecurityAdmin类继承User类,重写父类构造器。
    2. 自定义CrowdUserDetailsService类,实现UserDetailsService接口,实现loadUserByUsername方法,参数为username,返回值为UserDetails。
    3. 在方法体内,根据username在数据库查询用户、角色和权限信息,将数据封装进自定义类SecurityAdmin返回。
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    // 1.根据账号名称查询Admin对象
    Admin admin = adminService.getAdminByLoginAcct(username);
    // 2.获取adminId
    Integer adminId = admin.getId();
    // 3.根据adminId查询角色信息
    List<Role> assignedRoleList = roleService.getAssignedRoleByAdminId(adminId);
    // 4.根据adminId查询权限信息
    List<String> authNameList = authService.getAssignedAuthNameByAdminId(adminId);
    // 5.创建集合对象用来存储GrantedAuthority
    List<GrantedAuthority> authorities = new ArrayList<>();
    // 6.遍历assignedRoleList存入角色信息
    for (Role role : assignedRoleList) {
        // 注意:不要忘了加前缀!
        String roleName = "ROLE_" + role.getName();
        SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(roleName);
        authorities.add(simpleGrantedAuthority);
    }
    // 7.遍历authNameList存入权限信息
    for (String authName : authNameList) {
        SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(authName);
        authorities.add(simpleGrantedAuthority);
    }
    // 8.封装SecurityAdmin对象
    SecurityAdmin securityAdmin = new SecurityAdmin(admin, authorities);
    return securityAdmin;
}

        4. 在configure(AuthenticationManagerBuilder builder)方法内

builder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);//passwordEncoder=new BCryptPasswordEncoder(); 盐值加密

6、权限控制

  1. Controller方法的权限控制,在方法上添加以下注解

    • @PreAuthorize(value="hasRole('PM - 项目经理')"):表示执行此方法需要该角色,先判断后执行
    • @PostAuthorize:先调用该接口,获得结果后在去判断该用户是否有权限
    • @PreFilter:对集合类型的参数或返回值进行过滤,Spring Security将移除使对应表达式的结果为false的元素。
    • @PostFilter:@PostFilter注解的作用:如果控制器方法的return返回值是一个集合,此注解可以对return的这个集合进行过滤输出;
    • @Secured注解: 在用户向浏览器发送一个请求时会去访问控制器中的方法,然后在访问此控制器中的方法之前会先去UserDetailsService用户细节实现类的实现方法中return的User对象查看是否具有@Secured注解中指定的角色,如果有指定的角色,那么系统允许用户访问此控制器方法,否则,系统不允许访问此控制器方法;注意在使用@Secured设置角色名字的时候,角色名的前面一定要加上ROLE_前缀;
  2. 使用全局配置控制,参考总结第5点。

  3. 页面元素权限控制

    • 页面上导入标签库<%@ taglib uri="http://www.springframework.org/security/tags" prefix="security" %>
    • 使用 security:authorize 标签
<security:authorize access="hasRole('经理')">
    <a href="assign/to/assign/role/page/${admin.id }.html" class="btn btn-success btn-xs">
        <i class=" glyphicon glyphicon-check"></i>
    </a>
</security:authorize>
posted on 2021-04-18 22:11  lukazan  阅读(151)  评论(0编辑  收藏  举报