文章目錄
通過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 的對象使用 PortMapper 從 HTTP 重定向到 HTTPS 或者從 HTTPS 重定向到 HTTP 。 默認情況下,Spring Security 使用一個PortMapperImpl 映射 HTTP 端口8080 到 HTTPS 端口8443 ,HTTP 端口80 到 HTTPS 端口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.0 或OpenID 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(); }
-
注銷操作
因為我們使用的是
WebSecurityConfigurerAdapter
,logout
功能默認就有,不需要我們自己編碼,只需要訪問/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
,它很適用於 內存
、JDBC
、LDAP
,或者增加一個 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 object
的Object
對象,在 AccessDecisionManager 、AccessDecisionVoter
中是通用的,代表用戶想訪問的東西,大部分時候都代表一個 web
資源,或者一個 java
類的方法 ;
ConfigAttribute
接口也是通用的,是安全對象的封裝,里面封裝一些元數據,用於確定訪問 Object
所需要的權限 ;它只有一個方法,返回 String
,代表開發者制定的規則,誰可以訪問 Object
;其中返回的字符串格式,一般都是以 ROLE_
為前綴的角色名稱,比如 ROLE_ADMIN
、ROLE_AUDIT
等;
使用 SPEL
表達式 的 ConfigAttribute
是很常見的,比如 isFullyAuthenticated()&& hasRole('FOO')
,這些表達式被 AccessDecisionVoter
所支持,它可以處理這些表達式,並且創建對應的上下文;
如果你想擴展 SPEL
的表達式范圍,需要自定義實現 SecurityExpressionRoot
,有時還需要SecurityExpressionHandler
。
大部分人都使用默認的 AccessDecisionManager
— AffirmativeBased
,它的內部是只要有一個投票通過,即可進行授權訪問,要想進行細致的投票定制,可以自定義實現 AccessDecisionManager
;
Spring Security
的過濾器
Spring Security
是基於 Filter
進行實現的;
當客戶端向應用程序發送請求,Spring Security
根據請求URI
的路徑決定哪些過濾器和哪個servlet
處理它 ;一個 servlet
處理一個請求,所有的 Filter
構建成一個 攔截鏈 ,它們是有序的,其中的任何一個都可以中斷請求,或者修改掉 request 、response
;
Spring Security
本身是在 Spring
容器中是作為作為一個 Filter
的,其類型是 FilterChainProxy
,但是其本身之中,又包含多個 Filter
;
Spring Security
在 Spring
容器中僅僅是一個物理的過濾器,也就是它本身其實不進行任何攔截,都是交給它背后的過濾器;
實際上在 Spring Security
的內部還有一個間接層,它通常作為 DelegatingFilterProxy
安裝在 Spring Security
容器中,它不需要在 Spring
中進行注冊,這個間接層最后委托 FilterChainProxy
在 spring
容器中進行注冊,並且名字必須是 springSecurityFilterChain
; 。
DelegatingFilterProxy
背后的那一堆 Filter
都實現了相同接口,只是具體邏輯不同,並且都可以決定后續的 Filter
是否進行攔截 ;
Spring Security
背后可以管理多個過濾器,但是這些過濾器對 Spring
都是不可見的,並且這些過濾器可以進行分組,然后 Spring Security
將 request
進行派發給第一個匹配的分組;
如圖所示:
這只是 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 }
此注解可以將當前的
Authentication
從SecurityContext
中抽取出來,然后調用getPrincipal()
填充方法參數;至於調用getPrincipal()
獲取的Principal
類型,取決於AuthenticationManager
使用什么類型來進行驗證Authentication
;
如果使用
Spring Security
的HttpServletRequest
中的Principal
,那么Principal
將是Authentication
類型,我們可以向下面這樣操作,獲取User
:@RequestMapping("/foo") public String foo(Principal principal) { Authentication authentication = (Authentication) principal; User = (User) authentication.getPrincipal(); ... // do stuff with user }
-
異步處理安全方法
由於
SecurityContext
是線程綁定的,因此如果要進行任何調用安全方法的后台處理,例如使用@Async
(Runnable, Callable
等等異步執行方法),需要確保傳播上下文。要將
SecurityContext
傳播到@Async
方法,需要提供AsyncConfigurer
並確保Executor
的類型正確:@Configuration public class ApplicationConfiguration extends AsyncConfigurerSupport { @Override public Executor getAsyncExecutor() { return new DelegatingSecurityContextExecutorService(Executors.newFixedThreadPool(5)); } }