Spring Security-how authentication and authorization work

table of Contents


One, Spring Security principle

Jump to table of contents

  • It is based on 过滤器链intercepting the request sent by the user;

  • The problem solved by Spring Security is 安全访问控制that the security access control function is actuallyIntercept all requests entering the system and verify whether each request can access the resources it expects. According to the previous knowledge, it can be achieved through Filteror AOPother technologies. Spring Security's protection of web resources is achieved by Filter , so start with this Filter and gradually deepen the principles of Spring Security.

  • When initializing Spring Security, it creates a named SpringSecurityFilterChainServlet filter type org.springframework.security.web.FilterChainProxy, it implements javax.servlet.Filter, therefore such external requests through the lower figure is misplaced Spring Security chain structure of FIG.

Insert picture description here
上图:

  • UsernamePasswordAuthenticationFilter —> corresponds to the actual work AuthenenticationManager
  • FilterSecurityInterceptor —> corresponds to the actual work AccessDecisionManager

FilterChainProxy is a proxy. What really works is the filters contained in SecurityFilterChain in FilterChainProxy. At the same time, these Filters are managed by Spring as Beans. They are the core of Spring Security and each have their own responsibilities, but they do not directly handle user authentication. It does not directly deal with user authorization, but hands them over 认证管理器(AuthenticationManagerand 决策(授权)管理器 (AccessDecisionManager)processes them. The following figure is a UML diagram of FilterChainProxy related classes.

Insert picture description here
Insert picture description here

1. Spring Security certification process (source code tracking)

Jump to the directory
Insert picture description here
According to the above sequence diagram, find the two classes in the program, UsernamePasswordAuthenticationFilterand DaoAuthenticationProvideranalyze the above 时序图process through breakpoints ;

1, first into the login page, enter the correct account password zhangsan, 123
Insert picture description here
2, because after enter the account password, will enter into AbstractAuthenticationProcessingFilterthe doFiltermethod,
Insert picture description here
Insert picture description here

3. The authenticator (AuthenticationManager) is mainly entrusted DaoAuthenticationProviderto authenticate the user's account and password; find the retrieveUsermethod
Insert picture description here
of UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);this type. In this step, the userDetailsServicemethod we wrote will be called to retrieve the user's account and password from the database;
Insert picture description here
Insert picture description here
4. The account password in the database, proceed 匹配, will enter DaoAuthenticationProviderthe parent class to AbstractUserDetailsAuthenticationProviderperform the judgment operation.
Insert picture description here
Enter the additionalAuthenticationChecksmethod to match the account password.
Insert picture description here
Insert picture description here
At this time, the login is successful;


The general relationship of certification core components is as follows:
Insert picture description here

1、AuthenticationProvider

Jump to table of contents
Insert picture description here
Insert picture description here

2、UserDetailsService

Jump to table of contents

  • Meet UserDetailsService

Now we now know that DaoAuthenticationProvider handles the authentication logic of the web form. After successful authentication, an Authentication (implemented by UsernamePasswordAuthenticationToken) is obtained, which contains the identity information (Principal). This identity information is an Object, and in most cases it can be forced into an UserDetailsobject.

DaoAuthenticationProvider contains an UserDetailsServiceinstance, which is responsible for extracting user information UserDetails (including password) based on the user name, and then DaoAuthenticationProvider will compare whether the user password extracted by UserDetailsService matches the password submitted by the user as the key basis for successful authentication, so it can be passed from The defined UserDetailsService is exposed as a spring bean to define custom authentication.

Insert picture description here

  • Many people confuse the responsibilities of DaoAuthenticationProviderand UserDetailsService, in fact, UserDetailsService is only responsible from a specific place (usually a database) 加载用户信息, nothing more. The DaoAuthenticationProvider has a greater responsibility, it completes the complete, 认证流程and at the same time fills the UserDetails to Authentication.

Insert picture description here

  • It is very similar to the Authentication interface, for example, they all have username and authorities. Authentication's getCredentials() and UserDetails' getPassword() need to be treated differently. The former is the password credential submitted by the user, and the latter is the password actually stored by the user. Authentication is actually a comparison of the two. The getAuthorities() in Authentication is actually formed by the getAuthorities() of UserDetails. Remember the getDetails() method in the Authentication interface? The UserDetails user details are populated after authentication by the AuthenticationProvider.

  • By implementing UserDetailsService and UserDetails, we can complete the expansion of user information acquisition methods and user information fields.

  • InMemoryUserDetailsManager (memory authentication) provided by Spring Security, JdbcUserDetailsManager (jdbc authentication) is the implementation class of UserDetailsService, the main difference is nothing more than loading users from 内存or from 数据库.

Test:
Custom UserDetailsService

@Service
public class MyUserDetailService implements UserDetailsService {
    
    

    // 根据账号查询信息
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    
    

        // 将来连接数据库根据账号来查询用户信息
        // 现在先模拟
        System.out.println("username = " + username);
        UserDetails userDetails = User.withUsername("zhangsan1").password("123").authorities("p1").build();
        return userDetails;
    }
}

Shield the definition of UserDetailsService in the security configuration class

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    

    // 配置用户信息服务
//    @Bean
//    public UserDetailsService userDetailsService() {
    
    
//        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
//        manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
//        manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());
//        return manager;
//    }

Restart the project and request authentication. The loadUserByUsername method of MyUserDetailsService is called to query user information.

3、PasswordEncoder

Jump to the directory to
know PasswordEncoder

After the DaoAuthenticationProvider authentication processor obtains UserDetails through UserDetailsService, how does it compare with the password in the authentication request?

