1. 概述

本教程将展示如何在Spring Security中自定义Authentication Provider ,相比默认实现中使用简单的UserDetailsService,更具灵活性。

2. Authentication Provider

Spring Security提供了多种执行身份认证的方法 。它们遵循简单的约定 —— AuthenticationProvider处理身份认证请求 并返回具有完整凭据的经过完全认证的对象 (Authentication)。

最常见和默认是使用DaoAuthenticationProvider —— 它会从简单的、只读的User DAO —— UserDetailsService中获取用户信息。UserDetailsService 仅能通过username 查询用户详情。对于大多数方案而言,这已足够。

一些更具体的场景需要访问完整的Authentication请求,以执行认证过程。例如当通过某些第三方服务(如Crowd)进行身份认证时,Authentication请求中的用户名和密码都是必需的。

对于这些更高级的方案,我们需要自定义一个AuthenticationProvider

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Override
    public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
        final String name = authentication.getName();
        final String password = authentication.getCredentials().toString();
        if (!"admin".equals(name) || !"system".equals(password)) {
            return null;
        }
        return authenticateAgainstThirdPartyAndGetAuthentication(name, password);
    }

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

这里,我们有一个返回 Authentication 对象的方法。它的实现可以根据我们想要的身份验证方式而有所不同。作为示例,我们可以编写一个固定凭据方法的示例:

private static UsernamePasswordAuthenticationToken authenticateAgainstThirdPartyAndGetAuthentication(String name, String password) {
    final List<GrantedAuthority> grantedAuths = new ArrayList<>();
    grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
    final UserDetails principal = new User(name, password, grantedAuths);
    return new UsernamePasswordAuthenticationToken(principal, password, grantedAuths);
}

值得注意的是,我们还为 UserDetails 对象添加了权限。在实际场景中,需要根据您的需求实现上述方法,受本文篇幅的限制,无法涵盖所有情况。

3. 注册Auth Provider

现在,我们已经定义了AuthenticationProvider,我们需要在XML中配置:

<http use-expressions="true">
    <intercept-url pattern="/**" access="isAuthenticated()"/>
    <http-basic/>
</http>

<authentication-manager>
    <authentication-provider
      ref="customAuthenticationProvider" />
</authentication-manager>

4. Java配置

当然也可以使用Java方式配置:

@Configuration
@EnableWebSecurity
@ComponentScan("com.baeldung.security")
public class SecurityConfig {

    @Autowired
    private CustomAuthenticationProvider authProvider;

    @Bean
    public AuthenticationManager authManager(HttpSecurity http) throws Exception {
        AuthenticationManagerBuilder authenticationManagerBuilder = 
            http.getSharedObject(AuthenticationManagerBuilder.class);
        authenticationManagerBuilder.authenticationProvider(authProvider);
        return authenticationManagerBuilder.build();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http.authorizeHttpRequests(request -> request.anyRequest()
                .authenticated())
            .httpBasic(Customizer.withDefaults())
            .build();
    }
}

在这里,我们为所有请求强制进行身份验证,并配置了 Http Basic 认证方式。

5. 执行认证

使用curl发送经过认证的请求:

curl --header "Accept:application/json" -i --user user1:user1Pass 
    http://localhost:8080/spring-security-custom/api/foo/1

使用Basic认证,我们成功得到200响应码:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=B8F0EFA81B78DE968088EBB9AFD85A60; Path=/spring-security-custom/; HttpOnly
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 02 Jun 2013 17:50:40 GMT

6. 总结

在本文中,我们讨论了如何在Spring Security中自定义Authentication Provider 。

本教程完整源代码,可从GitHub上找到。