Username/Password Authentication

验证用户最常见的方法之一是验证用户名和密码。Spring Security 全面支持使用用户名和密码进行验证。

One of the most common ways to authenticate a user is by validating a username and password. Spring Security provides comprehensive support for authenticating with a username and password.

您可以使用以下内容配置用户名和密码验证:

You can configure username and password authentication using the following:

Simple Username/Password Example
  • Java

  • XML

  • Kotlin

@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests((authorize) -> authorize
				.anyRequest().authenticated()
			)
			.httpBasic(Customizer.withDefaults())
			.formLogin(Customizer.withDefaults());

		return http.build();
	}

	@Bean
	public UserDetailsService userDetailsService() {
		UserDetails userDetails = User.withDefaultPasswordEncoder()
			.username("user")
			.password("password")
			.roles("USER")
			.build();

		return new InMemoryUserDetailsManager(userDetails);
	}

}
<http>
	<intercept-url pattern="/**" access="authenticated"/>
	<form-login />
	<http-basic />

	<user-service>
		<user name="user"
			password="{noop}password"
			authorities="ROLE_USER" />
	</user-service>
</http>
import org.springframework.security.config.annotation.web.invoke

@Configuration
@EnableWebSecurity
class SecurityConfig {

	@Bean
	fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
		http {
			authorizeHttpRequests {
				authorize(anyRequest, authenticated)
			}
			formLogin { }
			httpBasic { }
		}

		return http.build()
	}

	@Bean
	fun userDetailsService(): UserDetailsService {
		val user = User.withDefaultPasswordEncoder()
			.username("user")
			.password("password")
			.roles("USER")
			.build()

		return InMemoryUserDetailsManager(user)
	}

}

前面的配置会使用 SecurityFilterChain`自动注册一个 in-memory `UserDetailsService,使用默认 AuthenticationManager注册 DaoAuthenticationProvider,并启用 Form LoginHTTP Basic身份验证。

The preceding configuration automatically registers an in-memory UserDetailsService with the SecurityFilterChain, registers the DaoAuthenticationProvider with the default AuthenticationManager, and enables Form Login and HTTP Basic authentication.

如欲了解用户名/密码验证的更多信息,请考虑以下用例:

To learn more about username/password authentication, consider the following use cases:

Publish an AuthenticationManager bean

一个相当普遍的需求是发布一个`AuthenticationManager`bean以允许自定义身份验证,比如`@Service`或 Spring MVC `@Controller`中的身份验证。例如,您可能想要通过 REST API 而不是使用Form Login来对用户进行身份验证。

A fairly common requirement is publishing an AuthenticationManager bean to allow for custom authentication, such as in a @Service or Spring MVC @Controller. For example, you may want to authenticate users via a REST API instead of using Form Login.

对于自定义验证方案,您可以使用以下配置发布此类 AuthenticationManager

You can publish such an AuthenticationManager for custom authentication scenarios using the following configuration:

Publish AuthenticationManager bean for Custom Authentication
  • Java

  • XML

  • Kotlin

@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests((authorize) -> authorize
				.requestMatchers("/login").permitAll()
				.anyRequest().authenticated()
			);

		return http.build();
	}

	@Bean
	public AuthenticationManager authenticationManager(
			UserDetailsService userDetailsService,
			PasswordEncoder passwordEncoder) {
		DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
		authenticationProvider.setUserDetailsService(userDetailsService);
		authenticationProvider.setPasswordEncoder(passwordEncoder);

		return new ProviderManager(authenticationProvider);
	}

	@Bean
	public UserDetailsService userDetailsService() {
		UserDetails userDetails = User.withDefaultPasswordEncoder()
			.username("user")
			.password("password")
			.roles("USER")
			.build();

		return new InMemoryUserDetailsManager(userDetails);
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return PasswordEncoderFactories.createDelegatingPasswordEncoder();
	}

}
<http>
	<intercept-url pattern="/login" access="permitAll"/>
	<intercept-url pattern="/**" access="authenticated"/>

	<bean id="authenticationManager"
			class="org.springframework.security.authentication.ProviderManager">
		<constructor-arg>
			<bean class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
				<property name="userDetailsService" ref="userDetailsService" />
				<property name="passwordEncoder" ref="passwordEncoder" />
			</bean>
		</constructor-arg>
	</bean>

	<user-service id="userDetailsService">
		<user name="user"
			password="{noop}password"
			authorities="ROLE_USER" />
	</user-service>

	<bean id="passwordEncoder"
			class="org.springframework.security.crypto.factory.PasswordEncoderFactories" factory-method="createDelegatingPasswordEncoder"/>
