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发布于博客园

 

posted @ 2024-04-03 13:24  快乐的凡人721  阅读(133)  评论(0编辑  收藏  举报