初识SpringSecurity

前言:本系列文章主要讲述SpringSecurity在认证授权时的一系列过程,方便为各位小白答疑解惑,后续文章也会定时更新。谢谢大家的支持。

认证流程图(大概的基本流程)

在这里插入图片描述
默认情况下

  • 当前端发起请求时,经过一系列过滤器之后,被SpringSecurity中的过滤器进行拦截
  • 当拦截到该请求之后,会委派到身份验证管理其中进行认证,AuthenticationManager是一个接口,主要实现类为ProviderManager
  • 身份验证程序(AuthenticationProvider)实现了主要的认证逻辑,包括检查用户名,密码等
  • 认证完成之后,会将认证的结果依次返回,最终将认证的结果保存在SpringContext的上下文对象中

构建项目基础环境(jdk8)

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.10</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
  </dependency>
  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

搭建完成之后,输入http://localhost:8080/ 地址之后,可以看到一个默认的登录页面
在这里插入图片描述
idea控制台中会生成一个加密之后的密码字符串
在这里插入图片描述
此时表示环境已经搭建成功。用户名为user密码为控制台中生成的字符串(默认用户)

实现原理(过滤器链)DelegatingFilterProxy

当前端请求后端接口时,会首先经过一些原生的filter,在这些filter中会经过DelegatingFilterProxy委派到SpringSecurity所提供的的一系列过滤器链(SecurityFilterChain)
官方解释:Spring 提供了一个名为 DelegatingFilterProxy 的 Filter 实现,允许在 Servlet 容器的生命周期和 Spring 的 ApplicationContext 之间建立桥梁。Servlet容器允许通过使用自己的标准来注册 Filter 实例,但它不知道 Spring 定义的 Bean。你可以通过标准的Servlet容器机制来注册 DelegatingFilterProxy,但将所有工作委托给实现 Filter 的Spring Bean。
官网图片 (https://springdoc.cn/spring-security/servlet/architecture.html)
在这里插入图片描述

默认情况下会加载部分的过滤器

过滤器过滤器作用默认是否加载
ChannelProcessingFilter过滤请求协议 HTTP 、HTTPSNO
WebAsyncManagerIntegrationFilter将 WebAsyncManger 与 SpringSecurity 上下文进行集成YES
SecurityContextPersistenceFilter在处理请求之前,将安全信息加载到 SecurityContextHolder 中YES
HeaderWriterFilter处理头信息加入响应中YES
CorsFilter处理跨域问题NO
CsrfFilter处理 CSRF 攻击YES
LogoutFilter处理注销登录YES
OAuth2AuthorizationRequestRedirectFilter处理 OAuth2 认证重定向NO
Saml2WebSsoAuthenticationRequestFilter处理 SAML 认证NO
X509AuthenticationFilter处理 X509 认证NO
AbstractPreAuthenticatedProcessingFilter处理预认证问题NO
CasAuthenticationFilter处理 CAS 单点登录NO
OAuth2LoginAuthenticationFilter处理 OAuth2 认证NO
Saml2WebSsoAuthenticationFilter处理 SAML 认证NO
UsernamePasswordAuthenticationFilter处理表单登录YES
OpenIDAuthenticationFilter处理 OpenID 认证NO
DefaultLoginPageGeneratingFilter配置默认登录页面YES
DefaultLogoutPageGeneratingFilter配置默认注销页面YES
ConcurrentSessionFilter处理 Session 有效期NO
DigestAuthenticationFilter处理 HTTP 摘要认证NO
BearerTokenAuthenticationFilter处理 OAuth2 认证的 Access TokenNO
BasicAuthenticationFilter处理 HttpBasic 登录YES
RequestCacheAwareFilter处理请求缓存YES
SecurityContextHolder<br />AwareRequestFilter包装原始请求YES
JaasApiIntegrationFilter处理 JAAS 认证NO
RememberMeAuthenticationFilter处理 RememberMe 登录NO
AnonymousAuthenticationFilter配置匿名认证YES
OAuth2AuthorizationCodeGrantFilter处理OAuth2认证中授权码NO
SessionManagementFilter处理 session 并发问题YES
ExceptionTranslationFilter处理认证/授权中的异常YES
FilterSecurityInterceptor处理授权相关YES
SwitchUserFilter处理账户切换NO

SpringSecurity中的默认配置

每次启动项目之后,控制台中都会生成一次默认的随机密码字符串,包括默认的密码加密方式(PasswordEncoder)、默认的登录页面、认证、授权过滤器等。这些都是由SpringBoot来完成.

  • 首先讲解UserdetailService和PasswordEncoder
    UserdetailService
    在这里插入图片描述
    可以看到UserDetailService是一个接口,只有一个loadUserByUsername的方法,根据用户名获取一个UserDetails(后续说明) 对象,从内存中或者数据库中查询用户信息(默认是基于内存中查询,也就是登录也中输入的user用户名)。

说明:Userdetails:

在这里插入图片描述

  • Collection<? extends GrantedAuthority> getAuthorities();获取用户权限信息
  • String getPassword();获取密码
  • String getUsername();获取用户名
  • boolean isAccountNonExpired(); 账户是否不过期
  • boolean isAccountNonLocked();账户是否没被锁定
  • boolean isCredentialsNonExpired();凭据是否不过期
  • boolean isEnabled();是否开启
  • 从数据库或内存中获取的用户信息,将其封装为用UserDetails对象进行返回,工上层使用(后续在原码中可以看到)

PasswordEncoder
在这里插入图片描述

  • String encode(CharSequence rawPassword);对密码进行加密返回

  • boolean matches(CharSequence rawPassword, String encodedPassword);对密码进行比较,即,将用户传入的密码进行加密之后与数据库或内存中加密之后的密码进行匹配,匹配成功返回true

  • default boolean upgradeEncoding(String encodedPassword)对密码进行升级(多种加密方式)

  • 默认情况下,在项目启动时,会对加密对象(PasswordEncoder)进行多种加密方式的配置。

  • PasswordEncoderFactories类中的源码如下,可以看到有多种的加密方式,

  • return new DelegatingPasswordEncoder(encodingId, encoders);默认指定为BCryptPasswordEncoder,一种单向的hash加密方式
    在这里插入图片描述
    加密方式:

  • BCryptPasswordEncoder

    BCryptPasswordEncoder 使用 bcrypt 算法对密码进行加密,为了提高密码的安全性,bcrypt算法故意降低运行速度,以增强密码破解的难度。同时 BCryptP asswordEncoder “为自己带盐”开发者不需要额外维护一个“盐” 字段,使用 BCryptPasswordEncoder 加密后的字符串就已经“带盐”了,即使相同的明文每次生成的加密字符串都不相同。

  • Argon2PasswordEncoder

    Argon2PasswordEncoder 使用 Argon2 算法对密码进行加密,Argon2 曾在 Password Hashing Competition 竞赛中获胜。为了解决在定制硬件上密码容易被破解的问题,Argon2也是故意降低运算速度,同时需要大量内存,以确保系统的安全性。

  • Pbkdf2PasswordEncoder

    Pbkdf2PasswordEncoder 使用 PBKDF2 算法对密码进行加密,和前面几种类似,PBKDF2

    算法也是一种故意降低运算速度的算法,当需要 FIPS (Federal Information Processing Standard,美国联邦信息处理标准)认证时,PBKDF2 算法是一个很好的选择。

  • SCryptPasswordEncoder

    SCryptPasswordEncoder 使用scrypt 算法对密码进行加密,和前面的几种类似,serypt 也是一种故意降低运算速度的算法,而且需要大量内存。

DelegatingPasswordEncoder(实现多种编码策略)
在这里插入图片描述
可以看到虽然实现了PasswordEncoder这个接口,但是他并不是一种加密方式,也没有实现加密的操作,只是将加密等一系列的操作委派给实现了同一个接口下的其他xxxPasswordEncoder实现类进行相应的处理,PasswordEncoderFactories中提供的就是DelegatingPasswordEncoder可以进行委派的加密方式列表。
例如:{noop}123321 ,{noop}当带有这个前缀时DelegatingPasswordEncoder会将其委派给NoOpPasswordEncoder进行密码的一系列处理,其他同理。

如何更改SpringSecurity中UserDetailsService 和 PasswordEncoder这个两个默认配置(核心配置类WebSecurityConfigurerAdapter)?

  • 方式一:通过这种方式覆盖掉SpringBoot提供的默认配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }
    @Bean
    @Override
    protected UserDetailsService userDetailsService() {
        // 基于内存的方式创建一个UserDetails对象
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();//创建了一个基于内存的管理对象,UserDetailsService 实现子类之一
        //User是SpringSecurity提供的一个专门用来构建UserDetails的类
        UserDetails details = User.withUsername("admin") //用户名
                .password("123") // 密码
                .authorities("ADMIN")// 指定当前用户所拥有的权限(后续说明,暂时指定为ADMIN或者别的也可以)
                .build();
        manager.createUser(details);
        return manager;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    	//后续详细说明,暂时先按如下配置
        http.authorizeRequests() //开启请求认证
                .anyRequest() // 任何请求
                .authenticated()//需要认证
                .and()
                .formLogin();//基于表单的形式,也就是默认生成的登录页面
    }
}
  • 方式二:通过WebSecurityConfigurerAdapter中提供的configure方法进行自定义配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

   @Override
   protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
       UserDetails details = User.withUsername("admin")
               .password("123")
               .authorities("ADMIN")
               .build();
       manager.createUser(details);
       auth.userDetailsService(manager).passwordEncoder(NoOpPasswordEncoder.getInstance());
   }

   @Override
   protected void configure(HttpSecurity http) throws Exception {
       http.authorizeRequests()
               .anyRequest()
               .authenticated()
               .and()
               .formLogin();
   }
}
  • 方式三:混合配置(不推荐)
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        UserDetails details = User.withUsername("admin")
                .password("123")
                .authorities("ADMIN")
                .build();
        manager.createUser(details);
        auth.userDetailsService(manager);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin();
    }
}

此时当我们再次进入登录页面时,就可以输入我们自定义的内存用户名和密码进行登录了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值