日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

Spring Security 6.0:深度剖析其核心實現(xiàn)與工作原理

來源: 責(zé)編: 時間:2024-07-11 17:35:18 810觀看
導(dǎo)讀導(dǎo)言Spring Security是一個功能強大且高度且可定制的身份驗證和訪問控制框架,除了標準的身份認證和授權(quán)之外,它還支持點擊劫持,CSRF,XSS,MITM(中間人)等常見攻擊手段的保護,并提供密碼編碼,LDAP認證,Session管理,Remember Me認證

導(dǎo)言

Spring Security是一個功能強大且高度且可定制的身份驗證和訪問控制框架,除了標準的身份認證和授權(quán)之外,它還支持點擊劫持,CSRF,XSS,MITM(中間人)等常見攻擊手段的保護,并提供密碼編碼,LDAP認證,Session管理,Remember Me認證,JWT,OAuth 2.0等功能特性。QJM28資訊網(wǎng)——每日最新資訊28at.com

由于安全領(lǐng)域本身的復(fù)雜性和豐富的安全特性支持,以及Spring Security高度的可定制性,使得它成為一個龐大且復(fù)雜的框架。每次升級可能帶來的破壞性更新,加上網(wǎng)絡(luò)上的陳舊教程,更是加重了Spring Security非常難用的印象。很多新手可能跟作者一樣,首次引入Spring Security框架之后,突然發(fā)現(xiàn)很多頁面無法訪問,感到無所適從。QJM28資訊網(wǎng)——每日最新資訊28at.com

為此,本文將基于Spring Boot 3.1.x依賴的Spring Security 6.1.x版本,深入探討Spring Security的架構(gòu)和實現(xiàn)原理。本文將著重解釋Spring Security的設(shè)計思想,而不會過多涉及具體的實現(xiàn)細節(jié)。QJM28資訊網(wǎng)——每日最新資訊28at.com

文章的目標是讓讀者在閱讀完本文之后,能夠?qū)φ麄€Spring Security框架有個清晰的理解,并在面對問題時知道如何著手排查。另外,本文重點關(guān)注Spring Security的總體架構(gòu),以及身份認證(Authentication)和鑒權(quán)控制(Authorization)的實現(xiàn)。QJM28資訊網(wǎng)——每日最新資訊28at.com

?QJM28資訊網(wǎng)——每日最新資訊28at.com

【版本兼容性】Spring Security 6引入了很多破壞性的更新,包括廢棄代碼的刪除,方法重命名,全新的配置DSL等,但是架構(gòu)和基本原理還是保持不變的。本文在講解過程中會盡量指出當前版本跟老版本的差異,尤其是涉及到兼容性問題的時候。【閱讀提示】本文的篇幅較長,并且包含了部分源碼分析,時間有限的情況下,可以重點閱讀架構(gòu)圖部分。QJM28資訊網(wǎng)——每日最新資訊28at.com

Java Web應(yīng)用的Security實現(xiàn)基本思路

大家可以嘗試思考下,安全相關(guān)的校驗和處理,應(yīng)該處于應(yīng)用的哪個部分呢?答案是,應(yīng)該放在所有請求的入口,因為它是跟具體的業(yè)務(wù)邏輯無關(guān)的,在Spring MVC世界里就是@Controller之前。QJM28資訊網(wǎng)——每日最新資訊28at.com

在JakartaEE(JavaEE的新版)規(guī)范中,F(xiàn)ilter和Servlet都符合這個前置要求。然而,Spring的Web應(yīng)用基本上只包含一個DispatcherServelt,主要用于請求分發(fā),缺乏安全相關(guān)的支持和合適的擴展機制。而Filter運行在Servlet之前,而規(guī)范本身就支持配置多個Filter。因此,在請求到達Servlet之前,先通過Filter進行安全驗證就是一個非常合理的實現(xiàn)方式。這樣可以在請求進入業(yè)務(wù)邏輯之前,對請求進行攔擊,然后進行必要的安全性檢查和處理。QJM28資訊網(wǎng)——每日最新資訊28at.com

這也是Spring Security的實現(xiàn)方式。本質(zhì)上,Spring Security的實現(xiàn)原理很簡單,就是提供了一個用于安全驗證的Filter。假如我們自己實現(xiàn)一個簡化版的Filter,它的大概邏輯應(yīng)該是這樣的:QJM28資訊網(wǎng)——每日最新資訊28at.com

public class SimpleSecurityFilter extends HttpFilter {    @Override    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {        UsernamePasswordToken token = extractUsernameAndPasswordFrom(request);  // (1)        if (notAuthenticated(token)) {  // (2)            // 用戶名密碼錯誤            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // HTTP 401.            return;        }        if (notAuthorized(token, request)) { // (3)            // 當前登錄用戶的權(quán)限不足            response.setStatus(HttpServletResponse.SC_FORBIDDEN); // HTTP 403            return;        }        // 通過了身份驗證和權(quán)限校驗,繼續(xù)執(zhí)行其它Filter,最終到達Servlet        chain.doFilter(request, response); // (4)    }}
  1. 從HTTP請求中獲取用戶名和密碼,來源包括標準的Basic Auth HTTP Header,表單字段或者cookie等等。
  2. 身份認證,也就是校驗用戶名和密碼。
  3. 認證通過后,需要檢查當前登錄的用戶有沒有訪問當前HTTP請求的權(quán)限,也就是鑒權(quán)邏輯。
  4. 權(quán)限校驗也通過后,就繼續(xù)執(zhí)行其它Filter,所有Filter都通過后,進入Servlet,最終到達具體的Controller。

FilterChainQJM28資訊網(wǎng)——每日最新資訊28at.com

在安全領(lǐng)域,由于攻防手段的多樣性和認證鑒權(quán)方式的復(fù)雜性,將所有功能都放在一個Filter中會導(dǎo)致該Filter迅速演變?yōu)橐粋€龐大而復(fù)雜的類。QJM28資訊網(wǎng)——每日最新資訊28at.com

因此,在實際應(yīng)用場景中,我們常常將這個龐大的Filter拆分成多個小Filter,并將它們鏈接在一起。每個Filter都只負責(zé)特定領(lǐng)域的功能,比如CsrfFilter,AuthenticationFilter,AuthorizationFilter等。QJM28資訊網(wǎng)——每日最新資訊28at.com

這種概念被稱為FilterChain,實際上JarkataEE規(guī)范也有相識的概念。通過使用FilterChain,你就可以以插拔的方式添加或移除特定功能的Filter,而無需改動現(xiàn)有的代碼。QJM28資訊網(wǎng)——每日最新資訊28at.com

Spring Security框架的基本架構(gòu)和原理

上一節(jié)其實已經(jīng)說明了Spring Security框架的基本思路,下面我們深入分析其實現(xiàn)原理和架構(gòu)。QJM28資訊網(wǎng)——每日最新資訊28at.com

實現(xiàn)原理

一個應(yīng)用引入了Spring Security Starter包后,再啟動應(yīng)用,你會發(fā)現(xiàn)控制臺多了下面這條日志,說明已經(jīng)開啟了Security特性。QJM28資訊網(wǎng)——每日最新資訊28at.com

2023-07-12T10:05:23.168+08:00  INFO 680540 --- [main] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@46e3559f, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@3b83459e, org.springframework.security.web.context.SecurityContextHolderFilter@26837057, org.springframework.security.web.header.HeaderWriterFilter@2d74c81b, org.springframework.security.web.csrf.CsrfFilter@3a17b2e3, org.springframework.security.web.authentication.logout.LogoutFilter@5f5827d0, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@4ed5a1b0, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@3b332962, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@32118208, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@67b355c8, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@991cbde, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@dd4aec3, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@414f87a9, org.springframework.security.web.access.ExceptionTranslationFilter@59939293, org.springframework.security.web.access.intercept.AuthorizationFilter@f438904]

