在認證過程和訪問受權前必須瞭解spring Security如何知道咱們要求全部用戶都通過身份驗證? Spring Security如何知道咱們想要支持基於表單的身份驗證?所以必須瞭解WebSecurityConfigurerAdapter配置類如何工做的。並且也必須瞭解清楚filter的順序,才能更好了解其調用工做流程。web
在使用WebSecurityConfigurerAdapter前,先了解Spring security config。
Spring security config具備三個模塊,一共有3個builder,認證相關的AuthenticationManagerBuilder和web相關的WebSecurity、HttpSecurity。redis
HttpSecurity:具體的權限控制規則配置。一個這個配置至關於xml配置中的一個標籤。各類具體的認證機制的相關配置,OpenIDLoginConfigurer、AnonymousConfigurer、FormLoginConfigurer、HttpBasicConfigurer等。spring
WebSecurityConfigurerAdapter提供了簡潔方式來建立WebSecurityConfigurer,其做爲基類,可經過實現該類自定義配置類,主要重寫這三個方法:express
protected void configure(AuthenticationManagerBuilder auth) throws Exception {} public void configure(WebSecurity web) throws Exception {} protected void configure(HttpSecurity httpSecurity) throws Exception {}
並且其自動從SpringFactoriesLoader查找AbstractHttpConfigurer讓咱們去擴展,想要實現必須建立一個AbstractHttpConfigurer的擴展類,並在classpath路徑下建立一個文件META-INF/spring.factories。例如:api
org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyClassThatExtendsAbstractHttpConfigurer跨域
其源碼分析:緩存
//1.init初始化:獲取HttpSecurity和配置FilterSecurityInterceptor攔截器到WebSecurity public void init(final WebSecurity web) throws Exception { //獲取HttpSecurity final HttpSecurity http = getHttp(); //配置FilterSecurityInterceptor攔截器到WebSecurity web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() { public void run() { FilterSecurityInterceptor securityInterceptor = http .getSharedObject(FilterSecurityInterceptor.class); web.securityInterceptor(securityInterceptor); } }); } ...... //2.獲取HttpSecurity的過程 protected final HttpSecurity getHttp() throws Exception { if (http != null) { return http; } DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor .postProcess(new DefaultAuthenticationEventPublisher()); localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher); AuthenticationManager authenticationManager = authenticationManager(); authenticationBuilder.parentAuthenticationManager(authenticationManager); Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects(); http = new HttpSecurity(objectPostProcessor, authenticationBuilder, sharedObjects); if (!disableDefaults) { // 默認的HttpSecurity的配置 http //添加 CSRF 支持,使用WebSecurityConfigurerAdapter時,默認啓用,禁用csrf().disable() .csrf().and() //添加WebAsyncManagerIntegrationFilter .addFilter(new WebAsyncManagerIntegrationFilter()) //容許配置異常處理 .exceptionHandling().and() //將安全標頭添加到響應 .headers().and() //容許配置會話管理 .sessionManagement().and() //HttpServletRequest之間的SecurityContextHolder建立securityContext管理 .securityContext().and() //容許配置請求緩存 .requestCache().and() //容許配置匿名用戶 .anonymous().and() //HttpServletRequestd的方法和屬性註冊在SecurityContext中 .servletApi().and() //使用默認登陸頁面 .apply(new DefaultLoginPageConfigurer<>()).and() //提供註銷支持 .logout(); // @formatter:on ClassLoader classLoader = this.context.getClassLoader(); List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader); for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) { http.apply(configurer); } } configure(http); return http; } ... //3.可重寫方法實現自定義的HttpSecurity protected void configure(HttpSecurity http) throws Exception { logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity)."); http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin().and() .httpBasic(); } ....
從源碼init初始化模塊中的「獲取HttpSecurity」和「配置FilterSecurityInterceptor攔截器到WebSecurity」中能夠看出,想要spring Security如何知道咱們要求全部用戶都通過身份驗證? Spring Security如何知道咱們想要支持基於表單的身份驗證?只要重寫protected void configure(HttpSecurity http) throws Exception方法便可。所以咱們須要理解HttpSecurity的方法的做用,如何進行配置。下一節來討論HttpSecurity。安全
HttpSecurity基於Web的安全性容許爲特定的http請求進行配置。其有不少方法,列舉一些經常使用的以下表:cookie
方法 | 說明 | 使用案例 |
---|---|---|
csrf() | 添加 CSRF 支持,使用WebSecurityConfigurerAdapter時,默認啓用 | 禁用:csrf().disable() |
openidLogin() | 用於基於 OpenId 的驗證 | openidLogin().permitAll(); |
authorizeRequests() | 開啓使用HttpServletRequest請求的訪問限制 | authorizeRequests().anyRequest().authenticated() |
formLogin() | 開啓表單的身份驗證,若是未指定FormLoginConfigurer#loginPage(String),則將生成默認登陸頁面 | formLogin().loginPage("/authentication/login").failureUrl("/authentication/login?failed") |
oauth2Login() | 開啓OAuth 2.0或OpenID Connect 1.0身份驗證 | authorizeRequests()..anyRequest().authenticated()..and().oauth2Login() |
rememberMe() | 開啓配置「記住我」的驗證 | authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin().permitAll().and().rememberMe() |
addFilter() | 添加自定義的filter | addFilter(new CustomFilter()) |
addFilterAt() | 在指定filter相同位置上添加自定義filter | addFilterAt(new CustomFilter(), UsernamePasswordAuthenticationFilter.class) |
addFilterAfter() | 在指定filter位置後添加自定義filter | addFilterAfter(new CustomFilter(), UsernamePasswordAuthenticationFilter.class) |
requestMatchers() | 開啓配置HttpSecurity,僅當RequestMatcher相匹配時開啓 | requestMatchers().antMatchers("/api/**") |
antMatchers() | 其能夠與authorizeRequests()、RequestMatcher匹配,如:requestMatchers().antMatchers("/api/**") | |
logout() | 添加退出登陸支持。當使用WebSecurityConfigurerAdapter時,這將自動應用。默認狀況是,訪問URL」/ logout」,使HTTP Session無效來清除用戶,清除已配置的任何#rememberMe()身份驗證,清除SecurityContextHolder,而後重定向到」/login?success」 | logout().deleteCookies("remove").invalidateHttpSession(false).logoutUrl("/custom-logout").logoutSuccessUrl("/logout-success"); |
HttpSecurity還有不少方法供咱們使用,去配置HttpSecurity。因爲太多這邊就不一一說明,有興趣可去研究。session
## 3. WebSecurityConfigurerAdapter使用
WebSecurityConfigurerAdapter示例:
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MyFilterSecurityInterceptor myFilterSecurityInterceptor; protected void configure(HttpSecurity http) throws Exception { http //request 設置 .authorizeRequests() //http.authorizeRequests() 方法中的自定義匹配 .antMatchers("/resources/**", "/signup", "/about").permitAll() // 指定全部用戶進行訪問指定的url .antMatchers("/admin/**").hasRole("ADMIN") //指定具備特定權限的用戶才能訪問特定目錄,hasRole()方法指定用戶權限,且不需前綴 「ROLE_「 .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")// .anyRequest().authenticated() //任何請求沒匹配的都須要進行驗證 .and() //login設置 自定義登陸頁面且容許全部用戶登陸 .formLogin() .loginPage("/login") //The updated configuration specifies the location of the log in page 指定自定義登陸頁面 .permitAll(); // 容許全部用戶訪問登陸頁面. The formLogin().permitAll() 方法 .and .logout() //logouts 設置 .logoutUrl("/my/logout") // 指定註銷路徑 .logoutSuccessUrl("/my/index") //指定成功註銷後跳轉到指定的頁面 .logoutSuccessHandler(logoutSuccessHandler) //指定成功註銷後處理類 若是使用了logoutSuccessHandler()的話, logoutSuccessUrl()就會失效 .invalidateHttpSession(true) // httpSession是否有效時間,若是使用了 SecurityContextLogoutHandler,其將被覆蓋 .addLogoutHandler(logoutHandler) //在最後增長默認的註銷處理類LogoutHandler .deleteCookies(cookieNamesToClear);//指定註銷成功後remove cookies //增長在FilterSecurityInterceptor前添加自定義的myFilterSecurityInterceptor http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class); }
NOTE:此示例只供參考
Spring Security filter順序:
Filter Class | 說明 |
---|---|
ChannelProcessingFilter | 訪問協議控制過濾器,可能會將咱們從新定向到另一種協議,從http轉換成https |
SecurityContextPersistenceFilter | 建立SecurityContext安全上下文信息和request結束時清空SecurityContextHolder |
ConcurrentSessionFilter | 併發訪問控制過濾器,主要功能:SessionRegistry中獲取SessionInformation來判斷session是否過時,從而實現併發訪問控制。 |
HeaderWriterFilter | 給http response添加一些Header |
CsrfFilter | 跨域過濾器,跨站請求僞造保護Filter |
LogoutFilter | 處理退出登陸的Filter |
X509AuthenticationFilter | 添加X509預受權處理機制支持 |
CasAuthenticationFilter | 認證filter,通過這些過濾器後SecurityContextHolder中將包含一個徹底組裝好的Authentication對象,從而使後續鑑權能正常執行 |
UsernamePasswordAuthenticationFilter | 認證的filter,通過這些過濾器後SecurityContextHolder中將包含一個徹底組裝好的Authentication對象,從而使後續鑑權能正常執行。表單認證是最經常使用的一個認證方式。 |
BasicAuthenticationFilter | 認證filter,通過這些過濾器後SecurityContextHolder中將包含一個徹底組裝好的Authentication對象,從而使後續鑑權能正常執行 |
SecurityContextHolderAwareRequestFilter | 此過濾器對ServletRequest進行了一次包裝,使得request具備更加豐富的API |
JaasApiIntegrationFilter | (JAAS)認證方式filter |
RememberMeAuthenticationFilter | 記憶認證處理過濾器,便是若是前面認證過濾器沒有對當前的請求進行處理,啓用了RememberMe功能,會從cookie中解析出用戶,並進行認證處理,以後在SecurityContextHolder中存入一個Authentication對象。 |
AnonymousAuthenticationFilter | 匿名認證處理過濾器,當SecurityContextHolder中認證信息爲空,則會建立一個匿名用戶存入到SecurityContextHolder中 |
SessionManagementFilter | 會話管理Filter,持久化用戶登陸信息,能夠保存到session中,也能夠保存到cookie或者redis中 |
ExceptionTranslationFilter | 異常處理過濾器,主要攔截後續過濾器(FilterSecurityInterceptor)操做中拋出的異常。 |
FilterSecurityInterceptor | 安全攔截過濾器類,獲取當前請求url對應的ConfigAttribute,並調用accessDecisionManager進行訪問受權決策。 |
spring security的默認filter鏈:
SecurityContextPersistenceFilter ->HeaderWriterFilter ->LogoutFilter ->UsernamePasswordAuthenticationFilter ->RequestCacheAwareFilter ->SecurityContextHolderAwareRequestFilter ->SessionManagementFilter ->ExceptionTranslationFilter ->FilterSecurityInterceptor
在上節咱們已分析了核心的filter源碼以及功能。可回看上節源碼分析更加深刻的瞭解各個filter工做原理。
在認證和訪問受權過程前,首先必須進行WebSecurityConfigurer符合自身應用的security Configurer,也要清楚filter鏈的前後順序,才能更好理解spring security的工做原理以及在項目中出現的問題定位。瞭解完準備工做,接下來將展開對認證和訪問受權模塊的工做流程研究以及項目示例分析。最後若有錯誤可評論告知。
最後可關注公衆號:【Ccww筆記】 一塊兒學習,天天會分享乾貨,還有學習視頻領取!