</http>
import org.springframework.security.config.annotation.web.invoke

@Configuration
@EnableWebSecurity
class SecurityConfig {

	@Bean
	fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
		http {
			authorizeHttpRequests {
				authorize("/login", permitAll)
				authorize(anyRequest, authenticated)
			}
		}

		return http.build()
	}

	@Bean
	fun authenticationManager(
			userDetailsService: UserDetailsService,
			passwordEncoder: PasswordEncoder): AuthenticationManager {
		val authenticationProvider = DaoAuthenticationProvider()
		authenticationProvider.setUserDetailsService(userDetailsService)
		authenticationProvider.setPasswordEncoder(passwordEncoder)

		return ProviderManager(authenticationProvider)
	}

	@Bean
	fun userDetailsService(): UserDetailsService {
		val user = User.withDefaultPasswordEncoder()
			.username("user")
			.password("password")
			.roles("USER")
			.build()

		return InMemoryUserDetailsManager(user)
	}

	@Bean
	fun passwordEncoder(): PasswordEncoder {
		return PasswordEncoderFactories.createDelegatingPasswordEncoder()
	}

}

在先前配置就位后,您可以创建这样使用的 @RestController AuthenticationManager

With the preceding configuration in place, you can create a @RestController that uses the AuthenticationManager as follows:

Create a @RestController for Authentication
  • Java

  • Kotlin

@RestController
public class LoginController {

	private final AuthenticationManager authenticationManager;

	public LoginController(AuthenticationManager authenticationManager) {
		this.authenticationManager = authenticationManager;
	}

	@PostMapping("/login")
	public ResponseEntity<Void> login(@RequestBody LoginRequest loginRequest) {
		Authentication authenticationRequest =
			UsernamePasswordAuthenticationToken.unauthenticated(loginRequest.username(), loginRequest.password());
		Authentication authenticationResponse =
			this.authenticationManager.authenticate(authenticationRequest);
		// ...
	}

	public record LoginRequest(String username, String password) {
	}

}
@RestController
class LoginController(val authenticationManager: AuthenticationManager) {

	@PostMapping("/login")
	fun login(@RequestBody loginRequest: LoginRequest): ResponseEntity<Void> {
		val authenticationRequest =
			UsernamePasswordAuthenticationToken.unauthenticated(
				loginRequest.username, loginRequest.password)
		val authenticationResponse =
			authenticationManager.authenticate(authenticationRequest)
		// ...
	}

	data class LoginRequest(val username: String, val password: String)

}

在本示例中,您有责任在需要时将经过身份验证的用户保存在 SecurityContextRepository 中。例如,如果使用 HttpSession 在请求之间持久化 SecurityContext,则可以使用 xref:servlet/authentication/persistence.adoc#httpsecuritycontextrepository[HttpSessionSecurityContextRepository

In this example, it is your responsibility to save the authenticated user in the SecurityContextRepository if needed. For example, if using the HttpSession to persist the SecurityContext between requests, you can use HttpSessionSecurityContextRepository.

Customize the AuthenticationManager

通常,Spring Security 已构建内部包含用于用户名/密码身份验证的 AuthenticationManagerDaoAuthenticationProvider。在某些情况下,可能仍需要自定义 Spring Security 所使用的 AuthenticationManager 的实例。例如,您可能需要为缓存用户简单禁用 credential erasure

Normally, Spring Security builds an AuthenticationManager internally composed of a DaoAuthenticationProvider for username/password authentication. In certain cases, it may still be desired to customize the instance of AuthenticationManager used by Spring Security. For example, you may need to simply disable credential erasure for cached users.

执行此操作的推荐方法是仅发布您自己的 AuthenticationManager bean,Spring Security 将会使用它。您可以使用以下配置发布 AuthenticationManager

The recommended way to do this is to simply publish your own AuthenticationManager bean, and Spring Security will use it. You can publish an AuthenticationManager using the following configuration:

Publish AuthenticationManager bean for Spring Security
  • Java

  • XML

  • Kotlin

