1. 程式人生 > >Spring Security驗證流程剖析及自定義驗證方法

Spring Security驗證流程剖析及自定義驗證方法

com UNC title 具體實現 hold any pan imp uil

Spring Security本質上是一連串的Filter, 然後又以一個獨立的Filter的形式插入到Filter Chain裏,其名為FilterChainProxy。 如圖所示。

技術分享圖片

實際上FilterChainProxy下面可以有多條Filter Chain,來針對不同的URL做驗證,而Filter Chain中所擁有的Filter則會根據定義的服務自動增減。所以無需要顯示再定義這些Filter,除非想要實現自己的邏輯。

技術分享圖片

關鍵類

Authentication

Authentication是一個接口,用來表示用戶認證信息,在用戶登錄認證之前相關信息會封裝為一個Authentication

具體實現類的對象,在登錄認證成功之後又會生成一個信息更全面,包含用戶權限等信息的Authentication對象,然後把它保存在 SecurityContextHolder所持有的SecurityContext中,供後續的程序進行調用,如訪問權限的鑒定等。

AuthenticationManager

用來做驗證的最主要的接口為AuthenticationManager,這個接口只有一個方法:

public interface AuthenticationManager {

  Authentication authenticate(Authentication authentication)
    throws AuthenticationException;

}

其中authenticate()方法運行後可能會有三種情況:

  1. 驗證成功,返回一個帶有用戶信息的Authentication
  2. 驗證失敗,拋出一個AuthenticationException異常。
  3. 無法判斷,返回null

ProviderManager

ProviderManager是上面的AuthenticationManager最常見的實現,它不自己處理驗證,而是將驗證委托給其所配置的AuthenticationProvider列表,然後會依次調用每一個 AuthenticationProvider進行認證,這個過程中只要有一個AuthenticationProvider

驗證成功,就不會再繼續做更多驗證,會直接以該認證結果作為ProviderManager的認證結果。

技術分享圖片

認證過程

  1. 用戶使用用戶名和密碼進行登錄。
  2. Spring Security將獲取到的用戶名和密碼封裝成一個Authentication接口的實現類,比如常用的UsernamePasswordAuthenticationToken
  3. 將上述產生的Authentication對象傳遞給AuthenticationManager的實現類ProviderManager進行認證。
  4. ProviderManager依次調用各個AuthenticationProvider進行認證,認證成功後返回一個封裝了用戶權限等信息的Authentication對象。
  5. AuthenticationManager返回的Authentication對象賦予給當前的SecurityContext

自定義驗證

有了以上的知識儲備後就可以來自定義驗證方法了。通過上面可以看出,實際上真正來做驗證操作的是一個個的AuthenticationProvider,所以如果要自定義驗證方法,只需要實現一個自己的AuthenticationProvider然後再將其添加進ProviderManager裏就行了。

自定義AuthenticationProvider

@Component
public class CustomAuthenticationProvider
  implements AuthenticationProvider {
 
    @Override
    public Authentication authenticate(Authentication authentication) 
      throws AuthenticationException {
  
        String name = authentication.getName();
        String password = authentication.getCredentials().toString();
         
        if (shouldAuthenticateAgainstThirdPartySystem()) {
  
            // use the credentials
            // and authenticate against the third-party system
            return new UsernamePasswordAuthenticationToken(
              name, password, new ArrayList<>());
        } else {
            return null;
        }
    }
 
    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(
          UsernamePasswordAuthenticationToken.class);
    }
}

其中的supports()方法接受一個authentication參數,用來判斷傳進來的authentication是不是該AuthenticationProvider能夠處理的類型。

註冊AuthenticationProvider

現在再將剛創建的AuthenticationProvider與ProviderManager裏註冊,所有操作就完成了。

@Configuration
@EnableWebSecurity
@ComponentScan("org.baeldung.security")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
    @Autowired
    private CustomAuthenticationProvider authProvider;
 
    @Override
    protected void configure(
      AuthenticationManagerBuilder auth) throws Exception {
  
        auth.authenticationProvider(authProvider);
    }
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated()
            .and()
            .httpBasic();
    }
}

Spring Security驗證流程剖析及自定義驗證方法