Here, in order to adapt to a variety of encryption types, Spring Security has made abstractions. DaoAuthenticationProvider matchescompares passwords through the PasswordEncoder interface method, and the specific password comparison details depend on the implementation:

Insert picture description here
And Spring Security provides many built-in PasswordEncoders, which can be used out of the box. To use a certain PasswordEncoder, you only need to make the following declaration, as follows

    @Bean
    public PasswordEncoder passwordEncoder() {
    
    
        return new BCryptPasswordEncoder();
    }

NoOpPasswordEncoder adopts string matching method and does not encrypt and compare passwords. The password comparison process is as follows:

1. The user enters the password (in plain text)
2. DaoAuthenticationProvider obtains UserDetails (which stores the correct password of the user)
3. DaoAuthenticationProvider uses PasswordEncoder to verify the entered password and the correct password. If the password is consistent, the verification passes, otherwise the verification fails. .

  • The verification rule of NoOpPasswordEncoder compares the entered password with the correct password in UserDetails. If the 字符串string content is consistent, the verification passes, otherwise the verification fails.

  • It is recommended to use in actual projects BCryptPasswordEncoder, Pbkdf2PasswordEncoder, SCryptPasswordEncoder, etc. If you are interested, you can take a look at the specific implementation of these PasswordEncoders

Use BCryptPasswordEncoder
Insert picture description here
2 to test BCrypt

@SpringBootTest
public class TestBCrypt {
    
    

    @Test
    public void testBCrypt() {
    
    
        // 对密码进行加密
        String hashpw = BCrypt.hashpw("123", BCrypt.gensalt());
        String hashpw2 = BCrypt.hashpw("456", BCrypt.gensalt());
        System.out.println("hashpw = " + hashpw);
        System.out.println("hashpw2 = " + hashpw2);

        // 校验密码
        boolean checkpw1 = BCrypt.checkpw("123", "$2a$10$QfQYXOtc/2oSgiuYi.9x6.8VcFZ4RuQOq7WmzwkkhXoiD.hB5swP.");
        boolean checkpw2 = BCrypt.checkpw("123", "$2a$10$ptyf4yyfbc1oL.OJPfSKMOk.hO4eRS1SQj44MBhhHnSZFrphjGHK.");
        System.out.println("checkpw1 = " + checkpw1);
        System.out.println("checkpw2 = " + checkpw2);
    }
}

3. Modify the security configuration class

/**
 * Description: 安全配置的内容包括:用户信息、密码编码器、安全拦截机制。
 *
 * @author zygui
 * @date Created on 2020/7/22 15:11
 */
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    

    // 配置用户信息服务
    @Bean
    public UserDetailsService userDetailsService() {
    
    
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("zhangsan").password("$2a$10$QfQYXOtc/2oSgiuYi.9x6.8VcFZ4RuQOq7WmzwkkhXoiD.hB5swP.").authorities("p1").build());
        manager.createUser(User.withUsername("lisi").password("$2a$10$LYa/9GkXYzhc/UjD7S/D5OWE2F7RXHVgANsDHC4XSp8OiEfi1Fk4e").authorities("p2").build());
        return manager;
    }

    // 对密码进行编码, 使用不加密的对比
//    @Bean
//    public PasswordEncoder passwordEncoder() {
    
    
//        return NoOpPasswordEncoder.getInstance();
//    }
    @Bean
    public PasswordEncoder passwordEncoder() {
    
    
        return new BCryptPasswordEncoder();
    }

    // 配置安全拦截机制


    //安全拦截机制(最重要)
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
    
        http.authorizeRequests()
                .antMatchers("/r/r1").hasAuthority("p1")
                .antMatchers("/r/r2").hasAuthority("p2")
                .antMatchers("/r/**").authenticated()//所有/r/**的请求必须认证通过
                .anyRequest().permitAll()//除了/r/**,其它的请求可以访问
                .and()
                .formLogin()//允许表单登录
                .successForwardUrl("/login-success");//自定义登录成功的页面地址

    }
}

Three, Spring Security authorization process (source code tracking)

Go to the directory
through the quick start we know, Spring Security through http.authorizeRequests()to the web request to authorize protection. Spring Security uses the standard Filter to establish the interception of web requests, and finally realizes authorized access to resources.

The authorization process of Spring Security is as follows:
Insert picture description here
In the Decide()method in AccessDecisiontManager 拿到当前访问资源所需要的权限信息and 用户信息中的权限信息compared, if it meets, the authorization is successful;
Insert picture description here

Insert picture description here
The core interface of AccessDecisionManager (Access Decision Manager) is as follows:

public interface AccessDecisionManager {
    
    
    void decide(Authentication var1, Object var2, Collection<ConfigAttribute> var3) throws AccessDeniedException, InsufficientAuthenticationException;

    boolean supports(ConfigAttribute var1);

    boolean supports(Class<?> var1);
}

Here we will focus on the parameters of decide:

  • authentication: the identity of the visitor who wants to access the resource
  • object: the protected resource to be accessed, the web request corresponds to FilterInvocation
  • configAttributes: is the access policy of protected resources, obtained through SecurityMetadataSource.

The decide interface is used to identify whether the current user has the permission to access the corresponding protected resource.

1. Authorization decision

Jump to the directory
AccessDecisionManager uses 投票the method to determine whether the protected resource can be accessed.

Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
注意:The default is to use AffirmativeBased way

Enter this class and break the point under the decide method
Insert picture description here

Insert picture description here

Guess you like

Origin blog.csdn.net/m0_37989980/article/details/107519382