@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests((authorize) -> authorize
				.requestMatchers("/login").permitAll()
				.anyRequest().authenticated()
			)
			.httpBasic(Customizer.withDefaults())
			.formLogin(Customizer.withDefaults());

		return http.build();
	}

	@Bean
	public AuthenticationManager authenticationManager(
			UserDetailsService userDetailsService,
			PasswordEncoder passwordEncoder) {
		DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
		authenticationProvider.setUserDetailsService(userDetailsService);
		authenticationProvider.setPasswordEncoder(passwordEncoder);

		ProviderManager providerManager = new ProviderManager(authenticationProvider);
		providerManager.setEraseCredentialsAfterAuthentication(false);

		return providerManager;
	}

	@Bean
	public UserDetailsService userDetailsService() {
		UserDetails userDetails = User.withDefaultPasswordEncoder()
			.username("user")
			.password("password")
			.roles("USER")
			.build();

		return new InMemoryUserDetailsManager(userDetails);
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return PasswordEncoderFactories.createDelegatingPasswordEncoder();
	}

}
<http>
	<intercept-url pattern="/login" access="permitAll"/>
	<intercept-url pattern="/**" access="authenticated"/>
	<form-login />
	<http-basic />

	<bean id="authenticationManager"
			class="org.springframework.security.authentication.ProviderManager">
		<constructor-arg>
			<bean class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
				<property name="userDetailsService" ref="userDetailsService" />
				<property name="passwordEncoder" ref="passwordEncoder" />
			</bean>
		</constructor-arg>
	</bean>

	<user-service id="userDetailsService">
		<user name="user"
			password="{noop}password"
			authorities="ROLE_USER" />
	</user-service>

	<bean id="passwordEncoder"
			class="org.springframework.security.crypto.factory.PasswordEncoderFactories" factory-method="createDelegatingPasswordEncoder"/>
</http>
import org.springframework.security.config.annotation.web.invoke

@Configuration
@EnableWebSecurity
class SecurityConfig {

	@Bean
	fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
		http {
			authorizeHttpRequests {
				authorize("/login", permitAll)
				authorize(anyRequest, authenticated)
			}
			formLogin { }
			httpBasic { }
		}

		return http.build()
	}

	@Bean
	fun authenticationManager(
			userDetailsService: UserDetailsService,
			passwordEncoder: PasswordEncoder): AuthenticationManager {
		val authenticationProvider = DaoAuthenticationProvider()
		authenticationProvider.setUserDetailsService(userDetailsService)
		authenticationProvider.setPasswordEncoder(passwordEncoder)

		val providerManager = ProviderManager(authenticationProvider)
		providerManager.eraseCredentialsAfterAuthentication = false

		return providerManager
	}

	@Bean
	fun userDetailsService(): UserDetailsService {
		val user = User.withDefaultPasswordEncoder()
			.username("user")
			.password("password")
			.roles("USER")
			.build()

		return InMemoryUserDetailsManager(user)
	}

	@Bean
	fun passwordEncoder(): PasswordEncoder {
		return PasswordEncoderFactories.createDelegatingPasswordEncoder()
	}

}

或者,您可以利用这样一个事实:用于生成 Spring Security 全局 AuthenticationManagerAuthenticationManagerBuilder 作为 bean 发布。您可以这样配置生成器:

Alternatively, you can take advantage of the fact that the AuthenticationManagerBuilder used to build Spring Security’s global AuthenticationManager is published as a bean. You can configure the builder as follows:

Configure global AuthenticationManagerBuilder
  • Java

  • Kotlin

@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		// ...
		return http.build();
	}

	@Bean
	public UserDetailsService userDetailsService() {
		// Return a UserDetailsService that caches users
		// ...
	}

	@Autowired
	public void configure(AuthenticationManagerBuilder builder) {
		builder.eraseCredentials(false);
	}

}
import org.springframework.security.config.annotation.web.invoke

@Configuration
@EnableWebSecurity
class SecurityConfig {

	@Bean
	fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
		// ...
		return http.build()
	}

	@Bean
	fun userDetailsService(): UserDetailsService {
		// Return a UserDetailsService that caches users
		// ...
	}

	@Autowired
	fun configure(builder: AuthenticationManagerBuilder) {
		builder.eraseCredentials(false)
	}

}