使用者名稱/密碼認證

最常見的使用者認證方式之一是驗證使用者名稱和密碼。Spring Security 為使用者名稱和密碼認證提供了全面的支援。

您可以使用以下方式配置使用者名稱和密碼認證:

簡單的使用者名稱/密碼示例
  • 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)
	}

}
User#withDefaultPasswordEncoder 在生產環境中被認為是不安全的,僅用於示例應用程式。有關更多詳細資訊,請參閱 User#withDefaultPasswordEncoder

上述配置會自動向 SecurityFilterChain 註冊一個記憶體中的 UserDetailsService,向預設的 AuthenticationManager 註冊 DaoAuthenticationProvider,並啟用表單登入HTTP Basic認證。

要了解更多關於使用者名稱/密碼認證的資訊,請考慮以下用例:

釋出一個 AuthenticationManager bean

一個相當常見的需求是釋出一個 AuthenticationManager bean,以允許進行自定義認證,例如在 @Service 或 Spring MVC @Controller 中。例如,您可能希望透過 REST API 而不是使用表單登入來認證使用者。

您可以使用以下配置為自定義認證場景釋出這樣的 AuthenticationManager

為自定義認證釋出 AuthenticationManager bean
  • 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(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">
				<constructor-arg 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(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()
	}

}

有了上述配置,您可以建立一個使用 AuthenticationManager@RestController,如下所示:

為認證建立一個 @RestController
  • 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,您可以使用 HttpSessionSecurityContextRepository

自定義 AuthenticationManager

通常,Spring Security 內部構建一個由 DaoAuthenticationProvider 組成的 AuthenticationManager,用於使用者名稱/密碼認證。在某些情況下,可能仍然希望自定義 Spring Security 使用的 AuthenticationManager 例項。例如,您可能只需要停用快取使用者的憑據擦除

為此,您可以利用用於構建 Spring Security 全域性 AuthenticationManagerAuthenticationManagerBuilder 作為 bean 釋出的事實。您可以如下配置構建器:

配置全域性 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)
	}

}

或者,您可以配置一個本地 AuthenticationManager 來覆蓋全域性的。

為 Spring Security 配置本地 AuthenticationManager
  • 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())
			.authenticationManager(authenticationManager());

		return http.build();
	}

	private AuthenticationManager authenticationManager() {
		DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(userDetailsService());
		authenticationProvider.setPasswordEncoder(passwordEncoder());

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

		return providerManager;
	}

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

		return new InMemoryUserDetailsManager(userDetails);
	}

	private PasswordEncoder passwordEncoder() {
		return PasswordEncoderFactories.createDelegatingPasswordEncoder();
	}

}
<http authentication-manager-ref="authenticationManager">
	<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">
				<constructor-arg 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(anyRequest, authenticated)
			}
			formLogin { }
			httpBasic { }
			authenticationManager = authenticationManager()
		}

		return http.build()
	}

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

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

		return providerManager
	}

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

		return InMemoryUserDetailsManager(user)
	}

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

}
© . This site is unofficial and not affiliated with VMware.