(4)spring security - 认识AuthenticationProvider

目标:

  • 学习自定义Authentication Provider(认证程序)

本章还是基于以下图片进行学习:
在这里插入图片描述

如图所示,AuthenticationProvider负责身份验证逻辑。AuthenticationManager接收来自HTTP过滤层的请求,并将认证用户的职责委托给AuthenticationProvider。

  • 如果发起请求的用户没有经过身份验证,则返回给客户端的响应状态为HTTP 401 Unauthorized。
  • 如果用户通过了身份验证,则用户详细信息以及身份验证状态将存储在SecurityContext中。

0.AuthenticationManager接口简介

‌AuthenticationManager‌是负责管理认证流程的核心接口。默认的实现类是ProviderManager,主要负责管理多个AuthenticationProvider,并委托它们进行用户身份认证‌‌。ProviderManager会遍历所有支持的AuthenticationProvider,找到第一个能够成功认证的Provider。如果某个Provider认证成功,则整个认证过程结束;如果所有Provider都认证失败,则会尝试使用双亲认证管理器(如果有设置)继续认证‌。在认证过程中,如果某个AuthenticationProvider抛出AuthenticationException,整个认证过程不会停止,会继续尝试下一个Provider;如果抛出AccountStatusException或InternalAuthenticationServiceException,则异常会被继续抛出,整个认证过程停止;如果抛出其他异常,则整个认证过程停止‌。

当用户登录时,AuthenticationManager会将认证请求传递给各个AuthenticationProvider进行处理。每个AuthenticationProvider根据其实现逻辑(如数据库查询、LDAP验证等)来验证用户的身份。

public interface AuthenticationManager {
	Authentication authenticate(Authentication authentication) throws AuthenticationException;
}

1.AuthenticationProvider接口简介

‌AuthenticationProvider‌是一个接口,用于执行具体的认证逻辑。它接收一个Authentication对象(如用户名和密码),并尝试认证用户。每个AuthenticationProvider负责某种类型的认证方式,例如基于用户名和密码的认证、JWT认证等。如果某个AuthenticationProvider成功验证了用户身份,它会返回一个已认证的Authentication对象;否则,会抛出异常表示认证失败‌。

默认情况下,spring security将匹配请求中提供的用户名和密码,以验证用户身份。用户详细信息通常从提供的UserDetailsService获取,UserDetailsService从底层数据库加载用户详细信息。

但是,当我们不得不使用第三方提供的身份信息认证用户或某些spring security不支持的特定系统时,我们需要使用自定义AuthenticationProvider实现创建自定义身份认证逻辑。

以下是接口的源代码:

public interface AuthenticationProvider {
	Authentication authenticate(Authentication authentication) throws AuthenticationException;
	boolean supports(Class<?> authentication);
}
  • authenticate()方法接收一个未经身份认证的Authentication对象作为参数,并返回一个经过完整身份认证的Authentication对象,该对象具有包括凭据在内的最终身份认证结果。
  • 如果AuthenticationProvider无法支持所传递的Authentication 对象的身份认证,它可以返回null。在这种情况下,如果应用程序有多个AuthorizationProvider类,那么将尝试下一个Provider。
  • 如果身份验证失败,它应该抛出AuthenticationException。
  • 如果此AuthenticationProvider支持指定的Authentication 对象,则返回true。
  • 在有多个AuthenticationProvider实现类的情况下,能够执行身份认证的AuthenticationProvider的选择是由ProviderManager(一个AuthenticationManager接口的实现类)在运行时执行的。

在这里插入图片描述

2.实现自定义认证程序

创建和注册自定义认证程序需要两个步骤:

  1. 决定新的AuthenticationProvider支持哪些类型的Authentication 对象?
    • 覆盖supports()方法来指定它。在本例中,我们使用UsernamePasswordAuthenticationToken类型,它支持基于用户名和密码的身份验证流。
  2. 通过覆盖authenticate()方法来实现身份验证逻辑。
    • 在本例中,我们在isValidUser()方法中编写认证逻辑。
package com.drson.usermanagement.service;

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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
    // 在这个函数中,我们需要连接身份提供者并验证用户
    // 出于演示目的,我们对单个用户进行硬编码
    UserDetails isValidUser(String username, String password) {
        if (username.equalsIgnoreCase("drson")
                && password.equals("123456")) {

            UserDetails user = User
                    .withUsername(username)
                    .password("NOT_DISCLOSED")
                    .roles("USER_ROLE")
                    .build();

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

        UserDetails userDetails = isValidUser(username, password);//不再是从数据库加载用户信息
        if (userDetails != null) {
            return new UsernamePasswordAuthenticationToken(
                    username,
                    password,
                    userDetails.getAuthorities());
        } else {
            throw new BadCredentialsException("Incorrect user credentials !!");
        }
    }

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

3.注册自定义认证程序

package com.drson.usermanagement.config;

import com.drson.usermanagement.service.CustomAuthenticationProvider;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
    @Resource
    private CustomAuthenticationProvider customAuthenticationProvider;
    //定义密码编码器
    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        //注册新的认证程序
        http.authenticationProvider(customAuthenticationProvider);

        http.authorizeHttpRequests((authorize) ->{
            authorize.requestMatchers("/login").permitAll();//登录API不需要认证
            authorize.anyRequest().authenticated();//其它所有api都需要认证才能访问
        });

        http.formLogin(Customizer.withDefaults());//使用默认登录表单

        return http.build();
    }
}

SecurityFilterChain的工作原理
当请求到达FilterChainProxy时,它会根据当前请求匹配得到对应的SecurityFilterChain,然后将请求依次转发给该SecurityFilterChain中的所有Security Filters。每个过滤器根据其职责对请求进行处理,最终决定是否允许该请求继续处理或返回响应‌。
在这里插入图片描述

4.小结

本章简单实现了自定义的认证程序,了解了AuthenticationProvider接口的简单用法,以及简单配置了SecurityFilterChain 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值