spring security 系列 之 AuthenticationProvider

本文介绍了SpringSecurity中自定义认证和用户详情服务的实现过程。通过继承WebSecurityConfigurerAdapter并覆盖configure方法,配置登录拦截和认证管理。使用@Autowired的AuthenticationProviderService实现自定义认证逻辑,覆盖authenticate方法,根据不同的密码加密策略验证用户。同时,自定义UserDetailsService实现用户查询,通过loadUserByUsername方法从数据库获取用户信息。SpringSecurity的用户对象包括UserDetails、GrantedAuthority等,它们共同构成了认证的核心组件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们在使用spring sec的时候,一般会继承WebSecurityConfigurerAdapter类,然后选择覆盖protected void configure(AuthenticationManagerBuilder auth)和protected void configure(HttpSecurity http)方法

  @Autowired
    private AuthenticationProviderService authenticationProvider;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SCryptPasswordEncoder sCryptPasswordEncoder() {
        return new SCryptPasswordEncoder();
    }

//登录认证
    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(authenticationProvider);
    }

//登录拦截模式配置
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
        .authorizeRequests()
        .antMatchers("/**").authenticated()
        .and()
        .formLogin().loginPage("/login").permitAll()
        .successHandler(new MySavedRequestAwareAuthenticationSuccessHandler())
        .failureHandler(new SimpleUrlAuthenticationFailureHandler())
        .and()
        .logout().logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK))
        .and()
        .exceptionHandling().authenticationEntryPoint(new Http401AuthenticationEntryPoint("login first"))
        .accessDeniedHandler(new AccessDeniedHandlerImpl());
  }

Spring Security 中用户对象相关类结构图
在这里插入图片描述

一般登录的拦截 一般重写 protected void configure(HttpSecurity http) 方法,登录用户的认证我们一般,重写 protected void configure(AuthenticationManagerBuilder auth) 方法

登录用户的认证

简要说明
AuthenticationManager :认证是由 AuthenticationManager 来管理的,但是真正进行认证的是 AuthenticationManager 中定义的 AuthenticationProvider。AuthenticationManager 中可以定义有多个 AuthenticationProvider;

AuthenticationProvider :认证管理的逻辑实现类,当我们需要实现自己的认证逻辑的时候需要实现此接口,重写 public Authentication authenticate(Authentication authentication) 方法;

UserDetailsService :用户操作相关的接口类,当我们想要从数据库中查询用户的信息的时候需要实现实现此接口查询用户信息;

认证是由 AuthenticationManager 来管理的,但是真正进行认证的是 AuthenticationManager 中定义的 AuthenticationProvider。AuthenticationManager 中可以定义有多个 AuthenticationProvider。当我们使用 authentication-provider 元素来定义一个 AuthenticationProvider 时需要实现AuthenticationProvider接口,如果没有指定对应关联的 AuthenticationProvider 对象,Spring Security 默认会使用 DaoAuthenticationProvider。DaoAuthenticationProvider 在进行认证的时候需要一个 UserDetailsService 来获取用户的信息 UserDetails,其中包括用户名、密码和所拥有的权限等。所以如果我们需要改变认证的方式,我们可以实现自己的 AuthenticationProvider;如果需要改变认证的用户信息来源,我们可以实现 UserDetailsService。

实现了自己的 AuthenticationProvider 之后,我们可以在配置文件中这样配置来使用我们自己的 AuthenticationProvider。其中 myAuthenticationProvider 就是我们自己的 AuthenticationProvider 实现类对应的 bean。

   <security:authentication-manager>
      <security:authentication-provider ref="myAuthenticationProvider"/>
   </security:authentication-manager>

实现了自己的 UserDetailsService 之后,我们可以在配置文件中这样配置来使用我们自己的 UserDetailsService。其中的 myUserDetailsService 就是我们自己的 UserDetailsService 实现类对应的 bean。

   <security:authentication-manager>
      <security:authentication-provider user-service-ref="myUserDetailsService"/>
   </security:authentication-manager>

使用 jdbc-user-service 获取
在 Spring Security 的命名空间中在 authentication-provider 下定义了一个 jdbc-user-service 元素,通过该元素我们可以定义一个从数据库获取 UserDetails 的 UserDetailsService。jdbc-user-service 需要接收一个数据源的引用。

   <security:authentication-manager>
      <security:authentication-provider>
         <security:jdbc-user-service data-source-ref="dataSource"/>      
      </security:authentication-provider>
   </security:authentication-manager>

Spring Security 中认证的对象相关类结构图 ,具体关系如下图
在这里插入图片描述

代码实现

重写用户认证逻辑需要实现接口AuthenticationProvider ,重写 Authentication authenticate(Authentication authentication) 方法

package com.lagou.springsecurity.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import com.lagou.springsecurity.user.model.CustomUserDetails;

@Service
public class AuthenticationProviderService implements AuthenticationProvider {

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private SCryptPasswordEncoder sCryptPasswordEncoder;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        //根据用户名从数据库中获取CustomUserDetails
        CustomUserDetails user = userDetailsService.loadUserByUsername(username);

        //根据所配置的密码加密算法来分别验证用户密码
        switch (user.getUser().getPasswordEncoderType()) {
            case BCRYPT:
                return checkPassword(user, password, bCryptPasswordEncoder);
            case SCRYPT:
                return checkPassword(user, password, sCryptPasswordEncoder);
        }

        throw new  BadCredentialsException("Bad credentials");
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(aClass);
    }

    private Authentication checkPassword(CustomUserDetails user, String rawPassword, PasswordEncoder encoder) {
        if (encoder.matches(rawPassword, user.getPassword())) {
            return new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), user.getAuthorities());
        } else {
            throw new BadCredentialsException("Bad credentials");
        }
    }
}

