New version of Spring Security6.2 case - Authentication username and password

Foreword:

The new Spring Security6.2 architecture has been translated previously, includingoverall architecture, Authentication a> and Authorization. If you are interested, you can directly click on the link. This translation official website gives about Authentication’s Username/Password page.

First of all, the official website directly provides the authentication code based on user name and password. It can be said to be a small introductory case of spring security. Form login, enter the user name and password, and match the user name and password in the memory. If it matches, Will log in successfully.

Username/Password Authentication

One of the most common ways to authenticate a user is to verify a username and password. Spring Security provides comprehensive support for authentication using username and password. Username and password authentication can be configured in the following ways

@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);
	}

}

The preceding configuration automatically registers the in-memory UserDetailsService with the SecurityFilterChain, registers the DaoAuthenticationProvider with the default AuthenticationManager, and enables forms login and HTTP basic authentication.

There are also many examples listed on the official website. I have also attached hyperlinks here. Because the previous code is for form login, the priority is to put the hyperlink first,"I want to know how form login works"This section has been translated.

To learn more about usernamepassword authentication, consider the following use cases:

The principle of form login

Spring Security supports providing username and password via HTML forms. First, let's see how to redirect the user to the login form

The above figure is based on the SecurityFilterChain flow chart.​ 

  1. First, the user makes an unauthenticated request to an unauthorized resource (/private).
  2. Spring Security's AuthorizationFilter indicates that an unauthenticated request was denied by throwing AccessDeniedException.
  3. Since the user is not authenticated, the ExceptionTranslationFilter initiates Start Authentication and sends a redirect to the login page using the configured AuthenticationEntryPoint. In most cases, AuthenticationEntryPoint is an instance of LoginUrlAuthenticationEntryPoint.
  4. The login page to which browser requests are redirected.
  5. Something in the application must be rendered on the login page.

When a username and password are submitted, the UsernamePasswordAuthenticationFilter authenticates the username and password. UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter, so the diagram below should look very similar.

This graph is based on the SecurityFilterChain graph.

1. When the user submits their username and password, the UsernamePasswordAuthenticationFilter creates a UsernamePasswordAuthenticationToken, which is an authentication type, by extracting the username and password from the HttpServletRequest instance.

2. Next, pass the UsernamePasswordAuthenticationToken into the AuthenticationManager instance you want to authenticate to. The details of the AuthenticationManager depend on how the user information is stored.

3. If authentication fails, define it as "failed" and do the following:

        (1).Clear SecurityContextHolder.

        (2). Call RememberMeServices.loginFail. This is a no-op if Remember Me is not configured. See the RememberMeServices interface in the Javadoc.

        (3). Call AuthenticationFailureHandler. See AuthenticationFailureHandler class in Javadoc

4. If the authentication is successful, it will be explicitly defined as "success" and do the following:

        (1).SessionAuthenticationStrategy receives notification of new login. See the SessionAuthenticationStrategy interface in the Javadoc.

        (2). Authentication is set on the securitycontexholder. See the SecurityContextPersistenceFilter class in the Javadoc.

        (3).RememberMeServices. Call loginSuccess. If remember I have no configuration, this is a no-op. See the memormeservices interface in the Javadoc.

        (4).ApplicationEventPublisher publishes an InteractiveAuthenticationSuccessEvent event.

        (5). Call AuthenticationSuccessHandler. Normally this is a SimpleUrlAuthenticationSuccessHandler which redirects to the request saved by ExceptionTranslationFilter when we redirect to login page.

By default, Spring Security forms login is enabled. However, once any servlet-based configuration is provided, forms-based login must be provided explicitly. The following example shows a minimal explicit Java configuration:

public SecurityFilterChain filterChain(HttpSecurity http) {
	http
		.formLogin(withDefaults());
	// ...
}

In the previous configuration, Spring Security renders the default login page. Most production applications require custom login forms.

The following configuration demonstrates how to provide a custom login form.

ublic SecurityFilterChain filterChain(HttpSecurity http) {
	http
		.formLogin(form -> form
			.loginPage("/login")
			.permitAll()
		);
	// ...
}

When specifying a login page in Spring Security configuration, the user is responsible for rendering the page. The following Thymeleaf template generates an HTML login form that conforms to the /login login page.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
	<head>
		<title>Please Log In</title>
	</head>
	<body>
		<h1>Please Log In</h1>
		<div th:if="${param.error}">
			Invalid username and password.</div>
		<div th:if="${param.logout}">
			You have been logged out.</div>
		<form th:action="@{/login}" method="post">
			<div>
			<input type="text" name="username" placeholder="Username"/>
			</div>
			<div>
			<input type="password" name="password" placeholder="Password"/>
			</div>
			<input type="submit" value="Log in" />
		</form>
	</body>
</html>

There are a few key points about the default HTML form:

  • The form should perform a post to /login.
  • The form needs to contain a CSRF token, which is automatically included by Thymeleaf.
  • The form should specify the username in a parameter called username.
  • The form should specify the password in a parameter named password.
  • If an HTTP parameter named error is found, it means that the user failed to provide a valid username or password.
  • If an HTTP parameter named logout is found, the user has successfully logged out.

Many users just need a customized login page. However, you can customize everything shown previously with additional configurations if needed.

If you use Spring MVC, you will need a controller that maps GET /login to the login template we created. The following example shows a minimal LoginController:

@Controller
class LoginController {
	@GetMapping("/login")
	String login() {
		return "login";
	}
}

Finally, on the official websiteUsername/Password page, there is also a description of the authentication bean and global authenticationManger about custom identity, which is the above The last two points of the hyperlink should be translated first, and then discussed in the subsequent blog. This article can focus on the previous form login.

Publish an AuthenticationManager bean

A fairly common requirement is to publish an AuthenticationManager bean to allow custom authentication, for example in a @Service or Spring MVC @Controller. For example, you might want to authenticate users through a REST API rather than using a form login.

@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();
	}

}

With the above configuration, you can create a @RestController that uses AuthenticationManager like this:

@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) {
	}

}

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

Custom AuthenticationManager

Typically, Spring Security internally builds an AuthenticationManager consisting of a DaoAuthenticationProvider for username/password authentication. In some cases, you may still need to customize the AuthenticationManager instance used by Spring Security. For example, you may need to simply disable credential scrubbing for cached users. You can publish an AuthenticationManager with the following configuration:

@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();
	}

}

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:

@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);
	}

}

references:

"spring boot official website"

Guess you like

Origin blog.csdn.net/u012895183/article/details/134960546