SpringSecurity認證基本原理與認證2種方式
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強雜湊方法 每次加密的結果都不一樣,所以更加的安全。