1. 程式人生 > >Spring :SpringSecurity初探

Spring :SpringSecurity初探

文章目錄

什麼是Spring Security

是個安全框架,就是用來驗證身份和許可權的那種。這個框架使用十分廣泛,但是並不是很好理解,我在公司的新專案中就需要使用這個框架進行使用者身份的驗證和識別,並且這個需求要與JWT進行整合使用。

什麼是JWT

JWT是Json Web Token的簡稱,我比較喜歡叫他使用者令牌,使用者在訪問一些需要授權的地址的時候就需要在http的header上面攜帶Token,這樣伺服器就能夠認識這個使用者,並且瞭解他所擁有的許可權。

Token的來源就是使用者的登入,使用者在登入的時候,伺服器在驗證了使用者名稱和密碼之後就會生成一個Token。

JWT包含這些部分:頭部,明文負載,加密簽名。這三部分被編碼為Base64的形式,之間使用點.連起來,是長長的一串,通過Base64解碼可以直接得到前兩部分的具體內容,一般會包括一些類似於簽發人,加密演算法等相關內容,伺服器可以通過這些內容驗證一個使用者的身份。

使用JJWT庫可以很方便的生成一個JWT,這個後續在詳細說明。

Spring Security工作流程

在用了一下之後,粗略的整理了一個思維導圖,是這樣的:
Spring Security的結構

首先,使用者傳送了一個請求,請求被攔截器攔截,如果是登陸地址的攔截器,他就會將使用者名稱和密碼封裝到一個物件,然後轉交AuthenticationManager通過authenticate方法認證,但是Token可能有很多種,AuthenticationManager並不瞭解如何認證這些物件,所以他會尋找可以處理此物件的Provider,Provider同樣實現了authentic方法,這個方法就是用來驗證Token身份和許可權的方法,如果驗證成功,那麼他將會返回一個已經驗證的Token。

基礎使用方式


實現Token

Token就是在SpringSecurity中用來驗證的東西,一般是Authentication介面的實現類,這個介面有這些方法:

  • Collection<? extends GrantedAuthority> getAuthorities();
    角色,就是使用者在系統中的身份,例如管理員,工作人員,遊客等,這些一般就決定了使用者會有哪些許可權
  • Object getCredentials();
    一個可以驗證身份的東西,可以是密碼,也可以是別的。
  • Object getDetails();
    一些附加資料,常會用來放置附加了賬號許可權資訊的東西
  • Object getPrincipal();
    使用者的簡要資料
  • boolean isAuthenticated();
    是否認證通過
  • void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
    設定Token的狀態,一般是建立的時候為false,認證成功後為true

可以自己實現一個此介面的類,也可以繼承Spring Security中給出的其他Token,總之,按照實際情況的需要來編寫它。這次的工程中,我在這個工程中的做法是繼承了AbstractAuthenticationToken這個類,然後自己增加了一些需要的欄位。

實現Provider

有了一個Token,還需要一共支援此Token的AuthenticationProvider,這個是一個介面,直接實現它就可以。

介面有兩個方法,分別是

public Authentication authenticate(Authentication authentication) 
		throws AuthenticationException 

這個方法用於驗證Token,如果Token沒有問題,應該返回一個驗證完畢,含有使用者資訊的Token物件。

public boolean supports(Class<?> authentication)

這個方法是確定此Provider適用於那個Token,因此只需要對Token的class進行判斷,然後返回true或false就可以。

實現Principal

這是一個簡要的使用者身份資料,它也是一個介面,就是一些getter和Setter,把需要的資料欄位手動加到上面就可以。

實現Filter

這裡說的filter主要分兩種型別,一個是用於登入的LoginFilter,一個是用來驗證的Filter。

登入用的Filter可以繼承AbstractAuthenticationProcessingFilter得到,這是一個抽象類,需要重新編寫構造方法

構造方法:(SecurityService helper,AuthenticationManager manager)
這裡面需要制定此Filter攔截的路徑,通過父類的構造可以做到這一點,例如:

super(new AntPathRequestMatcher("/public/user/login","POST"));

另外一個方法是用來驗證使用者身份的:

public Authentication attemptAuthentication(HttpServletRequest request
		, HttpServletResponse response)

還有這個,在認證成功的時候這個方法會被呼叫的。

	protected void successfulAuthentication(HttpServletRequest request, 
	HttpServletResponse response, FilterChain chain,
	Authentication authResult) throws IOException, ServletException

在成功驗證了使用者名稱和密碼後,就應該簽發一個可以證明使用者身份的東西,這個東西使用者以後訪問系統都會攜帶在Http的請求頭中,系統就可以通過這個來確定使用者的身份以及許可權。這就需要另外一個Filter來完成了。

這裡我通過繼承BasicAuthenticationFilter這個Filter完成Token的驗證,需要重寫的方法是這樣的:

protected void doFilterInternal(HttpServletRequest request, 
							HttpServletResponse response, FilterChain chain)
							throws IOException, ServletException 

這個是Filter的過濾方法,可以通過request得到Http的請求頭,然後在那裡面尋找Token,如果有Token就要驗證下,沒有就讓他繼續doFilter。

Filter中的異常

安全驗證過程中出現異常是很常見的,但是這裡的異常物件是無法被ControllerAdvice以及ExceptionHandler捕獲和處理的,一旦異常就會出現非常大的一堆報錯資訊,對於後續的除錯非常不友好。

所以還是Try - Catch吧,木有很好的辦法。

可以自己定義一些在安全驗證中常出現的異常,做好log的同時丟擲,就可以在Filter中有針對性的輸出或者做些其他的處理。

配置Security到SpringBoot

通過繼承WebSecurityConfigurerAdapter可以得到一個用來配置SpringSecurity的Config類,首先在這裡配置可以直接訪問的URL和需要通過身份驗證的URL:

這裡需要兩個Filter,就在這個config中直接把他們定義為Bean就可以了(使用@Bean就可以做到)。

上述兩個Filter是需要一個AuthenticationManager的,這個Manager就在config中,但是如果直接啟動,Spring會找不到他,所以可以這樣做:

	@Bean
	@Override
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return super.authenticationManagerBean();
	}

這樣AuthenticationManager也就成為了一個Bean了。

@Override
protected void configure(HttpSecurity http) throws Exception {
	http.authorizeRequests()		// 開始配置Request的URL
		.antMatchers("/public/**")  //  匹配此路徑的
		.permitAll()				// 無需認證直接可以訪問
		.formLogin()				// 登入頁面
		.loginPage("/public/user/login").permitAll()  // 無需認證直接訪問
		.authorizeRequests()		 // 其他地址需要認證
		.anyRequest().authenticated()
		.addFilterBefore(LoginFilter()	// 新增Filter
		,UsernamePasswordAuthenticationFilter.class)
		.addFilterBefore(TokenFilter(),
		,UsernamePasswordAuthenticationFilter.class);  
}

這裡面permitAll允許訪問,denyAll禁止訪問,authenticated指的是要求認證,不認證身份會跳到登入介面。

然後需要在這裡注入之前實現的Provider,在這個方法中把它註冊到SpringSecurity中:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.authenticationProvider(provider);
}

到此,SpringSecurity配置就全部結束了。