從這條日志可以觀察到,Spring Security通過DefaultSecurityFilterChain類來完成安全相關(guān)的功能,而該類本身又由其它Filter組成。默認情況下,Spring Security Starter引入了15個Filter,下面我們簡要介紹下其中幾個重要的Filter:QJM28資訊網(wǎng)——每日最新資訊28at.com

  1. CsrfFilter:這個Filter用于防止跨站點請求偽造攻擊,這也是導(dǎo)致所有POST請求都失敗的原因。基于Token驗證的API服務(wù)可以選擇關(guān)閉CsrfFilter,而一般Web頁面需要開啟。
  2. BasicAuthenticationFilter:支持HTTP的標準Basic Auth的身份驗證模塊。
  3. UsernamePasswordAuthenticationFilter:支持Form表單形式的身份驗證模塊。
  4. DefaultLoginPageGeneratingFilter和DefaultLogoutPageGeneratingFilter:用于自動生成登錄頁面和注銷頁面。
  5. AuthorizationFilter: 這個Filter負責(zé)授權(quán)模塊。值得注意的是,在老版本中鑒權(quán)模塊是FilterSecurityInterceptor.

這些Filter構(gòu)成了Spring Security的核心功能,通過它們,我們可以實現(xiàn)身份驗證、授權(quán)、防護等安全特性。根據(jù)應(yīng)用的需求,我們可以選擇啟用或禁用特定的Filter,以定制和優(yōu)化安全策略。QJM28資訊網(wǎng)——每日最新資訊28at.com

SecurityFilterChain

DefaultSecurityFilterChain類實現(xiàn)了SecurityFilterChain接口,我們打開這個接口的源碼,會發(fā)現(xiàn)它只有兩個方法,matches用于匹配特定的Http請求(比如特定規(guī)則的URL),getFilters 用于獲取可用的所有Security Filter。QJM28資訊網(wǎng)——每日最新資訊28at.com

public interface SecurityFilterChain {    boolean matches(HttpServletRequest request); // 規(guī)則匹配    List<Filter> getFilters(); // 該FilterChain下的所有Security Filter}

從這段代碼可以得出兩個結(jié)論:QJM28資訊網(wǎng)——每日最新資訊28at.com

