This is the 12th day of my participation in the August Challenge

preface

Spring is a very popular and successful Java application development framework. Spring Security provides a complete solution for Web application Security based on the Spring framework. Generally speaking, the security of Web applications includes Authentication and Authorization.

User authentication refers to verifying whether a user is a legitimate subject in the system, that is, whether the user can access the system. User authentication generally requires a user name and password. The system verifies the user name and password to complete the authentication process.

User authorization refers to verifying whether a user has permission to perform an operation. In a system, different users have different permissions. For example, some users can only read a file, while others can modify it. Generally speaking, the system assigns different roles to different users, and each role has a series of permissions

Core components

SecurityContextHolder

SecurityContextHolder It holds information about the security context. The name of the current user, whether the user has been authenticated, what role rights he has, and so on are stored in the SecurityContextHolder. SecurityContextHolder uses the ThreadLocal policy to store authentication information by default. Seeing ThreadLocal means that this is a thread-bound policy. In the Web environment, Spring Security automatically binds authentication information to the current thread when the user logs in, and automatically clears authentication information of the current thread when the user logs out

Look at the source code it has static methods

  // Get the context
  public static SecurityContext getContext(a) {
        return strategy.getContext();
    }
  // Clear the context
  public static void clearContext(a) {
        strategy.clearContext();
    }   
Copy the code
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
Copy the code

GetAuthentication () returns authentication information, and getPrincipal() returns identity information

UserDetails is an interface that Spring wraps around identity information

SecurityContext

The security context holds the Authentication object. If the user is not authenticated, the Authentication object will be empty. See the source code

package org.springframework.security.core.context;

import java.io.Serializable;
import org.springframework.security.core.Authentication;

public interface SecurityContext extends Serializable {
    Authentication getAuthentication(a);

    void setAuthentication(Authentication var1);
}
Copy the code

Authentication

Authentication object. This object mainly contains UserDetails and information required for user Authentication, such as the user name and password submitted by the user, remember-me Token, or Digest Hash value. Different Authentication modes use different Authentication implementations

See the source can know

package org.springframework.security.core;

import java.io.Serializable;
import java.security.Principal;
import java.util.Collection;

public interface Authentication extends Principal.Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();

    Object getCredentials(a);

    Object getDetails(a);

    Object getPrincipal(a);

    boolean isAuthenticated(a);

    void setAuthenticated(boolean var1) throws IllegalArgumentException;
}
Copy the code
  1. Authentication is an interface in the Spring Security package that directly inherits from the Principal class, which is located in the Java.Security package. As you can see, Authentication is the highest level of identity/Authentication abstraction in Spring Security. From this top-level interface, we can get a list of user permissions, passwords, user details, user identities, and authentication information.

  2. GetAuthorities (), a list of permission information, defaults to some implementation classes for the GrantedAuthority interface, usually as a series of strings that represent permission information.

  3. GetCredentials (), password information, a string of passwords entered by users, which are usually removed after authentication to ensure security.

  4. GetDetails (), the implementation interface in a Web application is usually WebAuthenticationDetails, which records the visitor’s IP address and the value of the sessionId.

  5. GetPrincipal (), tap on the blackboard!! The most important identity information, most often returned, is the implementation class of the UserDetails interface, one of the commonly used interfaces in the framework

Notice that the GrantedAuthority interface represents the permissions (or roles) that the current user has. This information is used by the AccessDecisionManager, the authorization responsible object, and determines whether an end user can access a resource (URL or method call or domain object). This object is not used for authentication

UserDetails

This interface specifies the fields owned by user details, such as username, password, account expiration, locked, and so on. In Spring Security, to get information about the currently logged in user, you typically need to extend this interface to connect to the user of your own system

See the source code

package org.springframework.security.core.userdetails;

import java.io.Serializable;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;

public interface UserDetails extends Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();

    String getPassword(a);

    String getUsername(a);

    boolean isAccountNonExpired(a);

    boolean isAccountNonLocked(a);

    boolean isCredentialsNonExpired(a);

    boolean isEnabled(a);
}
Copy the code

UserDetailsService

This interface provides only one interface loadUserByUsername(String username). This interface is very important. Generally, we extend this interface to display and obtain our user information. The username and password passed by the user when logging in is also verified by the username and password found here, but the real verification is not done here, but by the AuthenticationManager and AuthenticationProvider. It should be emphasized that if the user does not exist, Should not return NULL, rather than throwing an exception UsernameNotFoundException

