概念:

  • 基于Spring AOP 和Servlet过滤器的安全框架.它提供全面的安全性解决方案,同时在Web请求级和方法调用级处理身份确认和授权

    核心功能:

    • 认证(你是谁,用户/设备,系统)
    • 验证(你能干什么,也叫权限控制/授权,允许执行的操作)
    • 攻击防护(防止伪造身份)

      Spring Security原理技术

    • Filter,Servlet,Spring DI,Spring AOP

      与Shiro相同点:

    • 认证功能
    • 授权功能
    • 加密功能
    • 会话功能
    • 缓存功能
    • remeberMe功能
不同点:
  • Spring Security基于Spring开发,项目中如果使用Spring作为基础,配合Spring security做权限更加方便.而shiro需要和Spring进行整合
  • Spring Security功能比Shiro更加丰富,例如:安全防护方面
  • Spring Security社区资源相对比Shiro更加丰富
  • 如果使用的是Springboot,springcloud的话,三者可以无缝集成
    缺点:
    • shiro的配置和使用比较简单,Spring Security上手复杂
    • shiro依赖性低,不需要任何框架和容器,可以独立运行,而Spring Security依赖Spring容器

关闭验证功能:

  • 在application.properties中配置security.basic.enabled=false,在5.x版本之后提示过时需在启动器中添加标签(@SpringBootApplication(exclude=SecurityAutoConfiguration.class))

    自定义用户名和密码

  • 在配置文件application.properties文件中添加:

     spring.security.user.name = admin
     spring.securitty.user.password = 1234

    编码思路:

  • 我们要在内存中初始化我们的认证信息的话,那么需要是重写WebSecurityConfigurerAdapter类中的configure方法:

  • configure(AuthenticationManagerBuilder auth)*

  • 然后通过auth对象的inMemoryAuthentication()方法指定认证信息:
    auth.inMemoryAuthentication().withUser("admin").password("1234");

说明:
  • @Configuration:注解这是一个配置类
  • @EnableWebSecurity:注解开启Spring Security 的功能
  • WebSecurityConfigurerAdapter:继承WebSecurityConfigurerAdapter,并重写它的方法来设置一些web安全的细节
  • configure(AuthenticationManagerBuilder auth):在内存中创建用户admin/密码1234

Bcrypt

  • bcrypt是一种跨平台的文件加密工具,bcrypt使用的 布鲁斯施内尔在1993年发布的Blowfish加密算法.由它加密的文件可在所有支持的操作系统和处理器上进行转移,它的口令必须是8至56个字符,并将在内部被转化为448位的密钥

    密码加密:

    @EnableWebSecurity//启用  Spring Security
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
      /*
      * 通过复写configure方法,进行用户创建
      * */
    
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws                  Exception{
    
          /*
          *基于内存的方式,构健两个用户账号:admin/1234
          * 在5.0后 会有多种密码加密方式,必须指定加密方式,否则
          * 会报错 these is no passwordEncoder mapper for the id 'null'
          * */
          auth.inMemoryAuthentication()
                  .passwordEncoder(new BCryptPasswordEncoder())
                  .withUser("admin")
                  .password(new BCryptPasswordEncoder().encode("1234"))
                  .roles();
      }

    通过@Bean注入指定PasswordEncoder

    @Bean
    public PasswordEncoder passwordEncoder(){
      return new BCryptPasswordEncoder();
    }

    加入这个之后,需要修改configure的密码加密

    @Configuration
    @EnableWebSecurity//启用  Spring Security
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
      /*
      * 通过复写configure方法,进行用户创建
      * */
    
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
          /*
          *基于内存的方式,构健两个用户账号:admin/1234
          * 在5.0后 会有多种密码加密方式,必须指定加密方式,否则
          * 会报错 these is no passwordEncoder mapper for the id 'null'
          * */
          auth.inMemoryAuthentication()
                  .withUser("admin")
                  .password(passwordEncoder().encode("1234"))
                  .roles();
      }
    
      @Bean
      public PasswordEncoder passwordEncoder(){
          return new BCryptPasswordEncoder();
      }
    }

    如何给指定的用户指定角色:

    auth.inMemoryAuthentication()
                  .withUser("admin")
                  .password(passwordEncoder().encode("1234"))
                  .roles("beijiangAdmin","shanghaiAdmin");

    如何开启方法级别安全控制

  • 在已经添加了@Configuration注解的类上再添加
    @EnableGlobalMethodSecurity注解即可

    如何配置方法级别的权限控制

  • 使用注解@PreAuthorize("hasAnyRole('admin')")即可指定访问级别的角色

    自定义登录页面:

  • 添加thymeleaf依赖

  • 配置Spring Security 的登录页面路径

    • 在WebSecurityConfig复写config(HttpSecurity http)
      @Override
      protected void configure(HttpSecurity http) throws Exception {
          http.formLogin().loginPage("/login");//指定表单登录方式
      }
      

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.formLogin().loginPage("/login")//指定表单登录方式
            .and()
            .authorizeRequests() //所有页面需要认证
            .antMatchers("/login").permitAll() //允许所有人访问登录页面
            .anyRequest().authenticated() //所有的请求需要在登录之后才能访问

    ;
}
编写登录页面的html文件
用户名密码错误,要不去首页看看?
您已经退出
<form method="post" th&#58;action="&#64;&#123;&#47;login&#125;">
<label> 用户名: </label>
<label> 密码: </label>
</form>
编写主页html

