一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分,这两点也是 Spring Security 重要核心功能。

    • 用户认证指的是:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。通俗点说就是系统认为用户是否能登录。
    • 用户授权指的是:验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。通俗点讲就是系统判断用户是否有权限去做某些事情。

    **
    常用的安全管理框架:

    • Shiro
    • Spring Security

    Spring Security 本质上是一个过滤器链:

    • FilterSecurityInterceptor:方法级的权限过滤器,基本位于过滤器链的最底层
    • ExceptionTranslationFilter:异常过滤器,用来处理在认证授权过程中抛出的异常
    • UsernamePasswordAuthenticationFilter:对 /login 的请求进行拦截,校验表单中的用户名、密码

    过滤器自动加载原理:使用 SpringSecurity 配置过滤器:DelegatingFilterProxy

    如何自定义登录验证逻辑:

    1. 创建类继承 UsernamePasswordAuthenticationFilter,重写三个方法
    2. 创建类实现 UserDetailsService,编写查询数据过程,返回 User 对象(该User对象由框框架提供)

    PasswordEncoder 接口:对密码进行加密

    • BCryptPasswordEncoder:Spring Security官方推荐的密码解析器,平时使用较多
    • BCryptPasswordEncoder 是对 bycrypt 强散列方法的具体实现,是基于 Hash 算法实现的单项加密,可以通过 strength 控制加密强度,默认 10

    设置登录的用户名和密码:三种方式
    1、通过配置文件 application.properties

    1. spring.security.user.name=
    2. spring.security.user.password=

    2、通过配置类:继承 WebSecurityConfigurerAdapter 类,重写 configure 方法

    1. @Configuration
    2. public class SecurityConfig extends WebSecurityConfigurerAdapter {
    3. @Override
    4. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    5. BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
    6. String password = encoder.encode("password");
    7. auth.inMemoryAuthentication().withUser("username").password(password).roles("admin");
    8. }
    9. // 配置密码解码器
    10. @Bean
    11. public PasswordEncoder passwordEncoder() {
    12. return new BCryptPasswordEncoder();
    13. }
    14. }

    3、自定义实现类:

    1. 创建配置类,设置使用哪个 userDetailsService 实现类
    2. 编写实现类,返回 User 对象,User 对象有密码和操作权限 ```java / 创建配置类 / @Configuration public class MySecurityConfig extends WebSecurityConfigurerAdapter {

      @Autowired private UserDetailsService userDetailsService;

      @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception {

      1. auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());

      }

      @Bean public PasswordEncoder passwordEncoder() {

      1. return new BCryptPasswordEncoder();

      } }

    / 实现 UserDetailsService / @Service(“userDetailsService”) public class MyUserDetailsService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 权限列表 List auths = AuthorityUtils.commaSeparatedStringToAuthorityList(“role”); return new User(username, new BCryptPasswordEncoder().encode(“password”), auths); } }

    1. 从数据库中获取登录数据:
    2. - 引入依赖:mybatis-plus-startermysqllombok
    3. - 使用 mybatis-plus:接口直接继承 BaseMapper<T>
    4. - QueryWrapper<User> 条件构造器
    5. - 启动类上 @MapperScan
    6. 自定义设置登录页面:在配置类中进行配置
    7. ```java
    8. @Configuration
    9. public class MySecurityConfig extends WebSecurityConfigurerAdapter {
    10. @Override
    11. protected void configure(HttpSecurity http) throws Exception {
    12. http.formLogin() // 自定义登录页面
    13. .loginPage("/login.html") //登录页面设置
    14. .loginProcessingUrl("/user/login") //登录访问路径
    15. .defaultSuccessUrl("/index.html").permitAll() //登录成功跳转路径
    16. .and().authorizeRequests()
    17. .antMatchers("/user/login").permitAll() //设置那些路径无需登录,可以直接访问
    18. .anyRequest().authenticated() //其他页面必须认证
    19. .and().csrf().disable(); //关闭csrf防护
    20. }
    21. }

    基于角色或权限进行访问控制:
    前提:在配置类中返回 User 对象时,必须设置访问权限

    1. List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");

    1、hasAuthority(),当前主体有指定的权限,返回 true,否则 false

    1. .and().authorizeRequests()
    2. .antMatchers("/user/login").permitAll() //设置那些路径无需登录,可以直接访问
    3. .antMatchers("/admin").hasAuthority("admin") // 设置该页面只有admin用户才能访问
    4. .anyRequest().authenticated() //其他页面必须认证

    没有访问权限:type = Forbidden,status = 403
    2、hasAnyAuthority():如果当前的主体有任何提供的角色(给定一个逗号分隔的字符串列表)的话,返回 true

    1. .antMatchers("/admin").hasAnyAuthority("admin,manager")

    3、hasRole():如果用户具备给定角色就允许访问,否则出现 403;如果当前主体具有指定的角色,则返回 true
    4、hasAnyRole():表示用户具备任何一个条件都可以访问
    区别:在设置角色权限时,前必须加 ROLE 前缀,而在进行匹配时,则必须省略 ROLE 前缀
    image.png

    自定义 403 无权限访问页面:

    1. 修改访问配置类:http.exceptionHandling().accessDeniedPage(“/unauth”);
    2. 添加对应的控制器:@GetMapping(“/unauth”)

    @Secured:判断是否具有某角色

    • 这里匹配的字符串需要添加前缀 “ROLE_”
    • 该注解用在控制器方法上,用于标注该方法的访问权限:@Secured({“ROLE_normal”,”ROLE_admin”})

    注解使用:

    1. 使用注解必须现在启动类上开启注解功能,@EnableGlobalMethodSecurity(securedEnabled = true)
    2. 在 Controller 的方法上添加相应注解:@Secured({“ROLE_normal”,”ROLE_admin”})
    3. userDetailsService 设置用户角色:AuthorityUtils.commaSeparatedStringToAuthorityList(“admin , ROLE_sale”);

    @PreAuthorize:注解适合进入方法前的权限验证,@PreAuthorize 可以将登录用户的roles/permissions 参数传到方法中 @PreAuthorize(“hasAnyAuthority(‘menu : system’)”),方法之前的校验

    1. 开启注解功能:@EnableGlobalMethodSecurity(securedEnabled=true , prePostEnabled=true)
    2. 在 Controller 的方法上添加相应注解:@PreAuthorize(“hasAnyAuthority(‘admins’)”)

    @PostAuthorize:在方法执行后再进行权限验证,适合验证带有返回值的权限 @PostAuthorize(“hasAnyAuthority(‘menu : system’)”),方法之后的校验

    1. 开启注解功能:@EnableGlobalMethodSecurity(securedEnabled=true , prePostEnabled=true)

    @PostFilter:权限验证之后对数据进行过滤,留下用户名是 admin1 的数据,表达式中的 filterObject 引用的是方法返回值 List 中的某一个元素,方法返回数据进行过滤
    image.png

    @PreFilter:进入控制器之前对数据进行过滤,传入方法数据进行过滤
    image.png