Spring Security 官網文檔學習



通過maven向普通的WEB項目中引入spring security


        <!-- spring核心包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-oxm</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>



        <!-- 解決spring security傳遞依賴-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-framework-bom</artifactId>
            <version>5.1.4.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>


        <!-- 項目配置,使用spring security 依賴的相關jar包 1、按照文檔,將可能需要的包都全部添加進來,即使目前有的包,我們暫且沒用到(remoting 遠程訪問的除外) 2、包含 core、web、config、LDAP、OAuth、ACL、CAS、OpenID、test -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-ldap</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-core</artifactId>
            <version>5.1.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-client</artifactId>
            <version>5.1.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-jose</artifactId>
            <version>5.1.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
            <version>1.0.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-acl</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-cas</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-openid</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <version>${spring.security.version}</version>
            <scope>test</scope>
        </dependency>

        <!-- 否則會提示 Error:(9, 8) java: 無法訪問javax.servlet.ServletException 找不到javax.servlet.ServletException的類文件 -->
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
        </dependency>

配置 spring security

示例代碼:

package hello;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        UserDetails user =
             User.withDefaultPasswordEncoder()
                .username("user")
                .password("password")
                .roles("USER")
                .build();

        return new InMemoryUserDetailsManager(user);
    }
}

我們在WebSecurityConfig 類使用了 @EnableWebSecurity 注解 ,該注解提供 spring security的支持以及springMvc的集成支持,配合 @Configuration 注解,即可構成一個 spring security 的配置;

其中我們自己寫的WebSecurityConfig 類必須是擴展了 WebSecurityConfigurerAdapter 抽象類的類;我們就是通過它,告訴 spring security ,哪些用戶需要經過身份驗證,通過何種方式驗證;

其中我們選擇覆寫一些方法,來做一些安全的細節設置,達到上面的目的,比如,配置 :攔截什么URL、設置什么權限,檢驗表單數據 等 ;

userDetailsService() 方法,在內存中添加了一個用戶,並設置了其角色、用戶名、密碼,不多講;


configure(HttpSecurity) 方法

其中 configure(HttpSecurity) 方法里面定義了哪些路徑需要被保護,那些路徑不需要被保護。簡單而言,只有 //home 不需要保護,可以被任何權限、角色查看,其他的路徑都需要進行驗證;

代碼中配置:

protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .anyRequest().authenticated()
            .and()
        .formLogin()
            .and()
        .httpBasic();
}

其實上面的配置,就是一個 xml 配置:

<http>
    <intercept-url pattern="/**" access="authenticated"/>
    <form-login />
    <http-basic />
</http>

每個方法或多或少都對應着一個標簽,想象成這樣以后,就便於我們看懂代碼的配置;

配置規則都是使用方法,諸多方法的具體含義如下(圖內容來自 spring4All 社區 ):

其中有一個特殊的方法 and() ,類比於 xml 的標簽結束符;

