如何为具有 Spring 安全性的 UI 和 REST 端点使用不同的会话创建策略?

How does one use different session creation policies for UI and REST endpoints with Spring Security?

我有一个包含 UI 和一些 REST 端点的应用程序。 UI 使用 SAML 登录(旧的 Spring 安全 SAML 扩展)和使用自定义身份验证的 REST 端点。 REST 端点仅由外部应用程序调用。 对于 REST 端点(“/api/**”),我声明了无状态会话创建策略,而对于其余端点,根本没有会话创建策略(我也尝试使用 ALWAYS,如下例所示) .

在某些 Spring 引导版本之前,不确定是哪个版本,这有效。目前我正在使用 Spring Boot v.2.6.1。 UI 端点从 Http 会话中获取身份验证对象。

但是现在不行了。在使用默认 HttpSessionSecurityContextRepository 实现的 Http 会话中找不到安全上下文对象。已保存但无法恢复

那么是否可以使用两个会话创建策略,一个用于 REST,另一个用于 UI 部分,还是应该以不同的方式处理? 现在看来 UI 也使用了无状态会话创建策略,这不是故意的。 我正在使用两个 WebSecurityConfigurerAdapter 类;一个用于 API,另一个用于 UI。 SAML 登录成功后,重定向 URL 现在包含“;jsessionid=6051854D94A0771BB9B99FE573AA4DFD”参数。可能是因为无国籍政策...?

    protected void configure(HttpSecurity http) throws Exception {

        List<AbstractAuthenticationProcessingFilter> authFilters = new ArrayList<>();
        authFilters.add(new OAuthMacAuthenticationProcessingFilter(authenticationManager(), this.properties));

        ApiAuthenticationProcessingFilter apiAuthenticationProcessingFilter = new ApiAuthenticationProcessingFilter(authenticationManager(),authFilters);

        http
        .csrf()
        .disable()
        .antMatcher("/api/**")
        .authorizeRequests()
        .anyRequest()
        .authenticated()
        .and()
        .exceptionHandling()
        .authenticationEntryPoint((req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED))
        .and()
        .addFilterBefore(apiAuthenticationProcessingFilter, BasicAuthenticationFilter.class)
        .sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

以及 UI 部分

    protected void configure(HttpSecurity http) throws Exception {

        http.securityContext().securityContextRepository(customSessionSecurityContextRepository);

        http
        .httpBasic()
        .authenticationEntryPoint(samlEntryPoint());

        http
        .addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class);

        var auth = http
                .authorizeRequests()
                .antMatchers("/saml/**").permitAll()
                .antMatchers("/loggedout/**").permitAll()
                .antMatchers("/error").permitAll();
                
        auth
        .anyRequest()
        .authenticated();

        http.csrf().disable();

        http.sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.ALWAYS);

        http.headers().frameOptions().sameOrigin();

        http.exceptionHandling().accessDeniedHandler(this.accessDeniedHandler());

        http
        .logout()
        .disable();    // The logout procedure is already handled by SAML filters.
    }

我会自己回答这个问题。上面的代码确实有效。问题出在远端,那天我使用的 IDP 出现了一些问题,导致它没有按预期工作。第二天,它起作用了。