天天看點

spring security 5 (4)-認證流程

本篇介紹登入時的認證流程,及其相關的重要概念接口等,現在隻需要有一個基本概念,以後将以此為基礎介紹自定義配置,到時結合本篇會更加清晰。

SecurityContext

使用者登入時,會将使用者相關資訊組裝成一個Authentication對象,而SecurityContext的主要作用就是儲存Authentication。并且 SecurityContext會通過session來維持狀态,是以登入後每次請求都可以從session中擷取目前使用者Authentication,這是系統内部實作的。我們可直接使用系統封裝好的方法來擷取目前使用者Authentication對象,如下

SecurityContextHolder.getContext().getAuthentication();
           

Authentication

這個Authentication也是第2篇表達示中的authentication,其源碼如下

public interface Authentication extends Principal, Serializable {
	Collection<? extends GrantedAuthority> getAuthorities();//使用者權限集合
	Object getCredentials();//密碼類參數
	Object getDetails();    //其他登入參數
	Object getPrincipal();//使用者資訊,如使用者名
	boolean isAuthenticated();//認證狀态:是否認證過
	void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;//設定認證狀态
           
  • 第3篇的UserDetails,就是這裡的getPrincipal(),使用者登入後,通過以下方法就可以擷取到UserDetails。
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
           
  • Object類型表明,你在自定義配置實作時自由度非常高。UserDetails隻是系統預設的getPrincipal()類型,在自定義配置中可以是任何類型,以後會講。
  • getDetails()是使用者/密碼以外的額外參數,根據你的業務需要而定,比如登入時可能還會使用驗證碼參數。
  • isAuthenticated用于辨別目前Authentication是否認證過,下面會講。
  • UsernamePasswordAuthenticationToken是Authentication的預設實作類,了解下其源碼會更清晰。

AuthenticationManager

使用者登入時,首先生成的Authentication是未認證狀态,交由AuthenticationManager認證。AuthenticationManager會将Authentication中的使用者名/密碼與UserDetails中的使用者名/密碼對比,完成認證工作,認證成功後會生成一個已認證狀态的Authentication,此時就會寫入到SecurityContext。在使用者有後續請求時,可從Authentication中檢查權限

認證流程

以下例子出自官方文檔,這是一個最簡化的認證流程,而且省略了UserDetails,隻做了簡單認證。

public class AuthenticationExample {
	private static AuthenticationManager am = new SampleAuthenticationManager();

	public static void main(String[] args) throws Exception {
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

		while (true) {
                        //模拟輸入使用者名密碼
			System.out.println("Please enter your username:");
			String name = in.readLine();
			System.out.println("Please enter your password:");
			String password = in.readLine();
			try {
				//根據使用者名/密碼,生成未認證Authentication
				Authentication request = new UsernamePasswordAuthenticationToken(name, password);
                                //交給AuthenticationManager 認證
				Authentication result = am.authenticate(request);
				//将已認證的Authentication放入SecurityContext
				SecurityContextHolder.getContext().setAuthentication(result);
				break;
			} catch (AuthenticationException e) {
				System.out.println("Authentication failed: " + e.getMessage());
			}
		}
		System.out.println("Successfully authenticated. Security context contains: "
				+ SecurityContextHolder.getContext().getAuthentication());
	}
}
//認證類
class SampleAuthenticationManager implements AuthenticationManager {
        //配置一個簡單的使用者權限集合
	static final List<GrantedAuthority> AUTHORITIES = new ArrayList<GrantedAuthority>();
	static {
		AUTHORITIES.add(new SimpleGrantedAuthority("ROLE_USER"));
	}

	public Authentication authenticate(Authentication auth) throws AuthenticationException {
                //如果使用者名和密碼一緻,則登入成功,這裡隻做了簡單認證
		if (auth.getName().equals(auth.getCredentials())) {
			//認證成功,生成已認證Authentication,比未認證多了權限
			return new UsernamePasswordAuthenticationToken(auth.getName(), auth.getCredentials(), AUTHORITIES);
		}
		throw new BadCredentialsException("Bad Credentials");
	}
}
           

認證過濾器

main方法中的代碼就是模拟認證過濾器做的事,當認證過濾器攔截到/login登入請求,就會開始執行。

繼續閱讀