重写用户的查询逻辑需要实现接口UserDetailsService

package com.lagou.springsecurity.service;

import com.lagou.springsecurity.domain.User;
import com.lagou.springsecurity.repository.UserRepository;
import com.lagou.springsecurity.user.model.CustomUserDetails;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.function.Supplier;

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public CustomUserDetails loadUserByUsername(String username) {
        Supplier<UsernameNotFoundException> s =
                () -> new UsernameNotFoundException("Username" + username + "is invalid!");

        User u = userRepository.findUserByUsername(username).orElseThrow(s);

        return new CustomUserDetails(u);
    }
}

补充说明

Spring Security 中的用户对象
Spring Security 中的用户对象用来描述用户并完成对用户信息的管理,涉及UserDetails、GrantedAuthority、UserDetailsService 和 UserDetailsManager这四个核心对象。

UserDetails:描述 Spring Security 中的用户。
GrantedAuthority:定义用户的操作权限。
UserDetailsService:定义了对 UserDetails 的查询操作。
UserDetailsManager:扩展 UserDetailsService,添加了创建用户、修改用户密码等功能。

这四个对象之间的关联关系如下图所示,显然,对于由 UserDetails 对象所描述的一个用户而言,它应该具有 1 个或多个能够执行的 GrantedAuthority:

Drawing 0.png

Spring Security 中的四大核心用户对象

结合上图,我们先来看承载用户详细信息的 UserDetails 接口,如下所示:

public interface UserDetails extends Serializable {
 
    //获取该用户的权限信息
    Collection<? extends GrantedAuthority> getAuthorities();
 
    //获取密码
	String getPassword();
	 
	//获取用户名
    String getUsername();
 
	//判断该账户是否已失效
    boolean isAccountNonExpired();
 
    //判断该账户是否已被锁定
    boolean isAccountNonLocked();
 
    //判断该账户的凭证信息是否已失效
    boolean isCredentialsNonExpired();
 
    //判断该用户是否可用
    boolean isEnabled();
}

通过 UserDetails,我们可以获取用户相关的基础信息,并判断其当前状态。同时,我们也可以看到 UserDetails 中保存着一组 GrantedAuthority 对象。而 GrantedAuthority 指定了一个方法用来获取权限信息,如下所示:

public interface GrantedAuthority extends Serializable {
    //获取权限信息
    String getAuthority();
}

UserDetails 存在一个子接口 MutableUserDetails,从命名上不难看出后者是一个可变的 UserDetails,而可变的内容就是密码。MutableUserDetails 接口的定义如下所示:

interface MutableUserDetails extends UserDetails {
    //设置密码
    void setPassword(String password);
}

如果我们想要在应用程序中创建一个 UserDetails 对象,可以使用如下所示的链式语法:

UserDetails user = User.withUsername("jianxiang") 
  .password("123456") 
  .authorities("read", "write") 
  .accountExpired(false) 
  .disabled(true) 
  .build();

Spring Security 还专门提供了一个 UserBuilder 对象来辅助构建 UserDetails,使用方式也类似:

User.UserBuilder builder = 
	User.withUsername("jianxiang");
 
UserDetails user = builder
  .password("12345") 
  .authorities("read", "write")
  .accountExpired(false) 
  .disabled(true) 
  .build();

在 Spring Security 中,针对 UserDetails 专门提供了一个 UserDetailsService,该接口用来管理 UserDetails,定义如下:

public interface UserDetailsService {
 
    //根据用户名获取用户信息
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

而 UserDetailsManager 继承了 UserDetailsService,并提供了一批针对 UserDetails 的操作接口,如下所示:

public interface UserDetailsManager extends UserDetailsService {
    //创建用户
    void createUser(UserDetails user);
 
    //更新用户
    void updateUser(UserDetails user);
 
    //删除用户
    void deleteUser(String username);
 
    //修改密码
    void changePassword(String oldPassword, String newPassword);
 
    //判断指定用户名的用户是否存在
    boolean userExists(String username);
}
SpringSecurity提供了自定义AuthenticationProvider和AuthenticationFilter的功能。在Spring Security中,AuthenticationProvider是一个接口,用于对用户进行身份验证。默认的实现是DaoAuthenticationProvider。你可以通过实现该接口来创建自定义的身份验证提供者,以适应特定的需求。自定义的AuthenticationProvider可以通过在配置文件中指定来替换默认的Provider。例如,在配置文件中添加以下代码可以引用自定义的Provider: ```xml <authentication-manager> <authentication-provider ref="customProvider" /> </authentication-manager> ``` 此处的`customProvider`是指自定义的AuthenticationProvider的bean的ID,你可以根据实际情况进行修改。 另外,AuthenticationFilter是用于处理身份验证请求的过滤器。它负责从请求中提取用户凭证并使用AuthenticationProvider进行身份验证。Spring Security提供了多个不同类型的AuthenticationFilter,如UsernamePasswordAuthenticationFilter、BasicAuthenticationFilter等。你可以根据需要选择合适的AuthenticationFilter,并将其配置到Spring Security的过滤器链中。 关于Spring Security的源码,你可以在GitHub上找到它的源码存储库。在这个存储库中,你可以查看和学习Spring Security的实现细节。 总结起来,你可以通过自定义AuthenticationProvider来实现特定需求的身份验证,同时可以选择合适的AuthenticationFilter来处理身份验证请求。你可以参考Spring Security的源码来了解更多细节和实现方式。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [SpringSecurity自定义AuthenticationProvider和AuthenticationFilter](https://blog.csdn.net/weixin_34248849/article/details/93984642)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Spring Security笔记:自定义Login/Logout Filter、AuthenticationProvider、AuthenticationToken](https://blog.csdn.net/weixin_33907511/article/details/85647330)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值