天天看點

UsernamePasswordAuthenticationFilter執行流程

SpringSecurity是在攔截器Interceptor前面的。

SpringSecurity走完,如果項目中設定了攔截器,還會走攔截器的。

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
           

添加依賴後,啟動項目,通路任意接口都會跳轉到登入頁

UsernamePasswordAuthenticationFilter執行流程

輸入賬号,密碼,程式會先執行到

UsernamePasswordAuthenticationFilter

的attemptAuthentication方法

// UsernamePasswordAuthenticationFilter.class
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response){
        if (this.postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            String username = this.obtainUsername(request);		// 從HttpServletRequest中拿到傳來的的使用者名參數
            username = username != null ? username : "";
            username = username.trim();
            String password = this.obtainPassword(request);		// 從HttpServletRequest中拿到傳來的的密碼參數
            password = password != null ? pasword : "";
            // 封裝 Authentication
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            this.setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);	// 試圖認證
        }
    }
           

進入到

ProviderManager

這個類當中的authenticate方法。

之後調用

DaoAuthenticationProvider

繼承的其父類

public abstract class AbstractUserDetailsAuthenticationProvider

的authenticate方法。

之後進入到

DaoAuthenticationProvider

這個類中retrieveUser方法。

protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) {
        this.prepareTimingAttackProtection();

        try {	// 根據使用者名在系統中查詢使用者資訊
            UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
            if (loadedUser == null) {
                throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
            } else {
                return loadedUser;
            }
        } catch (Exception var6) {
            throw new InternalAuthenticationServiceException(var6.getMessage(), var6);
        }
    }
           

之後又進入到

public class InMemoryUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService

這個類中的loadUserByUsername方法

public UserDetails loadUserByUsername(String username){
    // 根據使用者名在系統中查詢使用者資訊
    // InMemory是預設記憶體中存着相應的使用者資訊
        UserDetails user = (UserDetails)this.users.get(username.toLowerCase());
        if (user == null) {
            throw new UsernameNotFoundException(username);
        } else {
			// 封裝傳回
            return new User(user.getUsername(), user.getPassword(), user.isEnabled(), 
            user.isAccountNonExpired(), user.isCredentialsNonExpired(), 
            user.isAccountNonLocked(), user.getAuthorities());
        }
    }
           

查詢到的話,封裝為UserDetails,又層層傳回至

AbstractUserDetailsAuthenticationProvider

類中的authenticate方法。

之後又進入到

DaoAuthenticationProvider

類中的additionalAuthenticationChecks方法。

// userDetails是在系統中查到的使用者資訊, authentication是登入時填寫的資訊進行封裝
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication){
        if (authentication.getCredentials() == null) {
            this.logger.debug("Failed to authenticate since no credentials provided");
            throw new BadCredentialsException(this.messages
            .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        } else {
        // 比對密碼
            String presentedPassword = authentication.getCredentials().toString();
            if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
                this.logger.debug("Failed to authenticate since password does not match stored value");
                throw new BadCredentialsException(this.messages
                .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
            }
        }
    }
           

比對成功之後,又傳回到

AbstractUserDetailsAuthenticationProvider

類中的authenticate方法,調用

DaoAuthenticationProvider

類中的createSuccessAuthentication方法

将從系統中查到的UserDetails資訊(改名為principal),以及authentication(使用者登陸時填的資訊),以及一個MutableUserDetails(将從系統中查到的UserDetails資訊,将其中的密碼進行Bcrpt加密,封裝為MutableUserDetails對象)這三者進行整合,最後封裝資訊,傳回一個UsernamePasswordAuthenticationToken。