  1. 不同的Http請求可以對應(yīng)不同的SecurityFilterChain(通過matches方法)。
  2. SecurityFilterChain不是我們以為的JakartaEE的Servlet Filter實現(xiàn),它僅僅是一個包含多個Filter的容器,本身不負責(zé)調(diào)度和執(zhí)行。它只是一個配置項,用于指定一組Filter,以實現(xiàn)特定的安全需求。

DelegatingFilterProxy

實際上,JakartaEE層面上的Filter實現(xiàn)是DelegatingFilterProxy類,它在Spring Security中起到了一個重要的橋梁作用,連接了Servlet容器和Spring容器。Servlet容器不了解Spring定義的Beans,而Spring Security的大部分組件及其依賴都是注冊到Spring容器中的Bean。QJM28資訊網(wǎng)——每日最新資訊28at.com

DelegatingFilterProxy核心代碼的主要工作就是從WebApplicationContext獲取指定名稱的Filter Bean,然后委托給這個Bean的doFilter方法。以下是簡化后的偽代碼:QJM28資訊網(wǎng)——每日最新資訊28at.com

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)        throws ServletException, IOException {    Filter delegateToUse = this.delegate;    if (delegateToUse == null) {        synchronized (this.delegateMonitor) {            delegateToUse = this.delegate;            if (delegateToUse == null) {                WebApplicationContext wac = findWebApplicationContext();                // 獲取Filter Bean并初始化                delegateToUse = initDelegate(wac);            }            this.delegate = delegateToUse;        }    }    // 委托給的delegate對象完成實際的doFilter    invokeDelegate(delegateToUse, request, response, filterChain);}protected Filter initDelegate(WebApplicationContext wac) throws ServletException {    // Bean名稱配置在SecurityFilterAutoConfiguration.DEFAULT_FILTER_NAME = "springSecurityFilterChain"    String targetBeanName = getTargetBeanName();    // 從容器中獲取指定名稱的Filter類型Bean    Filter delegate = wac.getBean(targetBeanName, Filter.class);    if (isTargetFilterLifecycle()) {        delegate.init(getFilterConfig());    }    return delegate;}

通過這種方式,DelegatingFilterProxy實現(xiàn)了將Servlet容器中的Filter請求委托給Spring容器中的具體Filter Bean處理,從而實現(xiàn)了Servlet容器和Spring容器之間的無縫連接。QJM28資訊網(wǎng)——每日最新資訊28at.com

FilterChainProxy

而這個被委托的Filter Bean的類型就是FilterChainProxy,是在WebSecurityConfiguration中配置的:QJM28資訊網(wǎng)——每日最新資訊28at.com

// name = "springSecurityFilterChain"@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)public Filter springSecurityFilterChain() throws Exception {    // 配置SecurityFilterChain    boolean hasFilterChain = !this.securityFilterChains.isEmpty();    if (!hasFilterChain) {        this.webSecurity.addSecurityFilterChainBuilder(() -> {            this.httpSecurity.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated());            this.httpSecurity.formLogin(Customizer.withDefaults());            this.httpSecurity.httpBasic(Customizer.withDefaults());            return this.httpSecurity.build();        });    }    for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {        this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);    }    // WebSecurity自定義配置    for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {        customizer.customize(this.webSecurity);    }    // FilterChainProxy最終是由WebSecurity構(gòu)建出來的    return this.webSecurity.build();}

從上面代碼可以發(fā)現(xiàn),F(xiàn)ilterChainProxy對象最終是由WebSecurity根據(jù)SecurityFilterChain和其它一些配置構(gòu)建出來的。QJM28資訊網(wǎng)——每日最新資訊28at.com

FilterChainProxy主要作用就是查找匹配當前Http請求規(guī)則的SecurityFilterChain,然后將工作委派給SecurityFilterChain的所有Filter。簡化后的偽代碼如下所示:QJM28資訊網(wǎng)——每日最新資訊28at.com

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {    // 獲取匹配的所有Filter    List<Filter> filters = getFilters(request);     // 按順序執(zhí)行Filter    Filter nextFilter = this.filters.get(this.currentPosition - 1);    nextFilter.doFilter(request, response, this);}private List<Filter> getFilters(HttpServletRequest request) {    for (SecurityFilterChain chain : this.filterChains) {        // 返回匹配規(guī)則的SecurityFilterChain的Filter列表        if (chain.matches(request)) {             return chain.getFilters();        }    }    return null;}

?QJM28資訊網(wǎng)——每日最新資訊28at.com

【Tips】FilterChainProxy可以認為是整個Spring Security處理請求的一個起點,如果你遇到Security相關(guān)問題,又不清楚是具體哪個Filter導(dǎo)致的,就可以從這里開始Debug。QJM28資訊網(wǎng)——每日最新資訊28at.com

基本架構(gòu)

從上一節(jié)的內(nèi)容,我們可以得出下面這一副架構(gòu)圖(圖中藍色和橘紅色的部分代表Security Security)。從圖中可以看出,Spring Security框架通過DelegatingFilterProxy建立起了Servlet容器和Spring容器的鏈接,F(xiàn)ilterChainProxy基于匹配規(guī)則(比如URL匹配),決定使用哪個SecurityFilterChain。而SecurityFilterChain又由零到多個Filter組成,這些Filter完成實際的功能。QJM28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片QJM28資訊網(wǎng)——每日最新資訊28at.com

Security Filter和配置DSL

Spring Security是基于Jakarta EE的Filter實現(xiàn)的,而在此基礎(chǔ)上,它提供了一套自身的Filter機制,相當于兩層的Filter嵌套。為了不混淆這兩種Filter,我們把Spring Security框架提供的Filter稱為Security Filter。在下文中,我們所提及的配置,擴展和自定義的Filter都指的是Security Filter,如果沒有特別說明,都默認指的是Security Filter。QJM28資訊網(wǎng)——每日最新資訊28at.com

通過一系列Security Filter,Spring Security提供了豐富的開箱即用的安全功能,包括身份認證,鑒權(quán),Csrf等等。每個功能都是通過一個或者多個Security Filter實現(xiàn)的。有些復(fù)雜的Filter,例如身份認證和鑒權(quán),擁有自己的特定架構(gòu),并且會依賴Filter的順序和執(zhí)行過程中的上下文信息,這也是導(dǎo)致Spring Security在使用上相對復(fù)雜的原因之一。QJM28資訊網(wǎng)——每日最新資訊28at.com

Spring Securiy的基本配置都是通過自定義SecurityFilterChain的Bean來實現(xiàn)的。下面是一個示例配置,它提供了自定義的登錄頁面,并且針對不同的URL配置了不同的角色權(quán)限,這些配置方法實際上就是配置不同的Security Filter,更詳細的解釋會在后面講解具體特性的時候時展開說明。QJM28資訊網(wǎng)——每日最新資訊28at.com

@Beanstatic SecurityFilterChain mySecurityFilterChain(HttpSecurity http) throws Exception {    // 鑒權(quán)相關(guān)配置    http.authorizeHttpRequests((requests) ->            request.requestMatchers("/admin").hasAuthority("ROLE_ADMIN") // "/admin"要求有“ROLE_ADMIN"角色權(quán)限                    .requestMatchers("/hello").hasRole("USER") // "/hello"要求有"ROLE_USER"角色權(quán)限                    .anyRequest().authenticated()); // 其它只需要身份認證通過即可,不需要其它特殊權(quán)限    // 登錄相關(guān)配置    http.formLogin(formLogin -> formLogin            .loginPage("/authentication") // 自定義登錄頁面,不再使用內(nèi)置的自動生成頁面            .permitAll() // 允許自定義頁面的匿名訪問,不需要認證和鑒權(quán)    );    return http.build(); // 返回構(gòu)建的SecurityFilterChain實例}

【版本兼容性】Spring Security 6.0在配置方面引入了許多改變。在之前的老版本中,可以選擇廢棄的WebSecurityConfigurerAdapter進行配置,但從6.0版本開始,這個廢棄類已經(jīng)被刪除了。而目前很多老項目以及網(wǎng)上的教程仍在使用WebSecurityConfigurerAdapter。另外,配置DLS也發(fā)生了變化。Spring Security 6.0采用了基于Lambda表達式的DSL配置方式,取代了之前的純鏈式調(diào)用方式,使得配置更加靈活和直觀。一些方法名稱也進行了修改,例如antMatchers替換為requestMatchers。QJM28資訊網(wǎng)——每日最新資訊28at.com

除了Spring Boot的專有配置,Spring Security自身也提供了默認配置,這些默認配置在HttpSecurityConfiguration#httpSecurity方法中,它默認添加了很多Security Filter,核心代碼如下:QJM28資訊網(wǎng)——每日最新資訊28at.com

@Bean(HTTPSECURITY_BEAN_NAME)@Scope("prototype")HttpSecurity httpSecurity() throws Exception {    // ... //    http        .csrf(withDefaults())        .addFilter(webAsyncManagerIntegrationFilter)        .exceptionHandling(withDefaults())        .headers(withDefaults())        .sessionManagement(withDefaults())        .securityContext(withDefaults())        .requestCache(withDefaults())        .anonymous(withDefaults())        .servletApi(withDefaults())        .apply(new DefaultLoginPageConfigurer<>());    http.logout(withDefaults());    // ... //    return http;}

以上解釋了Spring Security的實現(xiàn)原理和基本架構(gòu),而具體到特定的Security Filter,又有各種的框架,下面將展開說明認證和鑒權(quán)兩個核心模塊。QJM28資訊網(wǎng)——每日最新資訊28at.com

Authentication身份認證

身份認證有很多種方式,大致可以分為以下4類:QJM28資訊網(wǎng)——每日最新資訊28at.com

  1. 標準的賬號密碼認證:這是很多網(wǎng)站都支持的方式,也是大家最熟悉的認證模式;
  2. 調(diào)用第三方服務(wù)或內(nèi)部其它API進行認證:當服務(wù)自身無法直接獲取用戶的密碼時,需要借助第三方服務(wù)或者內(nèi)部API進行認證;
  3. 基于Token的認證:這是API服務(wù)一般使用的認知方式,通過令牌來進行身份驗證;
  4. OAuth2或其它OpenID認證:這種方式廣泛用于允許用戶使用其它平臺的身份信息進行登錄,例如微信登錄,Google登錄等。

Spring Security支持大部分的認證方式,但不同的認證方式需要配置不同的Bean及其依賴Bean,否則很容易遇到各種異常和空指針。QJM28資訊網(wǎng)——每日最新資訊28at.com

本文重點討論標準的賬號密碼認證方式。QJM28資訊網(wǎng)——每日最新資訊28at.com

實現(xiàn)原理

如果你使用的是Spring Boot,那么Spring Boot Starter Security默認就配置了Form表單和Basic認證方式,其配置代碼如下所示:QJM28資訊網(wǎng)——每日最新資訊28at.com

@Configuration(proxyBeanMethods = false)@ConditionalOnWebApplication(type = Type.SERVLET)class SpringBootWebSecurityConfiguration {    @Configuration(proxyBeanMethods = false)    @ConditionalOnDefaultWebSecurity    static class SecurityFilterChainConfiguration {        @Bean        @Order(SecurityProperties.BASIC_AUTH_ORDER)        SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {            http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated()); // 所有URL都需要認證用戶            http.formLogin(withDefaults()); // 支持form表單認證,默認配置提供了自動生成的登錄和注銷頁面            http.httpBasic(withDefaults()); // 支持HTTP Basic Authentication            return http.build();        }    }    // ...其它配置...}

為了討論方便,我們用下面的配置覆蓋Spring Boot默認的配置,只支持Form表單認證方式,討論它具體是如何實現(xiàn)的。QJM28資訊網(wǎng)——每日最新資訊28at.com

@Configuration()  public class MySecurityConfig {    @Bean    SecurityFilterChain mySecurityFilterChain(HttpSecurity http) throws Exception {        http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated()); // (1)        http.formLogin(withDefaults()); // (2)        return http.build();    }}
  1. authorizeHttpRequests方法用于配置每個請求的權(quán)限控制,這里要求所有請求都要通過認證后才能訪問。實際上,這個方法配置的更多是鑒權(quán)相關(guān)的內(nèi)容,跟身份認證的關(guān)聯(lián)較小,它本質(zhì)上是增加了一個AuthorizationFilter用于鑒權(quán),具體細節(jié)在鑒權(quán)部分會詳細說明。
  2. http.formLogin方法提供了Form表單認證的方式,withDefaults方法是Form表單認證的默認配置。這段配置的作用就是增加了用于賬號密碼認證的UsernamePasswordAuthenticationFilter,以及自動生成登錄頁面和注銷頁面的DefaultLogoutPageGeneratingFilter和DefaultLogoutPageGeneratingFilter共3個Security Filter。值得注意的是,登錄頁面和注銷頁面這兩個Filter是配合DefaultLoginPageConfigurer配置一起注冊的。如果你通過formLogin.loginPage提供了自定義的登錄頁面,那么這兩個Filter就不會被注冊。

在本節(jié)中,我們主要討論身份認證的實現(xiàn),因此,接下來將詳細探究Form表單認證方式中UsernamePasswordAuthenticationFilter的實現(xiàn)。QJM28資訊網(wǎng)——每日最新資訊28at.com

AbstractAuthenticationProcessingFilter

對于Filter,我們重點分析它的doFilter方法的源碼。實際上,它繼承了抽象類AbstractAuthenticationProcessingFilter,而這個抽象類的doFilter是一個模板方法,定義了整個認證流程。其核心流程非常簡單,偽代碼如下:QJM28資訊網(wǎng)——每日最新資訊28at.com

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)        throws IOException, ServletException {    // 首先判斷該請求是否是認證請求或者登錄請求    if (!requiresAuthentication(request, response)) { // (1)        chain.doFilter(request, response);        return;    }    try {        Authentication authenticationResult = attemptAuthentication(request, response); // (2) 實際認證邏輯        // 認證成功        successfulAuthentication(request, response, chain, authenticationResult); // (3)    }    catch (AuthenticationException ex) {        // 認證失敗        unsuccessfulAuthentication(request, response, ex); // (4)    }}
  1. 首先requiresAuthentication方法用于判斷當前請求是否為認證請求或者登錄請求,例如通常是POST /login。只有在登錄認證的情況下,才需要通過這個Filter;
  2. attempAuthentication方法是實際的認證邏輯,這是一個抽象方法,具體的邏輯由子類重寫實現(xiàn)。它的規(guī)范行為是,如果認證成功,應(yīng)該返回認證結(jié)果Authentication,否則以拋出異常AuthenticationException的方式表示認證失敗;
  3. successfulAuthentication:認證成功后,該方法會將Authentication對象放到Security Context中,這是非常關(guān)鍵的一步,后續(xù)需要認證結(jié)果的時候都是從Security Context獲取的,比如鑒權(quán)Filter。此外,該方法還會處理其它一些相關(guān)功能,比如RememberMe,事件發(fā)布,最后再調(diào)用AuthenticationSuccessHandler;
  4. unsuccessfulAuthentication :在認證失敗后,它會清空Security Context,調(diào)用RememberMe相關(guān)服務(wù)和AuthenticationFailureHandler來處理認證失敗后的回調(diào)邏輯,比如跳轉(zhuǎn)到錯誤頁面。

Authentication模型

在這里,我們涉及到了一個非常重要的數(shù)據(jù)模型——Authentication,它是一個接口類型,它既是對認證結(jié)果的一個抽象表示,同時也是對認證請求的一個抽象,通常也被稱為認證Token。它的方法都比較抽象,定義如下:QJM28資訊網(wǎng)——每日最新資訊28at.com

public interface Authentication extends Principal, Serializable {    // 當前認證用戶擁有的權(quán)限列表    Collection<? extends GrantedAuthority> getAuthorities();    // 用戶的一個身份標識,通常就是用戶名    Object getPrincipal();    // 可用于證明用戶身份的一個憑證,通常就是用戶密碼    Object getCredentials();    // 當前用戶是否認證通過    boolean isAuthenticated();    // 更新用戶的認證狀態(tài)    void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;    // 獲取附加的詳情信息,比如原始的Http請求體等。    Object getDetails();}

具體的Authentication實現(xiàn)一般都命名為XXXToken,大部分都繼承自抽象類AbstractAuthenticationToken,比如表示標準的用戶名密碼認證結(jié)果的UsernamePasswordAuthenticationToken,表示匿名登錄用戶認證結(jié)果的AnonymousAuthenticationToken等等,你也可以完全實現(xiàn)自己的Authentication。QJM28資訊網(wǎng)——每日最新資訊28at.com

attempAuthentication方法

接下來,我們看下UsernamePasswordAuthenticationFilter的認證具體實現(xiàn)方法attempAuthentication,它的源碼如下:QJM28資訊網(wǎng)——每日最新資訊28at.com

public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)        throws AuthenticationException {    // 默認只支持POST請求    if (this.postOnly && !request.getMethod().equals("POST")) {        throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());    }    // 從form表單獲取用戶名和密碼    String username = obtainUsername(request);    username = (username != null) ? username.trim() : "";    String password = obtainPassword(request);    password = (password != null) ? password : "";    // 構(gòu)建一個用于認證的請求    UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,            password);    // 附加詳細信息,比如請求體,有些認證方式需要除了用戶名密碼外更多的信息    setDetails(request, authRequest);    // 委托給AuthenticationManager做具體的認證    return this.getAuthenticationManager().authenticate(authRequest);}

這個方法非常簡單,它主要進行一些前置校驗工作,從請求體中獲取用戶名和密碼,并構(gòu)建認證請求對象。然后,剩余的認證工作都是委托給AuthenticationManager接口來完成的,該接口的定義如下:QJM28資訊網(wǎng)——每日最新資訊28at.com

public interface AuthenticationManager {    Authentication authenticate(Authentication authentication) throws AuthenticationException;}

AuthenticationManager和AuthenticationProvider

AuthenticationManager接口只有一個方法,它的入?yún)⒑统鰠⒍际茿uthentication對象。通常情況下,入?yún)⑻峁┝吮匾恼J證信息,例如用戶名和密碼。而在認證成功后,該方法會返回認證結(jié)果,并附加認證狀態(tài),用戶擁有的權(quán)限列表等信息。如果認證失敗,它會拋出AuthenticationException異常類的子類,其中包括DisabledException,LockedException和BadCredentialsException等賬號相關(guān)的異常。QJM28資訊網(wǎng)——每日最新資訊28at.com

AuthenticationManager接口定義了Spring Security的認證行為。你可以提供自定義的實現(xiàn),Spring Security也提供了一個通用的實現(xiàn)類ProviderManager。ProviderManager將具體的認證工作委托給一系列的AuthenticationProvider。QJM28資訊網(wǎng)——每日最新資訊28at.com

每個AuthenticationProvider對應(yīng)不同的認證方式。比如最常見的用戶名密碼的認證實現(xiàn)是DaoAuthenticationProvider,而JwtAuthenticationProvider提供了JWT Token的認證。你可以通過添加不同的AuthenticationProvider的方式,在同一個服務(wù)內(nèi)支持多種類型的認證方式,比如需要調(diào)用其它API檢驗密碼的情況,就需要自定義AuthenticationProvider。QJM28資訊網(wǎng)——每日最新資訊28at.com

此外,ProviderManager還可以配置父級AuthenticationManager,當這個ProviderManager的所有AuthenticationProvider都不支持所需的認證方式時,它會繼續(xù)委托給父級的AuthenticationManager,而該父級通常也是一個ProviderManager類型。QJM28資訊網(wǎng)——每日最新資訊28at.com

UserDetailsService和PasswordEncoderQJM28資訊網(wǎng)——每日最新資訊28at.com

DaoAuthenticationProvider是最常用的認證實現(xiàn)之一,它通過UserDetailsService和PasswordEncoder來驗證用戶名和密碼。QJM28資訊網(wǎng)——每日最新資訊28at.com

UserDetailsService的作用是查找用戶信息UserDetails,這些信息包括用戶密碼,狀態(tài),權(quán)限列表等。用戶信息可以存儲在內(nèi)存,數(shù)據(jù)庫或者其它任何地方。Spring Security默認的配置是內(nèi)存存儲,對應(yīng)的UserDetailsService實現(xiàn)是InMemoryUserDetailsManager,而數(shù)據(jù)庫存儲則對應(yīng)JdbcUserDetailsManager。QJM28資訊網(wǎng)——每日最新資訊28at.com

從UserDetailsService獲取到用戶密碼后,需要通過PasswordEncoder來驗證密碼的正確性。因為密碼一般都不應(yīng)該以明文形式存儲,實際存儲的是按一定規(guī)則編碼后的文本,Spring Security支持多種編碼方式,例如bcrypt,argon2,scrypt,pbkdf2等。你可以配置PasswordEncoder Bean來選擇不同的編碼方式。都是請注意,內(nèi)置的編碼方式默認對編碼后的文本有一個格式要求,就是必須有類似{bcrypt}的前綴來表示編碼方式。QJM28資訊網(wǎng)——每日最新資訊28at.com

基本架構(gòu)

架構(gòu)圖

上一節(jié)中,我們講述了用戶名密碼認證的實現(xiàn)細節(jié),現(xiàn)在,讓我們以用戶名密碼認證方式為例,從整體上來看下身份認證的架構(gòu)和流程。它的整體架構(gòu)如下:QJM28資訊網(wǎng)——每日最新資訊28at.com

  1. 當一個HTTP請求進來后,UsernamePasswordAuthenticationFilter會從HTTP請求體中獲取用戶名和密碼,然后使用這些信息創(chuàng)建一個UsernamePasswordAuthenticationToken對象作為認證請求的參數(shù)。
  2. 接下來,AuthenticationManager(其實現(xiàn)類是ProviderManager)負責(zé)對接受到的UsernamePasswordAuthenticationToken進行認證。
  3. ProviderManager會遍歷配置的所有AuthenticationProvider,查找支持UsernamePasswordAuthenticationToken類型的AuthenticationProvider,然后委托其進行實際的認證工作,而在這里,匹配的就是DaoAuthenticationProvider。
  4. DaoAuthenticationProvider首先調(diào)用UserDetailService獲取用戶信息,然后將獲取到的密碼(通常是編碼后的密碼)委托給PasswordEncoder進行驗證。如果認證失敗,DaoAuthenticationProvider會拋出AuthenticationException的子類表示認證失敗。
  5. 當認證成功時,AuthenticationManager會返回一個UsernamePasswordAuthenticationToken對象作為認證結(jié)果,這個對象除了包含用戶的基本信息外,最重要的是認證通過狀態(tài)以及該用戶擁有的權(quán)限列表,這些信息在后續(xù)的鑒權(quán)模塊會用到。
  6. 認證結(jié)果會被放入SecurityContext,這樣后續(xù)的模塊(包括鑒權(quán)和用戶業(yè)務(wù)模塊等)如果需要這個結(jié)果(包括用戶信息和權(quán)限列表),就可以通過以下方法獲取:SecurityContextHolder.getContext().getAuthentication()。

組件替換

這個架構(gòu)非常靈活,大部分組件都是可配置和可替換的,自底向上,我們分別可以替換以下組件來滿足特定需求:QJM28資訊網(wǎng)——每日最新資訊28at.com

  1. UserDetailsService:根據(jù)用戶名查找用戶信息的組件,默認配置的是內(nèi)存存儲InMemoryUserDetailsManager,你也可以配置為內(nèi)置的數(shù)據(jù)庫存儲JdbcUserDetailsManager,但是它有很多默認的約定要遵守,對未來的擴展也不夠靈活。通常會根據(jù)公司的規(guī)范要求或數(shù)據(jù)庫存儲的方式提供自定義的實現(xiàn)。
  2. PasswordEncoder:對密碼進行編碼的組件,建議根據(jù)公司的編碼要求或當前數(shù)據(jù)庫中已使用的編碼來配置。如果沒有特殊要求,建議采用默認的BCryptPasswordEncoder。
  3. AuthenticationProvider: 為了安全需要,公司內(nèi)部很多應(yīng)用是不允許直接訪問用戶的密碼的,而通常會提供一個認證的API。此時,就需要自定義AuthenticationProvider,它的核心邏輯就是調(diào)用API做認證,然后把結(jié)果再包裝成Authentication返回給AuthenticationManager。
  4. AuthenticationManager:它的默認實現(xiàn)ProviderManager適用于大部分場景,通常不需要替換,除非你不想引入太多的概念。
  5. UsernamePasswordAuthenticationFilter:如果你不想引入過多的概念和復(fù)雜度,可以提供自己的Security Filter,從而完全脫離該框架。但是需要確保認證結(jié)果模型Authentication仍然被正確處理,并且將結(jié)果通過方法SecurityContextHolder.getContext().setAuthentication放入Security Context中。

【Tips】從整個Security框架的角度來看,認證模塊的核心概念只有兩個,分別是認證結(jié)果Authentication和Security Context。其它概念都可以認為是認證模塊的內(nèi)部實現(xiàn)細節(jié)。QJM28資訊網(wǎng)——每日最新資訊28at.com

鑒權(quán)模塊Authorization

認證模塊證明了用戶的身份,但顯然普通用戶不應(yīng)該可以隨意訪問管理頁面或敏感資源,因此還需要有個模塊來確保只有授權(quán)的用戶才能執(zhí)行特定的操作,這個模塊稱之為鑒權(quán)或者授權(quán)(Authorization)。QJM28資訊網(wǎng)——每日最新資訊28at.com

當你通過HttpSecurity.authorizeHttpRequests方法來配置請求的訪問權(quán)限控制時,就會自動添加鑒權(quán)的Security Filter:AuthorizationFilter,它是整個SecurityFilterChain的最后一個Filter。QJM28資訊網(wǎng)——每日最新資訊28at.com

【版本兼容性】在Spring Security 6.0版本中,鑒權(quán)模塊發(fā)生了很大變化。以前的版本中,鑒權(quán)模塊使用FilterSecurityInterceptor,而6.0版本之后,這個被廢棄了,取而代之的是AuthorizationFilter。同時,還有一些相關(guān)的依賴組件,如AccessDecisionManager和AccessDecisionVoter也被AuthorizationManger替換了。因此,本節(jié)的內(nèi)容只限于6.0以及之后的版本。QJM28資訊網(wǎng)——每日最新資訊28at.com

實現(xiàn)原理

我們先看下鑒權(quán)模塊的入口,也就是AuthorizationFilter的doFilter方法:QJM28資訊網(wǎng)——每日最新資訊28at.com

@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)        throws ServletException, IOException {    // ...其它非核心邏輯... //    try {        AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request); // (1)        this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, request, decision);        if (decision != null && !decision.isGranted()) { // (2)            throw new AccessDeniedException("Access Denied");        }        chain.doFilter(request, response);    }    finally {        request.removeAttribute(alreadyFilteredAttributeName);    }}private Authentication getAuthentication() {    Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication(); // (3)    if (authentication == null) {        throw new AuthenticationCredentialsNotFoundException(                "An Authentication object was not found in the SecurityContext");    }    return authentication;}
  1. 這個方法本身很簡單,核心邏輯都委托給了AuthorizationManager。AuthorizationManager會校驗Authentication的權(quán)限,并返回鑒權(quán)的結(jié)果AuthorizationDecision。
  2. 如果當前認證用戶沒有訪問權(quán)限,就會拋出AccessDeniedException異常,表示拒絕訪問。
  3. 待校驗的Authentication是從Security Context獲取的,通常是在前面的認證階段設(shè)置的。在這里,實際上傳給AuthorizationManager的是一個獲取Authentication的方法,而不是Authentication本身,這樣就把實際的獲取操作延后到了真正進行授權(quán)的時候,這在某些場景下可以提高性能,比如permitAll,實際上它根本用不到Authentication。

AuthorizationManager

AuthorizationManager才是真正執(zhí)行鑒權(quán)邏輯的類,最常用的實現(xiàn)類是AuthorityAuthorizationManager,它的實現(xiàn)邏輯很簡單,它會調(diào)用Authentication的getAuthorities方法,獲取當前登錄用戶的權(quán)限列表,然后將這些權(quán)限與請求需要的權(quán)限進行匹配。QJM28資訊網(wǎng)——每日最新資訊28at.com

實際上,選擇使用哪個AuthorizationManager是開發(fā)手動設(shè)置的。我們來分析一個常用的權(quán)限配置代碼片段:QJM28資訊網(wǎng)——每日最新資訊28at.com

@Beanstatic SecurityFilterChain mySecurityFilterChain(HttpSecurity http) throws Exception {    http.authorizeHttpRequests((requests) -> // (1)            requests                    .requestMatchers("/admin").hasAuthority("ROLE_ADMIN") // (2)                    .requestMatchers("/hello").hasRole("USER") // (3)                    .anyRequest().authenticated()); // (4)    // ... 其它配置 ... //    return http.build();}
  1. 調(diào)用authorizeHttpRequests方法就相當于打開了鑒權(quán)模塊,它會注冊AuthorizationFilter到SecurityFilterChain的最后。
  2. 對于匹配/admin的請求,要求有ROLE_ADMIN權(quán)限。hasAuthority的底層就是配置了一個要求ROLE_ADMIN權(quán)限的AuthorityAuthorizationManager對象。
  3. 對于匹配/hello的請求,要求有USER角色,等價于ROLE_USER權(quán)限。hasRole會自動在角色名稱前面加上前綴ROLE_。hasRole的底層就是配置了一個要求ROLE_USER權(quán)限的AuthorityAuthorizationManager對象。
  4. 對于其它的請求,只要通過身份認證就可以訪問,不需要特定的權(quán)限。類似的,authenticated方法的底層配置了一個AuthenticatedAuthorizationManager對象。

在Spring Security中,很多初學(xué)者都容易混淆Role和Authority的區(qū)別,實際上在技術(shù)實現(xiàn)層面上,這兩者沒有本質(zhì)區(qū)別,底層都僅僅是一個表示權(quán)限的字符串標識符。更多的區(qū)別在于權(quán)限管理的概念上,一般情況下,Authority表示細粒度的操作權(quán)限,比如ADD_USER,DELETE_USER等,通常是動詞;而Role則會與實際業(yè)務(wù)角色想對應(yīng),比如管理員ADMIN,普通員工STAFF等,通常是名稱。此外,一般一個Role會對應(yīng)多個Authority,同時角色之間可以存在繼承關(guān)系,比如ADMIN可以繼承STAFF的所有權(quán)限。QJM28資訊網(wǎng)——每日最新資訊28at.com

我們來看下hasAuthority的源碼,以分析它是如何配置AuthorizeManager的:QJM28資訊網(wǎng)——每日最新資訊28at.com

public AuthorizationManagerRequestMatcherRegistry hasAuthority(String authority) {    return access( // (3)      withRoleHierarchy( //(2)          AuthorityAuthorizationManager.hasAuthority(authority) // (1)      )    );}public static <T> AuthorityAuthorizationManager<T> hasAuthority(String authority) {    Assert.notNull(authority, "authority cannot be null");    return new AuthorityAuthorizationManager<>(authority);}public AuthorizationManagerRequestMatcherRegistry access(        AuthorizationManager<RequestAuthorizationContext> manager) {    Assert.notNull(manager, "manager cannot be null");    return AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, manager);}private AuthorityAuthorizationManager<RequestAuthorizationContext> withRoleHierarchy(           AuthorityAuthorizationManager<RequestAuthorizationContext> manager) {    manager.setRoleHierarchy(AuthorizeHttpRequestsConfigurer.this.roleHierarchy.get());    return manager;}
  1. AuthorityAuthorizationManager.hasAuthority方法簡單地創(chuàng)建了一個要求特定authority權(quán)限的AuthorityAuthorizationManager實例。
  2. withRoleHierarchy是一個裝飾器方法,它打開了角色繼承的功能。角色繼承允許一個角色繼承另一個角色的所有權(quán)限,從而簡化權(quán)限配置。
  3. 最后,access方法將這個AuthorityAuthorizationManager實例注冊到權(quán)限控制中。

access方法是公開的,你可以自己實現(xiàn)一個AuthorizationManager,然后通過這個方法進行注冊。例如,我們可以提供一個拒絕所有請求的實現(xiàn):QJM28資訊網(wǎng)——每日最新資訊28at.com

http.authorizeHttpRequests((requests) ->    requests.anyRequest().access((authentication, object) -> null));

【Tips】通過自定義AuthorizationManager,我們可以完全接管鑒權(quán)的邏輯,實現(xiàn)更加靈活和復(fù)雜的權(quán)限控制。QJM28資訊網(wǎng)——每日最新資訊28at.com

基本架構(gòu)

相比認證模塊,鑒權(quán)模塊不需要太多的靈活性和擴展性需求,因此它的架構(gòu)相對簡單。QJM28資訊網(wǎng)——每日最新資訊28at.com

同樣,我們以一個標準的鑒權(quán)流程為例,來看整體的架構(gòu)和流程圖。QJM28資訊網(wǎng)——每日最新資訊28at.com

  1. 一個HTTP請求進來,經(jīng)過了一系列Security Filter后,最終來到AuthorizationFilter,進而調(diào)用AuthorizationManager#check方法進行權(quán)限校驗。
  2. 實際的校驗工作繼續(xù)委托給AuthoritiesAuthorizationManager。
  3. AuthoritiesAuthorizationManager先從Security Context中獲取到Authentication對象(這個對象一般是前面的某個認證Filter設(shè)置的),然后基于其Authorites權(quán)限列表構(gòu)建GrantedAuthority列表,用于權(quán)限項的匹配。
  4. 最終會返回一個AuthorizationDecision表示權(quán)限校驗結(jié)果。

總結(jié)

本文重點分析了Spring Security的源碼和架構(gòu),幫助讀者理解其實現(xiàn)原理。由于篇幅有限,本文只覆蓋了身份認證和鑒權(quán)模塊的核心邏輯,很多特性沒有涉及,包括Session管理,Remember Me服務(wù),異常分支和錯誤處理等等,不過有了上述的基礎(chǔ)知識,讀者完全可以自己分析源碼并深入理解這些特性。QJM28資訊網(wǎng)——每日最新資訊28at.com

FAQ

認證和鑒權(quán)失敗拋出的異常是如何處理的?QJM28資訊網(wǎng)——每日最新資訊28at.com

當發(fā)生認證或鑒權(quán)失敗時,Spring Security有專門的Security Filter ExceptionTranslationFilter來捕獲并處理這些異常。如果是認證異常錯誤AuthenticationException及其子類,會觸發(fā)AuthenticationEntryPoint#commence方法,而如果是鑒權(quán)錯誤AccessDeniedException及其子類,則會觸發(fā)AccessDeniedHandler#handle方法。QJM28資訊網(wǎng)——每日最新資訊28at.com

一個請求被Security拒絕了,應(yīng)該如何Debug排查?QJM28資訊網(wǎng)——每日最新資訊28at.com

如果遇到身份認證錯誤,建議直接Debug相關(guān)Filter的doFilter方法,比如Form表單登錄的Filter就是UsernamePasswordAuthenticationFilter;而如果是鑒權(quán)錯誤,可以從AuthorizationFilter開始Debug。QJM28資訊網(wǎng)——每日最新資訊28at.com

但需要注意的是,出于安全考慮,Security相關(guān)的錯誤通常不會提供明確的錯誤信息,甚至不會顯示錯誤信息,而是直接跳轉(zhuǎn)到登錄頁面,比如CsrfFilter可能會導(dǎo)致這種情況。在這種情況下,可以從第一個Filter開始Debug,啟動日志搜索Will secure any request with,就可以找到所有Security Filter列表。或者直接從入口FilterChainProxy#doFilter開始Debug。QJM28資訊網(wǎng)——每日最新資訊28at.com

SecurityFilterChain的配置方法底層是如何實現(xiàn)的?QJM28資訊網(wǎng)——每日最新資訊28at.com

SecurityFilterChain是通過HttpSecurity提供的一套DSL進行配置的。諸如formLogin,csrf,authorizeHttpRequests等方法的邏輯都類似,參數(shù)都是一個lambda表達式,用于做各種自定義配置。而每個方法都會對應(yīng)一個特定的配置類,比如FormLoginConfigurer,CsrfConfigurer等,在執(zhí)行HttpSecurity#build方法的時候,會調(diào)用這些配置類的configure方法,該方法的作用就是根據(jù)用戶的自定義配置,創(chuàng)建一個或者多個Security Filter,并將其注冊到SecurityFilterChain。QJM28資訊網(wǎng)——每日最新資訊28at.com

此外,開發(fā)者還可以通過HttpSecurity.addFilter方法直接添加自定義的Security Filter。而對于復(fù)雜且有許多配置選項的Filter,也可以自定義SecurityConfigurerAdapter類,并通過HttpSecurity#apply方法來配置和注冊Filter。QJM28資訊網(wǎng)——每日最新資訊28at.com

Spring Security Starter有哪些默認配置?QJM28資訊網(wǎng)——每日最新資訊28at.com

Spring Security Starter默認配置在spring-boot-autoconfigure-x.x.x包下的文件META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中可以找到。而具體的核心配置類有HttpSecurityConfiguration和SpringBootWebSecurityConfiguration。QJM28資訊網(wǎng)——每日最新資訊28at.com

Spring Security版本跟本文的不一樣,遇到問題如何排查?QJM28資訊網(wǎng)——每日最新資訊28at.com

每次Spring Security升級,尤其是大版本升級,都可能引入破壞性或者不兼容的更新。不過,基于Filter和SecuiryFilterChain的框架和架構(gòu)通常是不會改變的。但是,通常會廢棄掉老的配置方法,引入新的配置,某些特定模塊的實現(xiàn)也有可能完全替換,比如6.0的鑒權(quán)模塊AuthorizationFilter就完全替換了老的鑒權(quán)模塊。QJM28資訊網(wǎng)——每日最新資訊28at.com

你可以先從Security Filter列表開始排查,也可以通過入口FilterChainProxy#doFilter來Debug。QJM28資訊網(wǎng)——每日最新資訊28at.com

Spring Security整體太復(fù)雜了,能不能不使用它,而完全自己實現(xiàn)?QJM28資訊網(wǎng)——每日最新資訊28at.com

Security是個一個非常復(fù)雜的領(lǐng)域,很多開發(fā)者對其了解不深。使用Spring Security不僅提供了大部分的安全特性,還包含了很多安全領(lǐng)域的最佳實踐。自己從頭實現(xiàn)安全功能成本很高,并可能缺乏一些重要的安全特性。不過Spring Security的復(fù)雜設(shè)計以及頻繁的破壞性更新,的確給開發(fā)帶來了很大的學(xué)習(xí)成本和維護成本。QJM28資訊網(wǎng)——每日最新資訊28at.com

Spring Security的架構(gòu)非常靈活,因此作者的建議是,不需要完全照搬整體框架,對于不同的應(yīng)用類型和場景,可以選擇性地引入部分功能。比如Admin應(yīng)用可以提供自定義的AuthenticationProvider,而API服務(wù)完全可以自定義Securiy Filter,只要維護好Security Context的Authentication,就可以很好的集成到Spring Security框架里,同時開發(fā)的學(xué)習(xí)和維護成本也能降到最低。QJM28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://m.www897cc.com/showinfo-26-100473-0.htmlSpring Security 6.0:深度剖析其核心實現(xiàn)與工作原理

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 使用Spring Boot 3.x實現(xiàn)多平臺購票信息一致性保障

下一篇: 威剛 2024 上半年累計營收達 209.1 億新臺幣,同比大增 48.56%

標簽:
  • 熱門焦點
  • 一文看懂為蘋果Vision Pro開發(fā)應(yīng)用程序

    譯者 | 布加迪審校 | 重樓蘋果的Vision Pro是一款混合現(xiàn)實(MR)頭戴設(shè)備。Vision Pro結(jié)合了虛擬現(xiàn)實(VR)和增強現(xiàn)實(AR)的沉浸感。其高分辨率顯示屏、先進的傳感器和強大的處理能力
  • 如何正確使用:Has和:Nth-Last-Child

    我們可以用CSS檢查,以了解一組元素的數(shù)量是否小于或等于一個數(shù)字。例如,一個擁有三個或更多子項的grid。你可能會想,為什么需要這樣做呢?在某些情況下,一個組件或一個布局可能會
  • 只需五步,使用start.spring.io快速入門Spring編程

    步驟1打開https://start.spring.io/,按照屏幕截圖中的內(nèi)容創(chuàng)建項目,添加 Spring Web 依賴項,并單擊“生成”按鈕下載 .zip 文件,為下一步做準備。請在進入步驟2之前進行解壓。圖
  • 使用LLM插件從命令行訪問Llama 2

    最近的一個大新聞是Meta AI推出了新的開源授權(quán)的大型語言模型Llama 2。這是一項非常重要的進展:Llama 2可免費用于研究和商業(yè)用途。(幾小時前,swyy發(fā)現(xiàn)它已從LLaMA 2更名為Lla
  • 微軟邀請 Microsoft 365 商業(yè)用戶,測試視頻編輯器 Clipchamp

    8 月 1 日消息,微軟近日宣布即將面向 Microsoft 365 商業(yè)用戶,開放 Clipchamp 應(yīng)用,邀請用戶通過該應(yīng)用來編輯視頻。微軟于 2021 年收購 Clipchamp,隨后開始逐步整合到 Microsof
  • 最“俊美”淘寶賣家,靠直播和短視頻圈粉,上架秒光,年銷3000萬

    來源 | 電商在線文|易琬玉編輯|斯問受訪店鋪:Ringdoll戒之人形圖源:微博@御座的黃山、&ldquo;Ringdoll戒之人形&rdquo;淘寶店鋪有關(guān)外貌的評價,黃山已經(jīng)聽累了。生于1985年的他,哪
  • 自律,給不了Keep自由!

    來源 | 互聯(lián)網(wǎng)品牌官作者 | 李大為編排 | 又耳 審核 | 谷曉輝自律能不能給用戶自由暫時不好說,但大概率不能給Keep自由。近日,全球最大的在線健身平臺Keep正式登陸港交所,努力
  • OPPO K11搭載長壽版100W超級閃充:26分鐘充滿100%

    據(jù)此前官方宣布,OPPO將于7月25日也就是今天下午14:30舉辦新品發(fā)布會,屆時全新的OPPO K11將正式與大家見面,將主打旗艦影像,和同檔位競品相比,其最大的賣
  • 聯(lián)想的ThinkBook Plus下一版曝光,鍵盤旁邊塞個平板

    ThinkBook Plus 是聯(lián)想的一個特殊筆記本類別,它在封面放入了一塊墨水屏,也給人留下了較為深刻的印象。據(jù)有人爆料,聯(lián)想的下一款 ThinkBook Plus 可能更特殊,它
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
麻豆乱码国产一区二区三区| 亚洲一区免费在线观看| 国一区二区在线观看| 激情成人综合网| 亚洲人午夜精品| 亚洲曰本av电影| 久久久中精品2020中文| 欧美精品日韩| 国产视频观看一区| 亚洲欧洲日本专区| 亚洲性视频网址| 久久久久久一区| 欧美日韩精品一区二区| 国产亚洲一级高清| 亚洲精品免费电影| 欧美一区二区三区四区高清| 欧美.com| 国产精品在线看| 最新国产成人av网站网址麻豆| 亚洲性视频网址| 美脚丝袜一区二区三区在线观看 | 国产精品一区二区在线观看| 欧美影片第一页| 久久久夜夜夜| 欧美午夜精品伦理| 亚洲春色另类小说| 亚洲一区二区四区| 蜜桃av综合| 国产日韩欧美制服另类| 亚洲精品中文字幕在线| 久久精品中文字幕一区| 欧美特黄视频| 亚洲国产精品专区久久| 欧美一区二粉嫩精品国产一线天| 女女同性精品视频| 国产亚洲欧洲一区高清在线观看| 一区二区三区视频在线播放| 老妇喷水一区二区三区| 国产乱码精品| 日韩小视频在线观看| 久久久久久久网站| 国产精品色网| aaa亚洲精品一二三区| 久久综合给合久久狠狠色| 国产精品午夜av在线| 日韩特黄影片| 欧美va天堂在线| 国产专区欧美精品| 欧美亚洲日本网站| 欧美色欧美亚洲另类二区| 91久久久一线二线三线品牌| 久久婷婷丁香| 国产亚洲激情| 午夜日韩在线观看| 国产精品每日更新在线播放网址| 亚洲免费久久| 欧美激情一区二区三区在线视频| 黄页网站一区| 欧美在线观看视频在线 | 狠狠色综合网站久久久久久久| 午夜精品亚洲| 国产精品久久久久久久久久尿| 日韩一级大片在线| 欧美激情综合五月色丁香| 亚洲国产成人午夜在线一区| 久久综合999| 一区免费观看| 久久亚洲精品伦理| 精品成人一区二区三区| 久久一区二区三区av| 黄色日韩网站| 久久久国产精品一区二区中文 | 欧美日韩一区二区三区四区在线观看 | 欧美日韩亚洲一区二区三区四区| 亚洲精品国产视频| 欧美风情在线观看| 亚洲欧洲日本国产| 欧美激情中文不卡| 亚洲毛片一区二区| 欧美日韩国产精品一区| 99视频精品| 欧美婷婷久久| 亚洲欧美日韩国产综合| 国产欧美日韩在线播放| 久久福利一区| 伊人影院久久| 欧美成人性生活| 亚洲精品一二区| 欧美日韩精品综合| 在线亚洲美日韩| 欧美日韩综合在线免费观看| 亚洲少妇诱惑| 国产精品稀缺呦系列在线| 午夜精品亚洲一区二区三区嫩草| 国产女人aaa级久久久级| 欧美一区二区三区免费大片| 国产一区二区三区久久| 久久人人爽人人| 亚洲国产成人久久| 欧美日韩网址| 亚洲午夜一二三区视频| 国产精品自拍三区| 久久精品理论片| 久久综合久久88| 亚洲黄色天堂| 欧美日在线观看| 午夜精品久久久久影视| 国产综合色一区二区三区 | 国产欧美在线观看| 久久久久久穴| 亚洲日本va午夜在线电影| 欧美日韩亚洲一区二| 欧美一区二区视频在线观看| 在线观看国产精品网站| 欧美日韩国产成人在线91| 亚洲欧美一区二区三区极速播放| 国产在线播精品第三| 欧美成人一区二区在线| 亚洲一级高清| 精品999日本| 欧美日韩在线免费视频| 欧美主播一区二区三区| 亚洲精品极品| 国产精品视频yy9299一区| 久久一区二区三区av| 一区二区三区精密机械公司| 国产亚洲欧美另类中文| 欧美精品在线一区| 欧美在线免费观看| 亚洲精品视频在线观看网站| 国产日韩欧美一区| 欧美黄网免费在线观看| 先锋影音一区二区三区| 亚洲日本一区二区| 国产精品一区视频| 欧美插天视频在线播放| 亚洲欧美日韩第一区| 伊人精品成人久久综合软件| 欧美午夜精彩| 麻豆精品视频在线| 午夜精品久久久久久久| 91久久精品国产91久久| 国产欧美日韩免费看aⅴ视频| 欧美国产先锋| 久久久精品动漫| 亚洲视频日本| 亚洲精品国产无天堂网2021| 国产欧美日韩在线播放| 欧美日韩一二区| 欧美.www| 久久久久久日产精品| 亚洲一区二区三| 亚洲欧洲在线一区| 国产中文一区二区三区| 欧美午夜精品久久久久久久| 蜜桃精品久久久久久久免费影院| 亚洲欧美在线x视频| 日韩手机在线导航| 在线免费一区三区| 国产欧美日韩在线| 欧美日韩系列| 欧美xx视频| 久久久久久久久久久一区| 亚洲欧美韩国| 在线亚洲一区| 亚洲精品国产欧美| 亚洲第一二三四五区| 国产一区二区三区高清播放| 欧美午夜视频网站| 欧美久久一级| 蜜臀av在线播放一区二区三区 | 欧美91大片| 久久久av水蜜桃| 欧美专区中文字幕| 亚洲欧美激情四射在线日 | 欧美中文日韩| 亚洲欧美国产va在线影院| 亚洲免费成人av| 亚洲黄色影院| 在线看片成人| 激情综合久久| 韩国一区二区三区美女美女秀| 国产精品丝袜91| 欧美香蕉视频| 欧美色图首页| 欧美午夜视频在线| 欧美午夜一区二区| 欧美三区在线视频| 欧美激情一区二区三区高清视频| 另类酷文…触手系列精品集v1小说| 久久成人精品无人区| 欧美一区二区大片| 欧美一区二区三区免费看| 午夜日韩av| 欧美在线一二三区| 欧美一区二区三区精品| 欧美在线观看www| 久久精品国产999大香线蕉| 久久国产欧美| 久久久久久婷| 老司机精品久久| 欧美 日韩 国产精品免费观看| 免费精品视频| 欧美黑人在线播放|