9.3 攔截請求
對每個請求進行細粒度安全性控制的關鍵在於重載configure(HttpSecurity) 方法。如下代碼中的重載configure,爲不同的URL路徑有選擇性的應用安全性
@Override
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests()
.antMatchers("/test1").authenticated() //對路徑認證
.antMatchers(HttpMethod.POST,"/test2").authenticated() //對路徑的POST請求認證
.anyRequest().permitAll(); //方通其他路徑請求,不需要認證和任何的權限
}
除了路徑選擇,還可以通過authenticated() 和 permitAll() 來定義該如何保護路徑。authenticated()要求在執行該請求時,必須已經登錄了應用。如果用戶沒有認證的話,Spring Security的Filter將會捕獲該請求,並將用戶重定向到應用的登錄界面。
其他的用來定義如何保護請求的方法
方法 | 能夠做什麼 |
---|---|
access(String) | 如果給定的SpEL表達式計算結果爲true,就允許訪問 |
anonymous() | 允許匿名訪問 |
authenticated() | 允許認證過的用戶訪問 |
denyAll() | 無條件拒絕訪問 |
fullyAuthenticated() | 如果用戶是完整認證的話(不是通過Remember-me功能認證的),就允許訪問 |
hasAnyAuthority(String…) | 如果用戶具備給定權限的某一個的話,就允許訪問 |
hashAnyRole(String…) | 如果用戶具備給定角色的某一個的話,就允許訪問 |
hashAuthority(String) | 如果用戶具備給定權限的話,就允許訪問 |
hasIpAddress(String) | 如果請求來自給定IP地址的話,就允許訪問 |
hasRole(String) | 如果用戶具備給定角色的話,就允許訪問 |
not() | 對其他訪問方法的結果求反 |
permitAll() | 無條件訪問 |
rememberMe() | 如果用戶是通過Remember-me功能認證的,就允許訪問 |
可以將任意數量的 antMatchers()、regexMatchers()和anyRequest()連接起來,以滿足Web應用安全規則的需要,但是!這些規則會按照給定的順序發揮作用。所以 要將最爲具體的請求路徑放在前面,而最不具體的路徑(如anyRequest())放在最後面。
9.3.1 SpringEL進行安全保護
SpringEL具有強大的多維度保護機制。它能支持很多SpEL表達式
.antMatchers("/test1").access("hasRole('ROLE_USER')") //具有ROLE_USER角色才能訪問的url
安全表達式 | 計算結果 |
---|---|
authentication | 用戶的認證對象 |
denyAll | 結果始終爲false |
hasAnyRole(list of roles) | 如果用戶被授予了列表中的指定角色,結果爲true |
hashRole(role) | 如果用戶被授予了指定的角色,結果爲true |
hasIpAddress(IP Address) | 如果請求來自指定IP的話,結果爲true |
isAnonymous() | 如果當前用戶爲匿名用戶,結果爲true |
isAuthenticated() | 如果當前用戶進行了認證,結果爲true |
ifFullyAuthenticated() | 如果當前用戶進行了完整的認證的話,結果爲true |
isRememberMe() | 如果當前用戶是通過Remember-me自動認證的,結果爲true |
permitAll | 結果始終爲true |
principal | 用戶的principal對象 |
.antMatchers("/test1")
.access("hashRole('ROLE_USER') and hasIpAddress('192.168.1.1') ") //指定權限且來自指定的IP
9.3.2 強制通道的安全性
藉助requiresChannel()方法,能夠爲各種URL模式聲明所要求的通道。
@Override
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests()
.antMatchers("/test1").hasRole("USER")//對路徑認證
.antMatchers(HttpMethod.POST,"/test2").hasRole("USER") //對路徑的POST請求認證
.anyRequest().permitAll() //方通其他路徑請求,不需要認證和任何的權限
.and()
.requiresChannel()
.antMatchers("/test1/test3").requiresSecure(); //需要HTTPS
//.antMatchers("/").requiresInecure(); 始終通過HTTP傳送
}
此時,只要是對 /test1/test3 的請求,Spring Security都視爲需要安全通道並自動將請求重定向到HTTPS上。
9.3.3 防止跨站請求僞造
跨站請求僞造(CSRF):如果一個站點欺騙用戶提交請求到其他服務器,即CSRF攻擊。
不去處理的方式:httpSecurity.csfr().disable(); //禁用CSRF防護功能
9.4 認證用戶
9.4.1 添加自定義的登錄頁
9.4.2 啓用HTTP Basic認證
HTTP Basic認證(HTTP Basic Authentication)會直接通過HTTP請求本身,對訪問應用程序的用戶進行認證。在REST客戶端向它使用的服務進行認證的場景中,這種方式比較適合。
@Override
protected void configure(HttpSecurity http) throws Exception{
http
.csrf().disable()
.formLogin() //啓用默認的登錄頁
.and()
.httpBasic().realmName("Spittr") //啓用HTTP Basic認證
....
}
9.4.3 啓用Remember-me
只要登陸過一次,應用程序就會記住你,當再次回到應用的時候就不需要登錄了,啓用方式:在configure()方法傳入的HttpSecurity對象上調用rememberMe( )即可。
@Override
protected void configure(HttpSecurity http) throws Exception{
http
.csrf().disable()
.formLogin() //啓用默認的登錄頁
.and()
.httpBasic().realmName("Spittr") //啓用HTTP Basic認證
.and()
.rememberMe()
.tokenValiditySeconds(2419200)
.key("spitterKey")
....
}
默認情況下,這個功能是通過在cookie中存儲一個token完成的,這個token最多兩週內有效。但是,在這裏,我們制定token最多四周內有效(2419200秒)。存儲在cookie中的token包含用戶名、密碼、過期時間和一個私鑰—在寫入cookie前都進行了MD5哈希。默認情況下,私鑰的名爲SpringSecured,可以自己命名使它用於Spittr應用。
9.4.4 退出
退出功能是通過Servlet容器中的Filter實現的(默認情況下),這個Filter會攔截針對"/logout"的請求。因此只要發起對"/logout"的請求,就會被Spring Security的LogoutFilter所處理,用戶會退出應用,所有的Remember-me token都會被清除掉。在退出完成後,用戶瀏覽器將會重定向到"/login?logout",從而允許用戶進行再次登錄。
可以通過在configure()配置,重定向指定頁面
@Override
protected void configure(HttpSecurity http) throws Exception{
http
.formLogon()
.and()
.logout()
.logoutSuccessUrl("/") //重定向到"/"
.logoutUrl("/signout") //重寫默認的LogoutFilter攔截路徑
}