将 Spring 会话 JDBC 添加到我的 Spring 引导项目会破坏 iframe 中的身份验证

Adding Spring Session JDBC to my Spring Boot project breaks authentication in an iframe

我目前正在构建一个 Spring 启动 Web 应用程序(2.3.1,但在版本 2.1.7 和 2.1.5 中也观察到了以下问题),该应用程序受到 Spring 安全性的保护.我主要使用默认设置(例如嵌入式 Tomcat、嵌入式 H2 数据库、Spring Web-MVC)。我使用以下代码对许可 POST 映射进行了一些自定义身份验证:

UsernamePasswordAuthenticationToken authentication = ...;
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(authentication);

这很好用。因为我想用它来进行学生考试,所以我想通过身份验证来保持会话,所以万一服务器软件由于某种原因崩溃或宕机,当服务器再次出现时,没有人需要重新进行身份验证。

为此我使用了 Spring 会话 JDBC。我将以下依赖项添加到我的 pom.xml:

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

以及我的 application.properties

的以下行
spring.session.store-type=jdbc

我在本地测试时一切正常。我看到会话出现在我的数据库中,一切正常。然而,在实践中,应用程序是从 iframe 开始的:学习管理系统执行 LTI 启动,这是一个 POST 请求,其响应在 iframe 中结束。当我尝试进行测试部署时,出现 403 错误,表明我的请求未通过身份验证。经过一天令人沮丧的调试后,我最终能够将此问题确定为将 spring-session-jdbc 添加到我的项目会导致 Web 应用程序在 iframe 的每个请求上发送新的会话 ID cookie。 .如果我在没有 iframe 的情况下重复相同的请求,则相同的应用程序可以正常工作。如果我删除 spring-session-jdbc 依赖项,应用程序在 iframe 中也能正常工作。

如果我在 iframe 中执行身份验证,我会在 Spring 安全调试日志中看到以下内容(exec-1 是身份验证成功的地方,exec-2 是 iframe 在被执行后执行的请求由 exec-1 请求重定向):

2020-06-24 22:14:02.015 DEBUG 7474 --- [nio-8031-exec-1] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@671fa7ec
2020-06-24 22:14:02.015 DEBUG 7474 --- [nio-8031-exec-1] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2020-06-24 22:14:02.063 DEBUG 7474 --- [nio-8031-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2020-06-24 22:14:02.113 DEBUG 7474 --- [nio-8031-exec-2] o.s.security.web.FilterChainProxy        : /launch/development at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-06-24 22:14:02.113 DEBUG 7474 --- [nio-8031-exec-2] o.s.security.web.FilterChainProxy        : /launch/development at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-06-24 22:14:02.114 DEBUG 7474 --- [nio-8031-exec-2] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2020-06-24 22:14:02.114 DEBUG 7474 --- [nio-8031-exec-2] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.

当我在常规浏览器 运行 中使用相同的 运行 应用程序执行相同的请求时 window,Spring 安全调试日志显示如下:

2020-06-24 22:18:10.518 DEBUG 7474 --- [nio-8031-exec-5] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@671fa7ec
2020-06-24 22:18:10.518 DEBUG 7474 --- [nio-8031-exec-5] w.c.HttpSessionSecurityContextRepository : SecurityContext 'org.springframework.security.core.context.SecurityContextImpl@e1239cdc: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@e1239cdc: Principal: testuserid; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_INSTRUCTOR' stored to HttpSession: 'org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper@66670854
2020-06-24 22:18:10.536 DEBUG 7474 --- [nio-8031-exec-5] o.s.s.w.a.ExceptionTranslationFilter     : Chain processed normally
2020-06-24 22:18:10.536 DEBUG 7474 --- [nio-8031-exec-5] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2020-06-24 22:18:10.590 DEBUG 7474 --- [nio-8031-exec-6] o.s.security.web.FilterChainProxy        : /home at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-06-24 22:18:10.591 DEBUG 7474 --- [nio-8031-exec-6] o.s.security.web.FilterChainProxy        : /home at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-06-24 22:18:10.592 DEBUG 7474 --- [nio-8031-exec-6] w.c.HttpSessionSecurityContextRepository : Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: 'org.springframework.security.core.context.SecurityContextImpl@82a4fa0f: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@82a4fa0f: Principal: testuserid; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_INSTRUCTOR'

对我来说这似乎很奇怪:如果这纯粹是浏览器问题,那么我是否使用 spring-session-jdbc 应该无关紧要。如果 spring-session-jdbc 如何存储我的身份验证有问题,那么它是否发生在 iframe 中应该无关紧要。我在这里缺少什么吗?我是不是遇到了错误?

我可以尝试一种变通方法,在 iframe 中使用一些 javascript 让 POST 出现在空白选项卡中,但这感觉很丑陋。

一般来说,我建议您不要在 iframe 中使用您的应用程序。
这会带来安全风险,您可以在 this answer.

中阅读更多相关信息

现在解释您所看到的行为。
Spring 安全使用 Session cookie 来存储用户的会话。
Cookie 与域关联,因此,例如,如果存在与域 whosebug.com 关联的 cookie,则该 cookie 将包含在对 stackoverlow.com.

的任何请求中

为了控制这种行为,cookies 还有一个名为 SameSite 的属性。
SameSite attribute 可以有 3 个值,NoneLaxStrict
当值为 None 时,它的行为如上所述(包含在所有请求中)。
当值为 Lax 时,cookie 将仅包含在顶级导航 GET 请求中。

当包含 Spring 会话依赖项时,Session cookie SameSite 属性默认设置为 Lax

由于在 iframe 中呈现应用程序不是顶级导航,因此 Session cookie 不包含在对 iframe 的请求中,应用程序无法知道用户已登录.

您可以使用 Spring 会话将 SameSite 属性显式设置为 None
同样,我会警告不要这样做,因为它会使您的应用程序容易受到 CSRF 和点击劫持攻击。
如果在考虑了安全隐患之后,您认为有必要将 SameSite 属性设置为 None,您可以通过在依赖项中包含 Spring Session 并创建一个 custom CookieSerializer.