你還不瞭解SpringSecurity嗎?快來看看SpringSecurity實戰總結~
阿新 • • 發佈:2022-01-03
SpringSecurity簡介:
許可權管理中的相關概念
主體 principal:
使用系統的使用者或裝置或從其他系統遠端登入的使用者等等,簡單說就是誰使用系統誰就是主體。
認證 authentication:
許可權管理系統確認一個主體的身份,允許主體進入系統。簡單說就是“主體”證明自己是誰。
授權 authorization:
將作業系統的“權力”“授予”“主體”,這樣主體就具備了作業系統中特定功能的能力。 所以簡單來說,授權就是給使用者分配許可權。
SpringSecurity本質是過濾器鏈:
客戶端發起一個請求,在請求到達Controller前Security通過一系列的過濾處理,完成對使用者的認證授權等相關處理。需要注意的是在傳統業務系統開發中,我們一般會在UserController中實現一個login介面來處理使用者登入,但在使用Security時不需要在Controller實現login介面,它幫助我們在過濾器中實現了使用者密碼登入。預設通過UsernamePasswordAuthenticationFilter過濾器實現,從表單中讀取使用者名稱密碼進行認證登入。
主要過濾器
ExceptionTranslationFilter:異常處理過濾器,凡是在過濾器環節出現的錯誤都或轉到該過濾器進行統一處理
UsernamePasswordAuthenticationFilter:預設的認證過濾器,從請求中讀取表單使用者名稱密碼資料進行校驗,如果是JSON提交需要對其進行重寫
FilterSecurityInterceptor:是一個方法級的許可權過濾器, 基本位於過濾鏈的最底部
自定義新增過濾器
除了SpringSecurity預設的過濾器,我們還可以新增自己的過濾器來進行自定義認證或授權。在SpringBoot開發中,SpringSecurity自動配置會向容器中自動注入相關過濾器,因此如果自己也有過濾器的情況下回導致過濾器順序混亂,建議通過SpringSecurity統一管理過濾器。
如下,我們在自己的配置類中重寫configure(HttpSecurity http)配置方法,自定義新增自己的過濾器
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
org.springframework.security.web.context.SecurityContextPersistenceFilter
org.springframework.security.web.header.HeaderWriterFilter
org.springframework.security.web.csrf.CsrfFilter
org.springframework.security.web.authentication.logout.LogoutFilter
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter
org.springframework.security.web.savedrequest.RequestCacheAwareFilter
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
org.springframework.security.web.authentication.AnonymousAuthenticationFilter
org.springframework.security.web.session.SessionManagementFilter
org.springframework.security.web.access.ExceptionTranslationFilter
org.springframework.security.web.access.intercept.FilterSecurityInterceptor
1 @Configuration //告訴SpringBoot該類是一個配置類,自動裝配到IOC容器 2 @EnableWebSecurity //全域性開啟SpringSecurity 3 @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) //開啟許可權註解,之後會介紹 4 public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 5 @Override 6 protected void configure(HttpSecurity http) throws Exception { 7 //新增自定義過濾器在某過濾器前執行 8 http.addFilterBefore(wrapperFilter, UsernamePasswordAuthenticationFilter.class); 9 //新增自定義過濾器在某過濾器之後 10 http.addFilterAfter(filter,UsernamePasswordAuthenticationFilter.class); 11 //新增過濾器在最後 12 http.addFilter(filter); 13 } 14 }配置常用元件: WebSecurityConfigurerAdapter(Security配置介面卡) UsernamePasswordAuthentcationFilter(使用者名稱密碼認證過濾器) UserDetailService(使用者許可權資料查詢服務) TokenRepository(記住我Token Dao) PasswordEncoder(NoOpPasswordEncoder、BCryptPasswordEncoder加密方式) HttpSecutity常用配置: Remember(記住我):
1 .rememberMe() 2 .tokenRepository(tokenRepository) //記住我的token管理Dao,操作資料庫的記住我快取 3 .tokenValiditySeconds(60) //token的有效時長formLogin(表單登入):
1 .formLogin() 2 .loginPage("/login_page") //登陸頁面 3 .loginProcessingUrl("/login") //登陸請求處理介面,Spring security預設的處理登入介面是/login這個自帶的介面 4 .usernameParameter("name") //指定使用者名稱引數名稱 5 .passwordParameter("passwd") //指定密碼引數名稱 6 .permitAll() //將登入操作url放行authorizeRequests(url請求許可權):
.authorzeRequests() .antMatchers("/admin/**") .hasRole("ADMIN") //具備特定角色可訪問 .antMatchers("/user/**") .access("hasAnyRole('ADMIN','USER')") //引數以表示式方式書寫,多個以 and 連線 //除了.permitAll()的url,其餘所有URL需要認證後訪問 .anyRequest() .authenticated()
logout(登出):
1 .logout() //開啟登出登陸 2 .logoutUrl("/logout") //登出登陸請求url 3 .clearAuthentication(true) //清除身份資訊 4 .invalidateHttpSession(true) //session失效 5 .addLogoutHandler(new LogoutHandler() { //登出處理 6 @Override 7 public void logout(HttpServletRequest req, 8 HttpServletResponse resp, 9 Authentication auth) { 10 11 } 12 }) 13 .logoutSuccessHandler(new LogoutSuccessHandler() { //登出成功處理 14 @Override 15 public void onLogoutSuccess(HttpServletRequest req, 16 HttpServletResponse resp, 17 Authentication auth) 18 throws IOException { 19 resp.sendRedirect("/login_page"); //跳轉到自定義登陸頁面 20 } 21 })csrf(跨站指令碼偽造):
1 .csrf().disabl(); //前後端分離時一般關閉
RemenberMe記住我(自動登入)原理: 總結配置流程- 認證使用者實現UserDetails介面
- 使用者來源的Service實現UserDetailsService介面,實現loadUserByUsername()方法,從資料庫中獲取資料
- 實現自己的過濾器繼承UsernamePasswordAuthenticationFilter,重寫attemptAuthentication()和successfulAuthentication()方法實現自己的邏輯
- Spring Security的配置類繼承自WebSecurityConfigurerAdapter,重寫裡面的兩個config()方法
- 如果使用RSA非對稱加密,就準備好RSA的配置類,然後在啟動類中加入註解將其加入IOC容器
- securedEnable=true 開啟@Securd註解
- prePostEnable=true 開啟@Preauthorize@PostAuthorize註解
1 @Service 2 public class MethodService { 3 @Secured("ROLE_ADMIN") //訪問此方法需要ADMIN角色 4 public String admin() { 5 return "hello admin"; 6 } 7 @PreAuthorize("hasRole('ADMIN') and hasRole('DBA')") //訪問此方法需要ADMIN且DBA 8 public String dba() { 9 return "hello dba"; 10 } 11 @PreAuthorize("hasAnyRole('ADMIN','DBA','USER')") //三個都行 12 public String user() { 13 return "user"; 14 } 15 }引數過濾 @PostFilter:對返回值進行過濾 @PreFilter:對方法引數進行過濾 用法: @PostFilter("filterObject.屬性==xx"):過濾物件屬性,當表示式為true時允許資料通過,為false的資料會被過濾掉 開起security 註解 @EnableWebSecurity在 Spring boot 應用中使用 Spring Security,用到了 @EnableWebSecurity註解,官方說明為,該註解和 @Configuration 註解一起使用, 註解 WebSecurityConfigurer 型別的類,或者利用@EnableWebSecurity 註解繼承 WebSecurityConfigurerAdapter的類,這樣就構成了 Spring Security 的配置。 單點登入相關 SpringSecurity總結理解: SpringSecurity的配置非常靈活可拓展性很強,因此使用該安全元件可以完成很多自定義配置。首先分析專案中的幾個要素:認證路徑(登入處理)、需要放行的路徑,需要授權的路徑、是否使用session、是否禁用csrf、配置跨域過濾器、是否自定義認證成功後失敗的邏輯處理、使用者認證失敗或許可權不足的響應邏輯。 就以我現在的專案分析: 對於前後端分離的專案首先要考慮到禁用session和csrf因為我們用jwt token來解決http的無狀態性,需要注意的是禁用session後SecurityContextHolder會失效,因為它預設依賴session來進行上下文處理。 然後對於靜態資源的url放行處理,和對公開訪問介面的放行處理,例如登入處理介面、註冊介面、驗證碼獲取介面等。做完這些放行處理之後,再對剩下所有的url進行認證攔截處理。 再然後就需要做對於token認證授權的一些適配配置問題進行自定義處理,比如登入成功後我們需要將使用者的token存入redis,那麼這個邏輯可以通過實現UsernamePasswordAuthenticationFilter然後重寫successfulAuthentication和unsuccessfulAuthentication方法,在successfulAuthentication方法中我們可以自定義登入成功後的處理邏輯,如token處理和security上下文處理等。unsuccessfulAuthentication一般就返回失敗就行了。注意我們自己實現的UsernamePasswordAuthenticationFilter想讓他生效需要在配置中讓他替換原來的通過http.addFilterAt(myUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); 還有一種方法通過http.formLogin().successHandler().failureHandler();來設定認證成功和失敗的處理器; 注意,如果我們使用token來維護狀態,那麼每個請求都是需要通過token來鑑權的,所以我們還需要寫一個過濾器來維護token校驗邏輯,寫一個過濾器獲取請求中的token做校驗邏輯,校驗處理成功後我們還可以將使用者資料新增到SecurityContextHolder中,以便在後續處理中獲取當前請求使用者資料資訊。 還有一個需要注意的地方,關於使用者的認證校驗邏輯,一般我們通過實現UsernamePasswordAuthenticationFilter並重寫attemptAuthentication方法進行認證校驗。還有一種方法是不實現UsernamePasswordAuthenticationFilter,而是提供一個我們的身份提供者,一個實現了AuthenticationProvider介面的類,然後將認證校驗邏輯解除安裝authenticate()方法中,並且通過authenticationProvider()方式進行配置。 由過濾器鏈組成,通過配置可以開啟或關閉特定過濾器,其中認證過濾器最終都會建立一個對應的XXXToken,例如UsernamePasswordAuthentication會建立一個UsernamePasswordAuthenticationToken。建立好了之後通過實現了authenticationManager介面的ProviderManager遍歷所有實現了AuthenticationProvider身份提供者進行身份校驗邏輯。注意每個ProviderManager都只處理自己關聯的Token,如RememberMeAuthenticationProvider只處理RememberMeAuthenticationToken。當遍歷到其中一個ProviderManager能夠成功驗證時使用者就認證成功了。