See the source code

package org.springframework.security.core.userdetails;

public interface UserDetailsService {
    UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}
Copy the code

Spring Security Security identity authentication process

  1. The filter access to user name and password that encapsulated into Authentication, usually UsernamePasswordAuthenticationToken the implementation class.

  2. AuthenticationManager The identity manager is responsible for verifying this Authentication

  3. After successful Authentication, the AuthenticationManager identity manager returns an Authentication instance filled with information (including the permission information, identity information, details mentioned above, but passwords are usually removed).

  4. SecurityContextHolder security context container will step 3 to fill the information Authentication, through SecurityContextHolder. GetContext () setAuthentication () method, set up to it.

AuthenticationManager

If you are new to Spring Security, you will be trusted with AuthenticationManager, ProviderManager, AuthenticationProvider… There are so many similar Spring authentication classes that are confusing, but a little bit of combing will make their connections and designers’ intentions clear.

AuthenticationManager (interface) is the core interface related to authentication, and also the starting point for initiating authentication, because in practical requirements, we may allow users to log in using the user name + password, while allowing users to log in using email + password, mobile phone number + password, or even, May allow users to log in using their fingerprints (what else? It’s surprising), so AuthenticationManager generally doesn’t directly authenticate,

The common implementation of the AuthenticationManager interface, the ProviderManager class, internally maintains a List

for various authentication methods, which is actually an application of the Delegate pattern.

In other words, the core authentication entry is always only one: AuthenticationManager, different authentication modes: User name + password (UsernamePasswordAuthenticationToken), email + password, mobile phone number + password corresponding to the three AuthenticationProvider. So that makes a lot of sense

Populated UserDetails and UserDetailsService

UserDetails

The UserDetails interface, which is mentioned repeatedly above, represents the most detailed user information. This interface covers the necessary user information fields and is generally extended as necessary.

It is similar to the Authentication interface in that both of them have username, authorities, and differentiating them is one of the highlights of this article.

The getCredentials() in Authentication need to be treated differently from the getPassword() in UserDetails. The former are the password credentials submitted by the user, and the latter are the correct password of the user. The authenticator is actually a comparison between the two. The getAuthorities() of Authentication are really formed by the getAuthorities() of UserDetails. Remember the getUserDetails() method in the Authentication interface? The UserDetails UserDetails are populated after passing through the AuthenticationProvider.

UserDetailsService

The responsibilities of UserDetailsService and AuthenticationProvider are often confused. The UserDetailsService is only responsible for loading user information from a specific place, be it a database, redis cache, interface, etc

Obtain user information globally

  1. Get user information by injecting the Principal interface

During the running process, Spring will inject Username, Password, Authentication and Token into the Principal interface, which can be directly obtained and used in the Controller

   @GetMapping("/home")
    @apiOperation (" User center ")
    public Result getUserHome(Principal principal) {
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken=(UsernamePasswordAuthenticationToken)principal;
        return ResultResponse.success(usernamePasswordAuthenticationToken.getPrincipal());
    }
Copy the code
  1. Using @authenticationPrincipal to annotate the parameter
   @GetMapping("/home")
    @apiOperation (" User center ")
    public Result getUserHome(@AuthenticationPrincipal cn.soboys.kmall.security.entity.User user ) {
        return ResultResponse.success(user);
    }
Copy the code
  1. Global context retrieval

Since obtaining the username of the current user is a common requirement, Spring Security has already implemented this for us in the Authentication implementation class, so there are simpler ways to obtain the username of the current user

@restController public Class HelloController {@getMapping ("/hello") public String hello() {return "current login user: " + SecurityContextHolder.getContext().getAuthentication().getName(); }}Copy the code
  1. Get the current logged-in User’s UserDetails instance, and then convert it to the custom User entity class User to get the User’s ID and other information
@RestController
public class HelloController {
 
    @GetMapping("/hello")
    public String hello(a) {
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        User user = (User)principal;
        return "Current login user information:"+ user.toString(); }}Copy the code
  1. Get user information in asynchronous methods

Spring Security cannot, by default, get the current logged-in user’s name in a method using the @async annotation. In @ Async method to obtain the current logged in user, you need to call SecurityContextHolder. SetStrategyName methods and set up the relevant strategies