1. Overview
1.概述
In this quick article, we’re going to focus on using multiple mechanisms to authenticate users in Spring Security.
在这篇快速文章中,我们将重点讨论在Spring Security中使用多种机制来验证用户。
We’ll do that by configuring multiple authentication providers.
我们将通过配置多个认证提供者来做到这一点。
2. Authentication Providers
2.认证提供者
An AuthenticationProvider is an abstraction for fetching user information from a specific repository (like a database, LDAP, custom third party source, etc. ). It uses the fetched user information to validate the supplied credentials.
AuthenticationProvider是一个抽象,用于从特定的存储库(如数据库、LDAP、自定义第三方源等)获取用户信息。).它使用获取的用户信息来验证所提供的凭证。
Simply put, when multiple authentication providers are defined, the providers will be queried in the order they’re declared.
简单地说,当定义了多个认证提供者时,这些提供者将按照它们被声明的顺序被查询。
For a quick demonstration, we’ll configure two authentication providers – a custom authentication provider and an in-memory authentication provider.
为了快速演示,我们将配置两个认证提供者–一个自定义认证提供者和一个内存认证提供者。
3. Maven Dependencies
3.Maven的依赖性
Let’s first add the necessary Spring Security dependencies into our web application:
首先,让我们把必要的Spring Security依赖性添加到我们的Web应用程序中。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
And, without Spring Boot:
而且,没有Spring Boot。
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
The latest version of these dependencies can be found at spring-security-web, spring-security-core, and spring-security-config.
这些依赖的最新版本可以在spring-security-web、spring-security-core和spring-security-config找到。
4. Custom Authentication Provider
4.自定义认证提供者
Let’s now create a custom authentication provider by implementing the AuthneticationProvider interface.
现在让我们通过实现AuthneticationProvider接口来创建一个自定义的认证提供者。
We’re going to implement the authenticate method – which attempts the authentication. The input Authentication object contains the username and password credentials supplied by the user.
我们将实现authenticate方法–尝试进行认证。输入的Authentication对象包含用户提供的用户名和密码凭证。
The authenticate method returns a fully populated Authentication object if the authentication is successful. If authentication fails, it throws an exception of type AuthenticationException:
如果认证成功,authenticate方法返回一个完全填充的Authentication对象。如果认证失败,它会抛出一个AuthenticationException类型的异常。
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication auth)
throws AuthenticationException {
String username = auth.getName();
String password = auth.getCredentials()
.toString();
if ("externaluser".equals(username) && "pass".equals(password)) {
return new UsernamePasswordAuthenticationToken
(username, password, Collections.emptyList());
} else {
throw new
BadCredentialsException("External system authentication failed");
}
}
@Override
public boolean supports(Class<?> auth) {
return auth.equals(UsernamePasswordAuthenticationToken.class);
}
}
Naturally, this is a simple implementation for the purpose of our example here.
当然,这只是为了我们这里的例子的简单实现。
5. Configuring Multiple Authentication Providers
5.配置多个认证供应商
Let’s now add the CustomAuthenticationProvider and an in-memory authentication provider to our Spring Security configuration.
现在让我们把CustomAuthenticationProvider和一个内存认证提供者添加到我们的Spring Security配置中。
5.1. Java Configuration
5.1.Java配置[/strong]
In our configuration class, let’s now create and add the authentication providers using the AuthenticationManagerBuilder.
在我们的配置类中,现在让我们使用AuthenticationManagerBuilder创建并添加认证提供者。
First, the CustomAuthenticationProvider and then, an in-memory authentication provider by using inMemoryAuthentication().
首先是CustomAuthenticationProvider,然后是通过使用inMemoryAuthentication()来提供内存中的认证。
We are also making sure that access to the URL pattern “/api/**” needs to be authenticated:
我们还确保对URL模式”/api/**“的访问需要经过认证。
@EnableWebSecurity
public class MultipleAuthProvidersSecurityConfig {
@Autowired
CustomAuthenticationProvider customAuthProvider;
@Bean
public AuthenticationManager authManager(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder =
http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.authenticationProvider(customAuthProvider);
authenticationManagerBuilder.inMemoryAuthentication()
.withUser("memuser")
.password(passwordEncoder().encode("pass"))
.roles("USER");
return authenticationManagerBuilder.build();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager authManager)
throws Exception {
http.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/api/**")
.authenticated()
.and()
.authenticationManager(authManager);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
5.2. XML Configuration
5.2.XML配置
Alternatively, if we want to use XML configuration instead of Java configuration:
另外,如果我们想使用XML配置而不是Java配置。
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="memuser" password="pass"
authorities="ROLE_USER" />
</security:user-service>
</security:authentication-provider>
<security:authentication-provider
ref="customAuthenticationProvider" />
</security:authentication-manager>
<security:http>
<security:http-basic />
<security:intercept-url pattern="/api/**"
access="isAuthenticated()" />
</security:http>
6. The Application
6.应用
Next, let’s create a simple REST endpoint that is secured by our two authentication providers.
接下来,让我们创建一个简单的REST端点,该端点由我们的两个认证提供者保证。
To access this endpoint, a valid username and password must be supplied. Our authentication providers will validate the credentials and determine whether to allow access or not:
要访问这个端点,必须提供一个有效的用户名和密码。我们的认证供应商将验证证书并决定是否允许访问。
@RestController
public class MultipleAuthController {
@GetMapping("/api/ping")
public String getPing() {
return "OK";
}
}
7. Testing
7.测试
Finally, let’s now test the access to our secure application. Access will be allowed only if valid credentials are supplied:
最后,让我们现在测试一下对我们安全应用程序的访问。只有在提供有效凭证的情况下,才会允许访问。
@Autowired
private TestRestTemplate restTemplate;
@Test
public void givenMemUsers_whenGetPingWithValidUser_thenOk() {
ResponseEntity<String> result
= makeRestCallToGetPing("memuser", "pass");
assertThat(result.getStatusCodeValue()).isEqualTo(200);
assertThat(result.getBody()).isEqualTo("OK");
}
@Test
public void givenExternalUsers_whenGetPingWithValidUser_thenOK() {
ResponseEntity<String> result
= makeRestCallToGetPing("externaluser", "pass");
assertThat(result.getStatusCodeValue()).isEqualTo(200);
assertThat(result.getBody()).isEqualTo("OK");
}
@Test
public void givenAuthProviders_whenGetPingWithNoCred_then401() {
ResponseEntity<String> result = makeRestCallToGetPing();
assertThat(result.getStatusCodeValue()).isEqualTo(401);
}
@Test
public void givenAuthProviders_whenGetPingWithBadCred_then401() {
ResponseEntity<String> result
= makeRestCallToGetPing("user", "bad_password");
assertThat(result.getStatusCodeValue()).isEqualTo(401);
}
private ResponseEntity<String>
makeRestCallToGetPing(String username, String password) {
return restTemplate.withBasicAuth(username, password)
.getForEntity("/api/ping", String.class, Collections.emptyMap());
}
private ResponseEntity<String> makeRestCallToGetPing() {
return restTemplate
.getForEntity("/api/ping", String.class, Collections.emptyMap());
}
8. Conclusion
8.结论
In this quick tutorial, we’ve seen how multiple authentication providers can be configured in Spring Security. We have secured a simple application using a custom authentication provider and an in-memory authentication provider.
在这个快速教程中,我们已经看到了如何在Spring Security中配置多个认证提供者。我们使用一个自定义的认证提供者和一个内存认证提供者来保护一个简单的应用程序。
And we’ve also written tests to verify that the access to our application requires credentials that can be validated by at least one of our authentication providers.
我们还编写了测试,以验证对我们的应用程序的访问需要至少由我们的一个认证提供者验证的凭证。
As always, the full source code of the implementation can be found over on GitHub.
一如既往,实现的完整源代码可以在GitHub上找到over on GitHub.