1.Spring Security流程解釋
流程圖
SpringSecurity 采用的是責任鏈的設計模式,它有一條很長的過濾器鏈
流程大致解釋
客戶端發起一個請求進入security的過濾鏈,將 Security 上下文異步映射繼承之后 儲存在SecurityContextPersistenceFilte中
走到登錄判斷之后,登出則是logoutHandler成功的話會到logoutSuccessHandler失敗ExceptionTranslationFilter,如果不是登出的話則會進入下個過濾器
首先DefaultLoginPageGeneratingFilter處會查詢有么有配置的登錄頁面如果有則跳入配置的沒有則跳入默認的
然后判斷是否為登錄請求,如果是則進入UsernamePasswordAuthenticationFilter,如果登錄失敗則到 AuthenticationFailureHandler 登錄失敗處理器處理,如果登錄成功則到AuthenticationSuccessHandler 登錄成功處理器處理,如果不是登錄請求則不進入該過濾器
具體參數解釋
- WebAsyncManagerIntegrationFilter:將 Security 上下文與 Spring Web 中用於處理異步請求映射的 WebAsyncManager 進行集成。
- SecurityContextPersistenceFilter:在每次請求處理之前將該請求相關的安全上下文信息加載到 SecurityContextHolder 中,然后在該次請求處理完成之后,將 SecurityContextHolder 中關於這次請求的信息存儲到一個“倉儲”中,然后將 SecurityContextHolder 中的信息清除,例如在Session中維護一個用戶的安全信息就是這個過濾器處理的。
- HeaderWriterFilter:用於將頭信息加入響應中。
- CsrfFilter:用於處理跨站請求偽造。
- LogoutFilter:用於處理退出登錄。
- DefaultLoginPageGeneratingFilter:如果沒有配置登錄頁面,那系統初始化時就會配置這個過濾器,並且用於在需要進行登錄時生成一個登錄表單頁面
- UsernamePasswordAuthenticationFilter:用於處理基於表單的登錄請求,從表單中獲取用戶名和密碼。默認情況下處理來自 /login 的請求。從表單中獲取用戶名和密碼時,默認使用的表單 name 值為 username 和 password,這兩個值可以通過設置這個過濾器的usernameParameter 和 passwordParameter 兩個參數的值進行修改。 (后續代碼中需要實現userDetailsService和passwordEncoder)
- BasicAuthenticationFilter:檢測和處理 http basic 認證
- RequestCacheAwareFilter:用來處理請求的緩存
- SecurityContextHolderAwareRequestFilter:主要是包裝請求對象request
- AnonymousAuthenticationFilter:檢測 SecurityContextHolder 中是否存在 Authentication 對象,如果不存在為其提供一個匿名 Authentication
- SessionManagementFilter:管理 session 的過濾器
- ExceptionTranslationFilter:處理 AccessDeniedException 和 AuthenticationException 異常
- FilterSecurityInterceptor:可以看做過濾器鏈的出口
- RememberMeAuthenticationFilter:當用戶沒有登錄而直接訪問資源時, 從 cookie 里找出用戶的信息, 如果 Spring Security 能夠識別出用戶提供的remember me cookie, 用戶將不必填寫用戶名和密碼, 而是直接登錄進入系統,該過濾器默認不開啟。
2.java配置
(1)導入xml
一般導入第一個就行了,第二個是將原本放入內存的session放入redis中 (可配可不配)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency>
(2)實現配置
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth){} @Override public void configure(WebSecurity web){} @Override protected void configure(HttpSecurity http) throws Exception {} }
(3)解釋
1. configure(WebSecurity web)這個配置方法用於配置靜態資源的處理方式,可使用 Ant 匹配規則。負責http web安全
configure(AuthenticationManagerBuilder auth)身份驗證管理生成器,AuthenticationManager 的建造器,配置 AuthenticationManagerBuilder 會讓Security 自動構建一個 AuthenticationManager(該類的功能參考流程圖);如果想要使用該功能你需要配置一個 UserDetailService 和 PasswordEncoder。UserDetailsService 用於在認證器中根據用戶傳過來的用戶名查找一個用戶, PasswordEncoder 用於密碼的加密與比對,我們存儲用戶密碼的時候用PasswordEncoder.encode() 加密存儲,在認證器里會調用 PasswordEncoder.matches() 方法進行密碼比對。如果重寫了該方法,Security 會啟用 DaoAuthenticationProvider 這個認證器,該認證就是先調用 UserDetailsService.loadUserByUsername 然后使用 PasswordEncoder.matches() 進行密碼比對,如果認證成功成功則返回一個 Authentication 對象。
3. configure(HttpSecurity http)HTTP請求安全處理,這個是最重要的
http.formLogin().loginPage("/login") .usernameParameter("username") .passwordParameter("password") .loginProcessingUrl("/sing_in").permitAll();
這個表示配置了以表單方式提交,登錄頁為login,登錄用戶名和密碼,還有登錄請求路徑,並且支持全部用戶訪問
http .authorizeRequests() .antMatchers("/test").hasRole("test") .anyRequest().authenticated() .accessDecisionManager(accessDecisionManager());
權限相關的配置,配置了一個 /test的url 該有什么權限才能訪問, anyRequest() 表示所有請求,authenticated() 表示已登錄用戶才能訪問, accessDecisionManager() 表示綁定在 url 上的鑒權管理器
所以權限的配置自由度很高,鑒權器可以綁定在任意url上,還可以硬編碼任意url
登出和登出成功處理器
http .logout() .logoutUrl("/logout") .logoutSuccessHandler(new MyLogoutSuccessHandler())
鑒權失敗的處理器
http .exceptionHandling() .accessDeniedHandler(new MyAccessDeniedHandler());
插入自定義過濾器。addFilterBefore 加在對應的過濾器之前,addFilterAfter 加在對應的過濾器之后,addFilterAt 加在過濾器同一位置
框架原有的 Filter 在啟動 HttpSecurity 配置的過程中,都由框架完成了其一定程度上固定的配置,是不允許更改替換的。根據測試結果來看,調用 addFilterAt 方法插入的 Filter ,會在這個位置上的原有 Filter 之前執行。
http.addFilterAfter(new MyFittler(), LogoutFilter.class); http.addFilterAt(getAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class);