spring security 6.0.8(boot 3.0.13)自定義 filter 踩坑-已解決

spring boot 3.0.13(3.1.10)

spring security 6.0.8(6.1.8)

--

 

官方文檔:

https://docs.spring.io/spring-security/reference/index.html

寫文時最新爲 6.2.3 。 

 

說明,先是用 spring boot 3.1.10 測試,失敗,降低到 3.0.13 仍然失敗。

 

開發

建立了 AppLoginFilter,實現了 attemptAuthentication 方法。

在 AppSecurityConfig 配置了:

@Configuration
@RequiredArgsConstructor
@Slf4j
public class AppSecurityConfig {

    @Bean
    public AppLoginFilter appLoginFilter(AuthenticationManager authenticationManager) throws Exception {
        AppLoginFilter appLoginFilter = new AppLoginFilter();

        appLoginFilter.setAuthenticationSuccessHandler(new AppLoginSuccessHandler());
        appLoginFilter.setAuthenticationFailureHandler(new AppLoginFailureHandler());

        appLoginFilter.setAuthenticationManager(authenticationManager);

        // todo more

        return appLoginFilter;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http,
                                                   AppLoginFilter appLoginFilter,
                                                   AuthenticationManager authenticationManager)

throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.csrf(csrf -> csrf.disable())
.cors(cors -> cors.disable())
.addFilterAt(appLoginFilter, UsernamePasswordAuthenticationFilter.class)
.formLogin(formlogin -> formlogin.disable())
.httpBasic(httpbasic -> httpbasic.disable())
;
 
return http.build();
}
 
// more
 
}

登錄接口:/app/login。

另外開發了測試接口:/test/getAppName。

 

發現問題

測試時,/app/login 正常調用,也有 Set-Cookie 響應頭。

可是,使用  Set-Cookie 的 Cookie 訪問 /test/getAppName 接口時,被拒絕了。

C:\Users\Mi>curl -v -X POST http://localhost:29001/app/login -H "Content-Type: application/json"  -d "{\"username\": \"user\", \"password\":\"111\"}"
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying [::1]:29001...
* Connected to localhost (::1) port 29001
> POST /app/login HTTP/1.1
> Host: localhost:29001
> User-Agent: curl/8.4.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 38
>
< HTTP/1.1 200
< Set-Cookie: JSESSIONID=43935860065AA00C85F6256735D1F368; Path=/; HttpOnly
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 0
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Tue, 02 Apr 2024 14:18:24 GMT
<
{"code":200,"data":true,"message":"登錄成功","timestamp":1712067504358}* Connection #0 to host localhost left intact

C:\Users\Mi>
C:\Users\Mi>curl -v http://localhost:29001/test/getAppName -H "Cookie: JSESSIONID=43935860065AA00C85F6256735D1F368"
*   Trying [::1]:29001...
* Connected to localhost (::1) port 29001
> GET /test/getAppName HTTP/1.1
> Host: localhost:29001
> User-Agent: curl/8.4.0
> Accept: */*
> Cookie: JSESSIONID=43935860065AA00C85F6256735D1F368
>
< HTTP/1.1 403
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 0
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Length: 0
< Date: Tue, 02 Apr 2024 14:18:42 GMT
<
* Connection #0 to host localhost left intact

 

後臺出現一條警告日誌:

WARN 5256 --- [io-29001-exec-3] o.s.w.s.h.HandlerMappingIntrospector     : Cache miss for REQUEST dispatch to '/test/getAppName' (previous null). Performing MatchableHandlerMapping lookup. This is logged once only at WARN level, and every time at TRACE.

忽略該信息。

 

解決方案

給 自定義 filter 設置 SecurityContextRepository 爲 HttpSessionSecurityContextRepository 即可。

SecurityContextRepository repo = new HttpSessionSecurityContextRepository();

appLoginFilter.setSecurityContextRepository(repo);

添加後,再次測試,成功。

 

說明,該方法設置了 AbstractAuthenticationProcessingFilter 的 securityContextRepository 屬性,其默認值爲:

private SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();

 

調試過程

在 自定義的 AppLoginFilter 的 父類 AbstractAuthenticationProcessingFilter#325 中有一個 successfulAuthentication 函數,其中會調用:

this.securityContextHolderStrategy.setContext(context);

調試發現,這裏的 this.securityContextHolderStrategy 值爲 RequestAttributeSecurityContextRepository 實例,而不是 HttpSessionSecurityContextRepository 實例。

而在 SecurityContextHolderFilter 中,其值爲 DelegatingSecurityContextRepository 實例——包含兩個 SecurityContextRepository,其中一個是  HttpSessionSecurityContextRepository 。

 

官方文檔:SecurityContextRepository

Persisting Authentication # SecurityContextRepository

https://docs.spring.io/spring-security/reference/servlet/authentication/persistence.html

The HttpSessionSecurityContextRepository associates the SecurityContext to the HttpSession. 

The RequestAttributeSecurityContextRepository saves the SecurityContext as a request attribute to make sure the SecurityContext is available for a single request that occurs across dispatch types that may clear out the SecurityContext.

 

---END---

ben發佈於博客園

本文鏈接:

https://www.cnblogs.com/luo630/p/18111645

 

ben發佈於博客園

ben發佈於博客園

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章