欢迎使用 Spring Security!

点击这里打个招呼吧

<p><a th:href="@{/hello/helloAdmin}">admin page</a></p>
<p><a th:href="@{/hello/helloUser}">user page</a></p>
``` ###退出登录: - 退出登录只需要在index.html使用form表单posi方式,提交/logout请求即可 ``` <form method="post" th&#58;action="&#64;&#123;&#47;logout&#125;"> </form> ``` #####修改页面不生效: spring.thymeleaf.cache=false ###Filter - 如果要对Web资源进行保护,最好的办法莫过于Filter,要想方法调用进行保护,最好的办法莫过于AOP.Spring Security 对Web资源的保护,就是靠Filter保护 - Spring Security提供的Filter不少,有十多个,过滤器顺序从上到下 - ChannelProcessingFilter(访问协议控制过滤器) - 如果你访问的channel错了,那首先就会在channel之间进行跳转,如http变为https - SecurityContextPersistenceFilter(SecurityContext持久化过滤器) - 用来创建一个SecurityContext并存储在SecurityHolder中,因为后续filter需要用SecurityContext存储的认证相关信息,所以需要在请求一开始就要把这些信息设置好,这样也能使在认证过程中对SecurityContext的任何修改都可以保存下来,并在请求结束后存储在HttpSession中(以在下次请求中使用) - ConcurrentSessionFilter(并发控制器过滤器) - 主要是判断session是否过期以及更新最新访问时间 - HeaderWriterFilter(请求头写入过滤器) - 往往请求的Header中添加相应的信息 - CsrfFilter(CSRF过滤器) - 为了防止跨站提交攻击 - LogoutFilter(退出过滤器) - 退出当前登录的账号 - X509AuthenticationFilter(X509认证过滤器) - 基于X509证书的认证过滤器 - AbstractPreAuthenticatedProcessingFilter - 处理form登陆的过滤器,与form登陆有关的所有操作都是在此进行的.这个请求应该是用户使用form登陆后的提交地址 - CasAuthenticationFilter(CAS认证过滤器) - 基于CAS的认证过滤器 - UsernamePasswordAuthenticationFilter(用户名密码认证过滤器) - 基于用户名和密码的认证过滤器 - BasicAuthenticationFilter(basic认证过滤器) - 此过滤器用于进行basic验证,功能与AuthenticationProcessingFilter类似,只是Basic验证方式相比较而言用的不是太多,默认对密码进行base64加密 - SecurityContextHolderAwareRequestFilt - 此过滤器用来包装客户的请求,通过查看其源码可以发现其doFilter方法中会创建一个包装类型SecurityContextHolderAwareRequestWrapper对SecurityRequest对象进行包装,主要实现了 servlet api 的一些接口方法isUserInRole, getRemoteUser , 为后续程序提供一些额外的数据.即可以从request对象中获取用户信息 - JaasApiIntegrationFilter - 如果SecurityContextHolder中拥有的Authentication是一个JaasAuthenticationToken,那么该Filter将使用包含在JassAuthenticationToken中的Subject继续执行FilterChain - RememberMeAuthenticationFilter(记住我认证过滤器) - 当用户没有登录而直接访问资源时,从cookie里找出用户的信息,如果Spring Security能够识别出用户提供的remember me cookie,用户将不必填写用户名和密码,而是直接登录进行系统.它先分析SecurityContext里有没有Authentication对象,如果有,则不做任何操作,直接跳到下一个过滤器.如果没有,则检查request里有没有包含remember-me的cookie信息,如果有,则解析出cookie里的验证信息,判断是否有权限 - AnonymousAuthenticationFilter(匿名认证过滤器) - 用于支持Spring Security的匿名访问,适用于一些公共资源希望所有人都可以看到,对于匿名访问的用户,SpringSecurity支持为其建立一个匿名的AnonymousAuthenticationToken存放在SecurityContextHolder中,这就是所谓的匿名认证.这样在以后进行权限认证或做其他操作时,我们就不需要再判断SecurityContextHolder中持有的Authentication对象是否为null了,而直接把它当做一个正常的Authentication进行使用就OK了 - SessionManagementFilter - 根据认证的安全实体信息跟踪session,保证所有关联一个安全实体的session都能被跟踪到 - ExceptionTranslationFilter - 解决再处理一个请求时产生的指定异常 - FilterSecurityInterceptor - 简化授权和访问控制决定,委托一个AccessDecisionManager完成授权的判断 - SwitchUserFilter - SwitchUserFilter是用来做账号切换的 - 这些Filter并不直接处理用户的认证,也不直接处理用户的授权,而是把它们交给了认证管理器和决策管理器 - 对于这两个管理器,是不需要我们写代码的,Spring Security也提供了现成的类/这两个管理器自己也做事情,认证管理器把任务交给了Provider,而决策管理器则把任务交给了Voter
  • Spring Security提供了多个Provider的实现类,如果我们想用数据库来存储用户的认证数据,那么我们就选择DaoAuthenticationProvider.对于Voter,我们一般选择RoleVoter就够用了,它会根据我们配置文件中的设置来决定是否允许某一个用户访问制定的Web资源.而DaoAuthenticationProvier也是不直接操作数据库的,他把任务委托给了UserDetailService

    自定义Filter

Spring Security 的HttpSecurity为此提供了三个常用方法来配置

  • addFilterBefore(Filter filter,Class<? extends Filter> beforeFilter)
    • 在beforeFilter之前添加filter
  • addFilterAfter(Filter filter,Class<? extends Filter> afterFilter)
    • 在afterFilter之后添加filter
  • addFilterAt(Filter filter,Class<? extends Filter> atFilter)
    • 在atFilter相同位置添加filter , 此filter不覆盖filter

      设置白名

  • .antMatchers("/login").permi'tAll()

单个文件配置白名单

http.authorizeRequests() //定义哪些URL需要被保护,哪些不需要被保护
     .antMatchers("/login").permitAll() //允许所有人访问登录页面

访问目录下所有文件

.antMatchers("/test/**","/test1/**").permitAll()

获取登录信息的方式

  • 主要是通过SecurityContextHolder获取到上下文SecurityContext,通过SecurityContext获取到权限信息Authentication,最后通过Authentication获取到当前的登录信息:
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();