方法 說明
openidLogin() 用於基於 OpenId 的驗證
headers() 將安全標頭添加到響應
cors() 配置跨域資源共享( CORS
sessionManagement() 允許配置會話管理
portMapper() 允許配置一個PortMapper(HttpSecurity#(getSharedObject(class))),其他提供 SecurityConfigurer的對象使用 PortMapperHTTP 重定向到 HTTPS 或者從 HTTPS 重定向到 HTTP。 默認情況下,Spring Security使用一個PortMapperImpl映射 HTTP 端口8080HTTPS 端口8443HTTP 端口80HTTPS 端口443
jee() 配置基於容器的預認證。 在這種情況下,認證由Servlet容器管理
x509() 配置基於x509的認證
rememberMe 允許配置“記住我”的驗證
authorizeRequests() 允許基於使用HttpServletRequest限制訪問
requestCache() 允許配置請求緩存
exceptionHandling() 允許配置錯誤處理
securityContext() HttpServletRequests之間的SecurityContextHolder上設置SecurityContext的管理。 當使用WebSecurityConfigurerAdapter時,這將自動應用
servletApi() HttpServletRequest方法與在其上找到的值集成到SecurityContext中。 當使用 WebSecurityConfigurerAdapter時,這將自動應用
csrf() 添加 CSRF 支持,使用WebSecurityConfigurerAdapter時,默認啟用
logout() 添加退出登錄支持。當使用WebSecurityConfigurerAdapter時,這將自動應用。 默認情況是,訪問"URL/logout”,使HTTP Session無效來清除用戶,清除已配置的任何#rememberMe()身份驗證,清除SecurityContextHolder,然后重定向到”/login?success”
anonymous() 允許配置匿名用戶的表示方法。 當與WebSecurityConfigurerAdapter結合使用時,這將自動應用。 默認情況下,匿名用戶將使用org.springframework.security.authentication.AnonymousAuthenticationToken表示,並包含角色 “ROLE_ANONYMOUS”
formLogin() 指定支持基於表單的身份驗證。如果未指定 FormLoginConfigurer#loginPage(String),則將生成默認登錄頁面
oauth2Login() 根據外部OAuth 2.0OpenID Connect 1.0提供程序配置身份驗證
requiresChannel() 配置通道安全。為了使該配置有用,必須提供至少一個到所需信道的映射
httpBasic() 配置 Http Basic 驗證
addFilterAt() 在指定的Filter類的位置添加過濾器

自定義URL身份驗證

  • 缺省登陸路徑

    protected void configure(HttpSecurity http) throws Exception {
        http
        // 表示允許使用HttpServletRequest限制訪問
            .authorizeRequests()
            // 對任何請求都進行身份驗證
                .anyRequest().authenticated()
                .and()
            // 啟動基於表單驗證
            .formLogin()
                // 該路徑允許所有人訪問到
                .permitAll();        
    }
    

    上面的 .formLogin() 沒有配置登陸頁面,spring security 會默認的生成一個頁面

    在這里插入圖片描述

    配置自己的登陸頁面,使用.loginPage("路徑")

     	.formLogin()
    	    .loginPage("/login")
    	    .permitAll();  
    

    自定義的頁面表單中,需要包含下面的代碼:

    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
    

    防止 CSRF 攻擊;

  • 授權請求

    上面配置的只是,一刀切,對除了登陸路徑的其他任何路徑都進行身份驗證,實際開發中,我們應該有細粒度的配置,比如權限控制;

    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()                                                                
            	// 如果URL以 "/resources/**", "/signup", "/about" 開頭,則任何用戶都可以訪問
                .antMatchers("/resources/**", "/signup", "/about").permitAll()         
                 // 如果訪問 "/admin/**" ,則必須具有 ”ADMIN" 角色才可以訪問 ;
                 // 其中,因為調用的就是 hasRole 方法,所以 前綴 ROLE_ 可以省略不寫;
                .antMatchers("/admin/**").hasRole("ADMIN")      
                // 如果訪問 /db/** 開頭的 URL ,則必須同時擁有 "ADMIN" "DBA" 兩個角色; 
                // 對於這樣多角色判斷的,使用 access 連接 
                .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")            
                // 任何URL ,經過上面幾道工序,如果還沒有得到匹配的,則只要求進行身份的驗證;
                // 這里說明下,匹配 URL 的規則是從上到下,按照順序匹配的,如果前面的已經匹配了,則后面的不再進行匹配;
                .anyRequest().authenticated()                                                   
                .and()
            // ...
            .formLogin();
    }
    
  • 注銷操作

    因為我們使用的是 WebSecurityConfigurerAdapterlogout 功能默認就有,不需要我們自己編碼,只需要訪問 /logout 即可,然后 spring security 會自動的幫我們完成注銷操作;

    並且 spring security 會完成以下幾件事:

    1、銷毀對應的會話 session
    2、清楚任何已經配置的 記住我 身份驗證;
    3、清除 SecurityContextHolder
    4、重定向到 /login?logout

    上面都是 spring security 默認的行為(前提是使用 WebSecurityConfigurerAdapter),我們還可以自己定制更細節的行為 :

    protected void configure(HttpSecurity http) throws Exception {
        http
        	// 提供注銷支持,如果使用 `WebSecurityConfigurerAdapter` 則默認提供支持,不需要顯式配置了
            .logout()       
            	// 觸發注銷行為的 URL ,默認是 /logout ,如果啟用了 CSRF ,則該請求必須是 post ;但是默認就是啟用 CSRF ,so 默認就必須使用 post 訪問 ; 
                .logoutUrl("/my/logout")   
                // 觸發注銷操作以后,重定到具體的頁面,默認是 /login?logout 
                .logoutSuccessUrl("/my/index")   
                // 我們指定一個自定義的LogoutSuccessHandler。如果指定了此參數,則忽略logoutSuccessUrl 的配置 
                .logoutSuccessHandler(logoutSuccessHandler)    
                // 指定在注銷時是否使HttpSession無效。默認情況下為 真 
                .invalidateHttpSession(true)                   
                // 添加LogoutHandler。默認情況下,SecurityContextLogoutHandler被添加為最后一個LogoutHandler。 
                .addLogoutHandler(logoutHandler)    
                // 指定注銷成功以后,要刪除的cookie的名字 
                // 同時這里也是顯式添加 CookieClearingLogoutHandler 處理器的快捷方式 
                .deleteCookies(cookieNamesToClear)                                      
                .and()
            ...
    }
    

身份驗證 and 授權

應用程序的安全問題,或許可以簡單的歸結為兩類:身份驗證 and 授權Spring Security 致力於將二者分開,為兩者提供各自的問題解決方法;


Authentication

AuthenticationManager 是用於身份認證的主要策略接口,它僅有一個方法;

public interface AuthenticationManager {

  Authentication authenticate(Authentication authentication)
    throws AuthenticationException;

}

該接口的方法,可以返回下面的三種數據的任何一個:

  • authenticated=true

    如果認為輸入的主體(可以理解為用戶),是有效的,即賬號密碼角色都對,則驗證通過,返回 authenticated=true

  • AuthenticationException

    如果驗證沒有通過沒,則拋出一個 AuthenticationException 異常,這個異常是一個運行時異常,官網文檔里面建議,不需要我們代碼中捕捉然后處理,我們應該做一個統一處理,讓其跳到特定頁面(網站全局異常頁面

  • null

    如果無法決定是否驗證成功,則返回 null,為什么會出現無法決定呢,因為一個 AuthenticationManager 實例,可能只管理一部分身份的驗證,另外一些身份驗證有其他 AuthenticationManager 實例驗證,因此這時候,就會返回一個 null


AuthenticationProvider

AuthenticationManager最常用的實現是ProviderManager,它委托一系列AuthenticationProvider實例。AuthenticationProvider有點像AuthenticationManager,但它有一個額外的方法允許調用者查詢它是否支持給定的身份驗證類型:

public interface AuthenticationProvider {

	Authentication authenticate(Authentication authentication)
			throws AuthenticationException;

	boolean supports(Class<?> authentication);

}

一個 ProviderManager 支持多種不同驗證機制,通過委托多個小弟 AuthenticationProvider 來完成,小弟的方法 supports(Class<?> authentication) ,用於校驗傳給它的驗證身份類型是否支持,如果不支持,則返回 false, 然后 ProviderManager就會跳過該小弟,詢問下一位小弟;(有待考證)

ProviderManager 有一個可選的父級,如果所有小弟都返回 null,它可以參考。如果父級不可用,則null驗證會導致AuthenticationException

有時候,web應用有多個受保護的邏輯組,比如:/db/**/login/** 等等,每個受保護的邏輯組可能有他們自己的專用的 AuthenticationManager,通常情況下這些 AuthenticationManager ,都是 ProviderManager,他們共享一個父級——這個父級是一個全局資源,充當所有的 ProviderManager 的后備資源,也是一個 ProviderManager,走投無路的時候,就去找老爹;


自定義身份驗證器

spring security 提供了配置幫助程序,用於快速獲取程序中設置的身份管理;其中,最常用的是 AuthenticationManagerBuilder ,它很適用於 內存JDBCLDAP ,或者增加一個 UserDetailsService

配置全局的 AuthenticationManager

@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {

   ... // web stuff here

  @Autowired
  public initialize(AuthenticationManagerBuilder builder, DataSource dataSource) {
    builder.jdbcAuthentication().dataSource(dataSource).withUser("dave")
      .password("secret").roles("USER");
  }

}

上面的配置中,關於 AuthenticationManagerBuilder 的用法是一種廣泛的使用方式;它被使用 @Autowired 注入到方法中,這將導致它可以構建全局的 AuthenticationManager

當然也有另外一種寫法:

@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {

  @Autowired
  DataSource dataSource;

   ... // web stuff here

  @Override
  public configure(AuthenticationManagerBuilder builder) {
    builder.jdbcAuthentication().dataSource(dataSource).withUser("dave")
      .password("secret").roles("USER");
  }

}

這里是覆蓋父類方法,這樣的 AuthenticationManagerBuilder 只能被用來構建局部的 AuthenticationManager ,構建出的 AuthenticationManager 將是全局 AuthenticationManager 的小弟;

springBoot 中可以使用 @Autowired 將一個全局的 bean 直接注入到另外一個 bean 里面 ,但是不能對局部的 bean 使用這種操作;

springBoot 中,如果自己沒有全局的自定義身份驗證,它默認提供一個全局的 AuthenticationManager,默認的 AuthenticationManager的安全性不需要我們去擔心;我們可以在局部的 AuthenticationManager 里面做配置,從而不去影響到全局的 AuthenticationManager


授權

身份認證成功以后,就需要我們進行授權了;

spring Security 中的對應的核心類是 AccessDecisionManager ,框架本身有三個默認實現,並且這三個實現都委托給 AccessDecisionVoter 管理,跟身份驗證的 AuthenticationProvider 委托給 ProviderManager

AccessDecisionVoter 如下:

public interface AccessDecisionVoter<S> {
    int ACCESS_GRANTED = 1;
    int ACCESS_ABSTAIN = 0;
    int ACCESS_DENIED = -1;

  	boolean supports(ConfigAttribute attribute);

	boolean supports(Class<?> clazz);

	int vote(Authentication authentication, S object,
        Collection<ConfigAttribute> attributes);
}

其中 S objectObject 對象,在 AccessDecisionManager 、AccessDecisionVoter 中是通用的,代表用戶想訪問的東西,大部分時候都代表一個 web 資源,或者一個 java 類的方法 ;

ConfigAttribute 接口也是通用的,是安全對象的封裝,里面封裝一些元數據,用於確定訪問 Object 所需要的權限 ;它只有一個方法,返回 String ,代表開發者制定的規則,誰可以訪問 Object ;其中返回的字符串格式,一般都是以 ROLE_ 為前綴的角色名稱,比如 ROLE_ADMINROLE_AUDIT 等;

使用 SPEL 表達式 的 ConfigAttribute 是很常見的,比如 isFullyAuthenticated()&& hasRole('FOO') ,這些表達式被 AccessDecisionVoter 所支持,它可以處理這些表達式,並且創建對應的上下文;

如果你想擴展 SPEL 的表達式范圍,需要自定義實現 SecurityExpressionRoot,有時還需要SecurityExpressionHandler

大部分人都使用默認的 AccessDecisionManagerAffirmativeBased,它的內部是只要有一個投票通過,即可進行授權訪問,要想進行細致的投票定制,可以自定義實現 AccessDecisionManager


Spring Security 的過濾器

Spring Security 是基於 Filter 進行實現的;

當客戶端向應用程序發送請求,Spring Security 根據請求URI的路徑決定哪些過濾器和哪個servlet處理它 ;一個 servlet 處理一個請求,所有的 Filter 構建成一個 攔截鏈 ,它們是有序的,其中的任何一個都可以中斷請求,或者修改掉 request 、response

Spring Security 本身是在 Spring 容器中是作為作為一個 Filter 的,其類型是 FilterChainProxy,但是其本身之中,又包含多個 Filter

在這里插入圖片描述

Spring SecuritySpring容器中僅僅是一個物理的過濾器,也就是它本身其實不進行任何攔截,都是交給它背后的過濾器;

實際上在 Spring Security 的內部還有一個間接層,它通常作為 DelegatingFilterProxy安裝在 Spring Security 容器中,它不需要在 Spring 中進行注冊,這個間接層最后委托 FilterChainProxyspring 容器中進行注冊,並且名字必須是 springSecurityFilterChain; 。

DelegatingFilterProxy 背后的那一堆 Filter 都實現了相同接口,只是具體邏輯不同,並且都可以決定后續的 Filter 是否進行攔截 ;

Spring Security 背后可以管理多個過濾器,但是這些過濾器對 Spring 都是不可見的,並且這些過濾器可以進行分組,然后 Spring Securityrequest 進行派發給第一個匹配的分組;

如圖所示:
在這里插入圖片描述

這只是 Spring Security 分配請求的一種方法,用此種方法分配時,只有一組攔截鏈可以處理請求 ;


創建和自定義 攔截鏈

SpringBoot 中有一個默認的最低級別的攔截器 ,它攔截 /** 請求,預定義的順序為 SecurityProperties.BASIC_AUTH_ORDER,如果不想使用它的話,設置 SecurityProperties.BASIC_AUTH_ORDER = false 即可 ;

但是一般都將其作為一個基准,便於我們自己創建攔截器的時候,定義順序 ;

比如新建一個攔截器,只需要繼承 WebSecurityConfigurerAdapter 或 WebSecurityConfigurer 即可,然后定義一個順序,這時候就可以使用 SecurityProperties.BASIC_AUTH_ORDER 作為基准;

使用 @Order(SecurityProperties.BASIC_AUTH_ORDER - 10) 定義順序,數值越少,級別越高 ;

@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.antMatcher("/foo/**")
     ...;
  }
}

其中對於同一組資源可能有多個攔截器設置匹配規則,此時匹配規則重疊,則哪一個攔截器的優先級高,就使用誰 ;


請求匹配調度和授權

一個 WebSecurityConfigurerAdapter 就是一組攔截鏈,使用 http.antMatcher("/foo/**") 決定匹配什么請求;

然后再使用antMatchers("/foo/bar").hasRole("BAR") ,設置訪問該組攔截鏈攔截的URL的角色 ;

@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.antMatcher("/foo/**")
      .authorizeRequests()
        .antMatchers("/foo/bar").hasRole("BAR")
        .antMatchers("/foo/spam").hasRole("SPAM")
        .anyRequest().isAuthenticated();
  }
}

方法安全

前面講的都是基於 url 級別的授權,下面說的授權,粒度更細,是基於方法的 ;

對於 Spring Security 來說,方法也是一種受保護的資源類型 ;

在應用程序的頂級配置中,啟動方法安全性 @EnableGlobalMethodSecurity(securedEnabled = true)

@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SampleSecureApplication {
}

然后在需要保護的方法上配置:

@Service
public class MyService {

  @Secured("ROLE_USER")
  public String secure() {
    return "Hello Security";
  }

}

這樣配置以后,如果Spring創建了這種類型的 Bean 對象,那么它將被代理,並且調用者必須在實際執行該方法之前通過安全攔截器。如果訪問被拒絕,則調用者將獲得AccessDeniedException而不是實際的方法結果 ;

還有其他注釋可用於強制執行安全約束的方法,特別是@PreAuthorize@PostAuthorize,它們允許您編寫包含對方法參數和返回值的引用的表達式。


spring Security 線程問題

  • SecurityContext

    Spring Security 是線程綁定的,最基本的組件是 SecurityContext 里面包含 Authentication,可以通過下面的方式,獲取和修改 SecurityContext

    SecurityContext context = SecurityContextHolder.getContext();
    Authentication authentication = context.getAuthentication();
    assert(authentication.isAuthenticated);
    
  • @AuthenticationPrincipal 注解

    @RequestMapping("/foo")
    public String foo(@AuthenticationPrincipal User user) {
      ... // do stuff with user
    }
    

    此注解可以將當前的 AuthenticationSecurityContext 中抽取出來,然后調用 getPrincipal() 填充方法參數;至於調用 getPrincipal() 獲取的 Principal 類型,取決於 AuthenticationManager 使用什么類型來進行驗證 Authentication

    如果使用 Spring SecurityHttpServletRequest中的 Principal ,那么 Principal 將是Authentication類型,我們可以向下面這樣操作,獲取 User

    @RequestMapping("/foo")
    public String foo(Principal principal) {
      Authentication authentication = (Authentication) principal;
      User = (User) authentication.getPrincipal();
      ... // do stuff with user
    }
    
  • 異步處理安全方法

    由於SecurityContext是線程綁定的,因此如果要進行任何調用安全方法的后台處理,例如使用 @AsyncRunnable, Callable等等異步執行方法),需要確保傳播上下文。

    要將SecurityContext傳播到@Async方法,需要提供AsyncConfigurer並確保Executor的類型正確:

    @Configuration
    public class ApplicationConfiguration extends AsyncConfigurerSupport {
    
      @Override
      public Executor getAsyncExecutor() {
        return new DelegatingSecurityContextExecutorService(Executors.newFixedThreadPool(5));
      }
    
    }
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM