Spring Security 为什么表单登录主体在Iframe中为空?Same Site None cookies不适用于Safari和Chrome 80

sh7euo9m  于 2023-03-02  发布在  Spring
关注(0)|答案(1)|浏览(144)

我正在尝试在iframe中启用我的Sping Boot 网站。我有安全配置、请求过滤器、表单登录和OAuth2。我的表单登录在浏览器中运行良好,但当我从iframe登录时,我可以看到从浏览器登录的相同用户的JSESSIONIDnull
iframe的日志:

UsernamePasswordAuthenticationToken [Principal=test@example.com, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[ROLE_USER]]

浏览器日志:

[Authentication=UsernamePasswordAuthenticationToken [Principal=test@example.com, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=04E5DB0982A859E5C215294EF96814FD], Granted Authorities=[ROLE_MANAGER, ROLE_USER]]]

下面是我的安全配置:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .cors().disable()
        .authorizeHttpRequests(authorize -> authorize
            .requestMatchers("/app_resources/**", "/signup", "/favicon.ico", "/login", "/logout", "/signup",
                            "/oauth2/authorization/**", "/pages/**", "/login/oauth2/code/**", "/login/oauth2/code/**",
                             "/product/authenticate", "/cdnjs.cloudflare.com/**").permitAll()
            .requestMatchers("/admin/**").hasRole("ADMIN")
            .anyRequest().authenticated()
        )
        .addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class)
        .formLogin(form -> form
            .loginPage("/auth")
            .defaultSuccessUrl("/index")
            .failureHandler(authenticationfailure())
            .successHandler(new AuthSuccessHandler()).permitAll()
        )
        .logout(out -> out
            .logoutUrl("/logout")
        )
        .sessionManagement()                         
            .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
            .and()
        .authenticationProvider(authProvider())
        .oauth2Login()
            .loginPage("/auth")
            .defaultSuccessUrl("/homesuccess")
            .successHandler(new AuthSuccessHandler());

    http
        .headers()
            .frameOptions().disable();
    return http.build();
}

下面是我的AuthenticationSuccesHandler

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
    Authentication authentication) throws IOException, ServletException {
    System.out.println("onAuthenticationSuccess >>>>>>>>>>>>>>"+authentication.toString());
    request.getSession(false).setMaxInactiveInterval(604800);
    RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    redirectStrategy.sendRedirect(request, response, "/");
}

不太确定iframe内部出了什么问题。我可以看到我的登录页面,我可以看到AuthennticationSuccessHandler中的用户密码令牌对象,但在将其重定向到 Jmeter 板后,安全过滤器中的主体对象突然变成了null
MVC配置:

@EnableWebMvc
@Configuration
public class MvcConfig implements WebMvcConfigurer{
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

        VersionResourceResolver versionResolver = new VersionResourceResolver().addContentVersionStrategy("/**/*.js",
                "/**/*.css", "/**/*.png");
        registry.addResourceHandler("/app_resources/**", "/favicon.ico", "/firebase-messaging-sw.js", "/manifest.json",
                "/salesken.png")
                .addResourceLocations("/assets/js/",
                        "/assets/css/","/assets/img/","/")
                .setCacheControl(CacheControl.maxAge(1, TimeUnit.MINUTES)).resourceChain(true)
                .addResolver(versionResolver);

    }   
    
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.defaultContentType(MediaType.APPLICATION_JSON);
    }
    
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver bean = new InternalResourceViewResolver();
        bean.setPrefix("pages/");
        bean.setSuffix(".jsp");
        return bean;
    }   
}

即使我尝试将身份验证对象放入会话中,也没有设置此对象,其值在请求过滤器中为null

f3temu5u

f3temu5u1#

Chrome使用“Lax”作为default same-site cookie policy
要在Sping Boot 2.5.0打开的情况下通过将SameSite标志设置为none来修复此问题,请添加以下内容:

import org.springframework.boot.web.servlet.server.CookieSameSiteSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class SameSiteNoneConfiguration {
    @Bean
    public CookieSameSiteSupplier applicationCookieSameSiteSupplier() {
        return CookieSameSiteSupplier.ofNone();
    }
}

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
</dependency>

仅适用于https,因为cookie必须是安全的。Sping Boot 会话cookie已经是httpOnly。
Sping Boot 2.5.0之前版本没有CookieSameSiteSupplier,因此必须创建以下内容。需要Spring Boot v2.1.0.RELEASE及更高版本。

@Component
@AllArgsConstructor
public class SameSiteInjector {

  private final ApplicationContext applicationContext;

  @EventListener
  public void onApplicationEvent(ContextRefreshedEvent event) {
    DefaultCookieSerializer cookieSerializer = applicationContext.getBean(DefaultCookieSerializer.class);
    cookieSerializer.setSameSite("none");
  }
}

具有以下依赖关系:

<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session-core</artifactId>
</dependency>

要在Tomcat上执行此操作-从8.5.42+. Edit %TOMCAT_HOME%/conf/context.xml开始,更新以下行中的CookieProcessor元素,例如在HTTP响应头的set-cookie中设置SameSiteCookies。

<CookieProcessor className="org.apache.tomcat.util.http.LegacyCookieProcessor" sameSiteCookies="none" />

因为你正在编辑%TOMCAT_HOME%/conf/context.xml,它会对所有安装的webapps都这样做。为了避免这种情况,你需要破解META-INF文件夹,创建一个context.xml,并把上面的代码片段放进去。

相关问题