Spring Security功能的實現主要是由一系列過濾器相互配合完 成。也稱之為過濾器鏈
,Spring Security默認加載15個過濾器, 但是隨着配置可以增加或者刪除一些過濾器.
一、過濾器鏈介紹
過濾器是一種典型的AOP思想,下面簡單了解下這些過濾器鏈,后續再源碼剖析中在涉及到過濾器鏈在 仔細講解
1.org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
根據請求封裝獲取WebAsyncManager,從WebAsyncManager獲取/注冊的安全上下文可調 用處理攔截器
2.org.springframework.security.web.context.SecurityContextPersistenceFilter
SecurityContextPersistenceFilter主要是使用SecurityContextRepository在session中保存 或更新一個SecurityContext,並將SecurityContext給以后的過濾器使用,來為后續fifilter 建立所需的上下文。SecurityContext中存儲了當前用戶的認證以及權限信息。
3.org.springframework.security.web.header.HeaderWriterFilter
向請求的Header中添加相應的信息,可在http標簽內部使用security:headers來控制
4.org.springframework.security.web.csrf.CsrfFilter
csrf又稱跨域請求偽造,SpringSecurity會對所有post請求驗證是否包含系統生成的csrf的 token信息,如果不包含,則報錯。起到防止csrf攻擊的效果。
5.org.springframework.security.web.authentication.logout.LogoutFilter
匹配URL為/logout的請求,實現用戶退出,清除認證信息。
6.org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
表單認證操作全靠這個過濾器,默認匹配URL為/login且必須為POST請求
7.org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
如果沒有在配置文件中指定認證頁面,則由該過濾器生成一個默認認證頁面。
8.org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter
由此過濾器可以生產一個默認的退出登錄頁面
9.org.springframework.security.web.authentication.www.BasicAuthenticationFilter
此過濾器會自動解析HTTP請求中頭部名字為Authentication,且以Basic開頭的頭信息
10.org.springframework.security.web.savedrequest.RequestCacheAwareFilter
通過HttpSessionRequestCache內部維護了一個RequestCache,用於緩存 HttpServletRequest
11. org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
針對ServletRequest進行了一次包裝,使得request具有更加豐富的API
12.org.springframework.security.web.authentication.AnonymousAuthenticationFilter
當SecurityContextHolder中認證信息為空,則會創建一個匿名用戶存入到 SecurityContextHolder中。spring security為了兼容未登錄的訪問,也走了一套認證流程, 只不過是一個匿名的身份。
13. org.springframework.security.web.session.SessionManagementFilter
securityContextRepository限制同一用戶開啟多個會話的數量
14.org.springframework.security.web.access.ExceptionTranslationFilter
異常轉換過濾器位於整個springSecurityFilterChain的后方,用來轉換整個鏈路中出現的異 常
15. org.springframework.security.web.access.intercept.FilterSecurityInterceptor
獲取所配置資源訪問的授權信息,根據SecurityContextHolder中存儲的用戶信息來決定其 是否有權限。
二、認證方式
2.1 HttpBasic認證
HttpBasic登錄驗證模式是Spring Security實現登錄驗證最簡單的一種方式,也可以說是最簡陋 的一種方式。它的目的並不是保障登錄驗證的絕對安全,而是提供一種“防君子不防小人”的登錄驗 證。
在使用的Spring Boot早期版本為1.X版本,依賴的Security 4.X版本,那么就無需任何配置,啟動 項目訪問則會彈出默認的httpbasic認證。現在使用的是spring boot2.0以上版本(依賴Security 5.X版本),HttpBasic不再是默認的驗證模式,在spring security 5.x默認的驗證模式已經是表單 模式。
HttpBasic模式要求傳輸的用戶名密碼使用Base64模式進行加密。如果用戶名是 "admin" , 密碼是“ admin”,則將字符串"admin:admin" 使用Base64編碼算法加密。加密結果可能是: YWtaW46YWRtaW4=。HttpBasic模式真的是非常簡單又簡陋的驗證模式,Base64的加密算法是 可逆的,想要破解並不難
2.2 formLogin登錄認證模式
Spring Security的HttpBasic模式,該模式比較簡單,只是進行了通過攜帶Http的Header進行 簡單的登錄驗證,而且沒有定制的登錄頁面,所以使用場景比較窄。對於一個完整的應用系統,與 登錄驗證相關的頁面都是高度定制化的,非常美觀而且提供多種登錄方式。這就需要Spring Security支持我們自己定制登錄頁面, spring boot2.0以上版本(依賴Security 5.X版本)默認會生 成一個登錄頁面.
2.3 表單認證
2.3.1 在config包下編寫SecurityConfiguration配置類
/**
* @author wuzhixuan
* @version 1.0.0
* @ClassName SecurityConfiguration.java
* @Description Security配置類
* @createTime 2021年11月24日 16:35:00
*/
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
/*http.httpBasic() // 開啟base驗證
.and().authorizeRequests().anyRequest().authenticated(); //所有請求都需要登錄認證才能訪問*/
http.formLogin().loginPage("/toLoginPage") // 開啟表單驗證
.and().authorizeRequests()
.antMatchers("/toLoginPage").permitAll() // 放行當前請求
.anyRequest().authenticated(); //所有請求都需要登錄認證才能訪問;
}
}
重啟服務發現css,js等靜態文件沒有成功加載
2.3.2 解決靜態資源被攔截問題
@Override
public void configure(WebSecurity web) throws Exception {
//解決靜態資源被攔截的問題
web.ignoring().antMatchers("/css/**", "/js/**", "/images/**", "/favicon.ico");
}
Spring Security 中,安全構建器 HttpSecurity 和 WebSecurity 的區別是 :
- WebSecurity 不僅通過 HttpSecurity 定義某些請求的安全控制,也通過其他方式定義其他某些 請求可以忽略安全控制;
- HttpSecurity 僅用於定義需要安全控制的請求(當然 HttpSecurity 也可以指定某些請求不需要 安全控制);
- 可以認為 HttpSecurity 是 WebSecurity 的一部分, WebSecurity 是包含 HttpSecurity 的更大 的一個概念;
- 構建目標不同
- WebSecurity 構建目標是整個 Spring Security 安全過濾器 FilterChainProxy`,
- HttpSecurity 的構建目標僅僅是 FilterChainProxy 中的一個 SecurityFilterChain 。
改造登錄
protected void configure(HttpSecurity http) throws Exception {
/*http.httpBasic() // 開啟base驗證
.and().authorizeRequests().anyRequest().authenticated(); //所有請求都需要登錄認證才能訪問*/
http.formLogin() // 開啟表單驗證
.loginPage("/toLoginPage") // 自定義登錄頁面
.loginProcessingUrl("/login") // 登錄請求url
.usernameParameter("username") // 修改自定義表單name值
.passwordParameter("password")
.successForwardUrl("/") // 登錄成功跳轉的路徑
.and().authorizeRequests()
.antMatchers("/toLoginPage").permitAll() // 放行當前請求
.anyRequest().authenticated(); //所有請求都需要登錄認證才能訪問;
// 關閉csrf防護
http.csrf().disable();
// 允許iframe加載頁面
http.headers().frameOptions().sameOrigin();
}
發現行內框架iframe這里出現問題了. Spring Security下,X-Frame-Options默認為DENY,非Spring Security環境下,X-Frame-Options的默認大多也是DENY,這種情況下,瀏覽器拒絕當前頁面加載任何 Frame頁面,設置含義如下:
- DENY:瀏覽器拒絕當前頁面加載任何Frame頁面 此選擇是默認的.
- SAMEORIGIN:frame頁面的地址只能為同源域名下的頁面
設置之后看到成功展示了
2.4 基於數據庫實現認證功能
之前我們所使用的用戶名和密碼是來源於框架自動生成的, 那么我們如何實現基於數據庫中的用戶名和 密碼功能呢? 要實現這個得需要實現security的一個UserDetailsService
接口, 重寫這個接口里面 loadUserByUsername
即可
- 編寫MyUserDetailsService並實現UserDetailsService接口,重寫loadUserByUsername方法
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username);// 用戶名沒有找到
}
// 先聲明一個權限集合, 因為構造方法里面不能傳入null
Collection<? extends GrantedAuthority> authorities = new ArrayList<>();
return new org.springframework.security.core.userdetails.User(username,
"{noop}" + user.getPassword(),// {noop}表示不加密認證
true, // 用戶是否啟用 true 代表啟用
true,// 用戶是否過期 true 代表未過期
true,// 用戶憑據是否過期 true 代表未過期
true,// 用戶是否鎖定 true 代表未鎖定
authorities
);
}
}
- 在SecurityConfiguration配置類中指定自定義用戶認證
/**
* 身份驗證管理器
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
2.5 密碼加密驗證
在基於數據庫完成用戶登錄的過程中,我們所是使用的密碼是明文的,規則是通過對密碼明文添加 {noop} 前綴。那么下面 Spring Security 中的密碼編碼進行一些探討。
Spring Security 中 PasswordEncoder 就是我們對密碼進行編碼的工具接口。該接口只有兩個功能: 一個是匹配驗證。另一個是密碼編碼。
- BCrypt算法介紹
BCrypt強哈希方法 每次加密的結果都不一樣,所以更加的安全。