SpringSecurity Basics
In the previous article "Basic Use of SpringBoot + Spring Security and Personalized Login Configuration", SpringSecurity was briefly introduced, basically introducing the interface and implementing the functions. This article attempts to make a simple analysis of the user authentication process from the source code perspective.
Before a specific analysis, we can first look at the general principles of SpringSecurity:
SpringSecurity Basics
In fact, it is relatively simple, mainly through a series of filters to intercept and process the request.
Certification processing process description
Let's look at UsernamePasswordAuthenticationFilter class directly.
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter // Log in to request authentication public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { // Determine whether it is a POST request if (this.postOnly && !request.getMethod().equals("POST")) { throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); } else { // Get the user, password String username = this.obtainUsername(request); String password = this.obtainPassword(request); if (username == null) { username = ""; } if (password == null) { password = ""; } username = username.trim(); // Generate Token, UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); this.setDetails(request, authRequest); // Further verify return this.getAuthenticationManager().authenticate(authRequest); } }} In the attemptAuthentication method, the main thing is to obtain the username and password request values, and then generate a UsernamePasswordAuthenticationToken object for further verification.
But we can first look at the construction method of UsernamePasswordAuthenticationToken
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) { // Set empty permission super((Collection)null); this.principal = principal; this.credentials = credentials; // Set whether this.setAuthenticated(false);} In fact, UsernamePasswordAuthenticationToken is inherited from Authentication . This object was mentioned in the previous article. It is a parameter in the successful login callback method, which contains parameters such as user information, request information, etc.
So let's see
this.getAuthenticationManager().authenticate(authRequest);
There is an AuthenticationManager here, but the real call is ProviderManager .
public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean { public Authentication authentication(Authentication authentication) throws AuthenticationException { Class<? extends Authentication> toTest = authentication.getClass(); AuthenticationException lastException = null; Authentication result = null; boolean debug = logger.isDebugEnabled(); Iterator var6 = this.getProviders().iterator(); while(var6.hasNext()) { AuthenticationProvider provider = (AuthenticationProvider)var6.next(); // 1. Determine whether there is a provider to support the Authentication if (provider.supports(toTest)) { // 2. True logical judgment result = provider.authenticate(authentication); } }} public Authentication authentication(Authentication authentication) throws AuthenticationException { UserDetails user = this.userCache.getUserFromCache(username); if (user == null) { cacheWasUsed = false; // 1. Go to get UserDetails user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication); } try { // 2. Pre-check this.preAuthenticationChecks.check(user); // 3. Additional check (password check) this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication); } catch (AuthenticationException var7) { } // 4. The last check this.postAuthenticationChecks.check(user); // 5. Return the real certified Authentication return this.createSuccessAuthentication(principalToReturn, authentication, user);} The two checks on UserDetails here are mainly through its four methods that return boolean type.
After verification of information, an authenticated Authentication is returned through the construction method of UsernamePasswordAuthenticationToken .
After obtaining the certified Authentication, successHandler will be called again. Or if it fails authentication, call failureHandler.
How authentication results are shared among multiple requests
After completing the user authentication processing process, let’s think about how to share the authentication result between multiple requests?
Because there is no configuration for this, we can think of the default method that should be to store the authentication result in the session.
So when is it stored in the session?
We can continue to look at the source code of the authentication process. After passing the attemptAuthentication method, if the authentication is successful, successfulAuthentication will be called. In this method, not only is successHandler called, but there is also a line of more important code.
SecurityContextHolder.getContext().setAuthentication(authResult);
SecurityContextHolder is a package for ThreadLocal. ThreadLocal is a data storage class inside a thread. Through it, data can be stored in a specified thread. After data storage, only the stored data can be obtained in the specified thread, but other threads cannot obtain data. For more information about the principles of ThreadLocal, please check out my previous article.
Generally speaking, the requests and returns of the same interface will be completed in one thread. We put authResult in the SecurityContextHolder, and we can also take it out anywhere else.
Finally, the authResult is retrieved from SecurityContextPersistenceFilter and the session is stored.
SecurityContextPersistenceFilter is also a filter, which is at the forefront of the entire Security filter chain, which means that the filter is first passed when verification is started, and the last pass is finally passed after verification is completed.
Obtain authenticated user information
/** * Get the currently logged in user* @return Complete Authentication */@GetMapping("/me1")public Object currentUser() { return SecurityContextHolder.getContext().getAuthentication();}@GetMapping("/me2")public Object currentUser(Authentication authentication) { return authentication;}/** * @param userDetails * @return Only userDetails */@GetMapping("/me3")public Object cuurentUser(@AuthenticationPrincipal UserDetails userDetails) { return userDetails;}The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.