Small knowledge, big challenge! This paper is participating in theEssentials for programmers”Creative activities.
Writing in the front
Above we talk about the user authentication system of the user system, today we talk about the authentication system, this article will take you to understand, Security is how to authenticate!
One more word, welcome to click on my avatar, to check the Security column, we study together!! The design patterns topic has been completed, and the Security and concurrent queues columns will be completed next.
The authentication object
With the user object mentioned above, we can discuss the specific Authentication process. First, we will look at the Authentication object, as shown below:
public interface Authentication extends Principal.Serializable {
// Permissions that security principals have
Collection<? extends GrantedAuthority> getAuthorities();
// The subject is valid
Object getCredentials(a);
// Details of authentication requests
Object getDetails(a);
// The identity information of the subject
Object getPrincipal(a);
// Whether the authentication succeeds
boolean isAuthenticated(a);
// Set the authentication result
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
Copy the code
The authentication object represents the authentication request itself and holds details of the various entities involved in the request’s access to the application.
In the security world, the user requesting access to the application is often referred to as the Principal, and Authentication extends an interface of the same name that exists in the JDK.
Obviously, Authentication only represents the Authentication request itself, and the specific Authentication process and logic need to be responsible for the special component, this component is the AuthenticationProvider, defined as follows:
public interface AuthenticationProvider {
// Perform the authentication and return the authentication result
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
// Determine whether the current authentication object is supported
boolean supports(Class
authentication);
}
Copy the code
At this point, you might think that Spring Security does user authentication directly using the AuthenticationProvider interface, but it doesn’t. If you look through the source code for Spring Security, you will see that it uses the AuthenticationManager interface to proxy the authentication functionality provided by AuthenticationProvider
The InMemoryUserDetailsManager changePassword, for example, we analysis the implementation process of user authentication
public void changePassword(String oldPassword, String newPassword) {
Authentication currentUser = SecurityContextHolder.getContext()
.getAuthentication();
if (currentUser == null) {
throw new AccessDeniedException(
"Can't change password as no Authentication object found in context "
+ "for current user.");
}
String username = currentUser.getName();
if(authenticationManager ! =null) {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
username, oldPassword));
}
else{... } MutableUserDetails user = users.get(username);if (user == null) {
throw new IllegalStateException("Current user doesn't exist in database.");
}
user.setPassword(newPassword);
}
Copy the code
The code was cropped to show it.
You can see from above that AuthenticationManager is used instead of the Authenticate () method in AuthenticationProvider to perform authentication. At the same time, we also noticed that there appeared a UsernamePasswordAuthenticationToken class, this is a specific Authentication interface implementation class, used to store user name and password needed for user Authentication information
See here small partners in the mind should probably have some understanding of it. So how do we customize the user authentication scheme
User-defined authentication schemes
From the previous analysis, we have made it clear that the implementation process of user information storage can actually be customized. All Security does is embed common implementations in the framework that conform to common business scenarios. If there are special scenarios, developers can fully implement custom user information storage schemes.
We now know that the UserDetails interface represents the UserDetails, and the UserDetailsService interface is responsible for various operations on the UserDetails. Therefore, the implementation of a customized user authentication scheme mainly involves the implementation of UserDetails and UserDetailsService interfaces
The way to extend UserDetails is to implement the interface directly. For example, we could build a SpringUser class like this:
public class SpringUser implements UserDetails {
private static final long serialVersionUID = 1L;
private Long id;
private final String username;
private final String password;
private final String phoneNumber;
/ / omit getter/setter
@Override
public String getUsername(a) {
return username;
}
@Override
public String getPassword(a) {
return password;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
}
@Override
public boolean isAccountNonExpired(a) {
return true;
}
@Override
public boolean isAccountNonLocked(a) {
return true;
}
@Override
public boolean isCredentialsNonExpired(a) {
return true;
}
@Override
public boolean isEnabled(a) {
return true; }}Copy the code
Obviously, this is the easiest way to meet the implementation requirements of the various interfaces in UserDetails. Once we have built such a SpringUser class, we can create the corresponding table structure to store the fields defined in the class
We can also extend the UserDetailsService
@Service
public class SpringUserDetailsService
implements UserDetailsService {
@Autowired
private SpringUserRepository repository;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
SpringUser user = repository.findByUsername(username);
if(user ! =null) {
return user;
}
throw new UsernameNotFoundException(
"SpringUser '" + username + "' not found"); }}Copy the code
We know that the UserDetailsService interface has only one loadUserByUsername method to implement. Therefore, we query data from the database based on the user name based on SpringUserRepository’s findByUsername method.
We can also extend the AuthenticationProvider
The process of extending AuthenticationProvider is to provide a custom AuthenticationProvider implementation class. The following describes the steps for customizing user name and password authentication
It’s not a very good drawing, but it’s very simple, and it should help you remember it.
First, we need to get a UserDetails object using the UserDetailsService, and then match the password in the object with the password in the authentication request. If the password is the same, the authentication succeeds, and if the password is the same, a BadCredentialsException is thrown. The sample code looks like this:
@Component
public class SpringAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication authentication) {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
UserDetails user = userDetailsService.loadUserByUsername(username);
if (passwordEncoder.matches(password, user.getPassword())) {
return new UsernamePasswordAuthenticationToken(username, password, u.getAuthorities());
} else {
throw new BadCredentialsException("The username or password is wrong!"); }}@Override
public boolean supports(Class
authenticationType) {
returnauthenticationType.equals(UsernamePasswordAuthenticationToken.class); }}Copy the code
Here we used the same UsernamePasswordAuthenticationToken to deliver user name and password, verify password and use a PasswordEncoder object.
Finally, let’s integrate this custom configuration
Integrate custom configurations
We create a SpringSecurityConfig class, the class inherited WebSecurityConfigurerAdapter configuration class. This time, we will use the customized SpringUserDetailsService to store and query user information, which requires some adjustments to the original configuration policy. The full SpringSecurityConfig class after adjustment looks like this:
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService springUserDetailsService;
@Autowired
private AuthenticationProvider springAuthenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(springUserDetailsService) .authenticationProvider(springAuthenticationProvider); }}Copy the code
This is the class you’ll probably see most in your projects.
-
Here we injected SpringUserDetailsService and SpringAuthenticationProvider and add it to the AuthenticationManagerBuilder
-
Such AuthenticationManagerBuilder will be done based on the custom SpringUserDetailsService populated UserDetails creation and management
-
Based on the custom SpringAuthenticationProvider complete the user authentication.
OK, that’s all for today’s certification stuff, basically. That’s all you need to know. I don’t think I need to go any further.
conclusion
This article focuses on the implementation of customized user authentication scheme by extending UserDetailsService and AuthenticationProvider interface. I hope you can go down and type in the code and make sense of it, and we’ll talk about it in the next video. Come on, study together!
overtones
Thank you for reading, if you feel that you have learned something, please like, follow. Also welcome to have a question we comment below exchange
Come on! See you next time!
To share with you a few I wrote in front of a few SAO operation
Talk about different strategy patterns (Bookmarks)
Copy object, this operation is a little SAO!