问题是
在DefaultOAuth2RequestFactory
的自定义实现将当前的AuthorizationRequest
替换为已保存的AuthorizationRequest
后,HttpSession
变为null
。这会导致对/oauth/token
的后续请求失败,因为/oauth/token
端点之前的Spring Security过滤器链中的CsrfFilter无法在null
session
与request
的Csrf token
进行比较。
出错时的控制流
下面的流程图说明了步骤14和步骤15以某种方式null
-ify HttpSession
。(或者可能不匹配JSESSIONID
。)在步骤14中,CustomOAuth2RequestFactory.java
开始处的SYSO
表明确实存在一个HttpSession
,它实际上包含正确的CsrfToken
。然而,不知何故,当步骤15触发从localhost:8080/login
url的客户端返回localhost:9999/oauth/token
端点的调用时,HttpSession
已经变成了null
。
断点被添加到下面的调试日志中提到的HttpSessionSecurityContextRepository
的每一行。(它位于authserver
eclipse项目的Maven Dependencies
文件夹中。)这些断点确认了在下面的流程图中对/oauth/token
发出最终请求时,HttpSession
是null
。(流程图左下角)null
HttpSession
可能是由于自定义DefaultOAuth2RequestFactory
代码运行后,保留在浏览器中的JSESSIONID
过期。
如何修复此问题,以便在流程图中的步骤15结束后,在对/oauth/token
端点的最后调用期间保持相同的HttpSession
?
x1c 0d1x的数据
相关代码和日志
我们可以猜测null
session
是由于1.)CustomOAuth2RequestFactory
中的代码没有在浏览器中更新JSESSIONID
,或者2.)HttpSession
实际上被null
-化了。
Sping Boot 调试日志在Step 15之后对/oauth/token
的调用明确指出,到那时没有HttpSession
,可以如下所示:
2016-05-30 15:33:42.630 DEBUG 13897 --- [io-9999-exec-10] o.s.security.web.FilterChainProxy : /oauth/token at position 1 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2016-05-30 15:33:42.631 DEBUG 13897 --- [io-9999-exec-10] o.s.security.web.FilterChainProxy : /oauth/token at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2016-05-30 15:33:42.631 DEBUG 13897 --- [io-9999-exec-10] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2016-05-30 15:33:42.631 DEBUG 13897 --- [io-9999-exec-10] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
2016-05-30 15:33:42.631 DEBUG 13897 --- [io-9999-exec-10] o.s.security.web.FilterChainProxy : /oauth/token at position 3 of 12 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2016-05-30 15:33:42.631 DEBUG 13897 --- [io-9999-exec-10] 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@2fe29f4b
2016-05-30 15:33:42.631 DEBUG 13897 --- [io-9999-exec-10] o.s.security.web.FilterChainProxy : /oauth/token at position 4 of 12 in additional filter chain; firing Filter: 'CsrfFilter'
2016-05-30 15:33:42.644 DEBUG 13897 --- [io-9999-exec-10] o.s.security.web.csrf.CsrfFilter : Invalid CSRF token found for http://localhost:9999/uaa/oauth/token
2016-05-30 15:33:42.644 DEBUG 13897 --- [io-9999-exec-10] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2016-05-30 15:33:42.644 DEBUG 13897 --- [io-9999-exec-10] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
字符串
在您的计算机上重现问题
您可以通过以下简单的步骤在任何计算机上重现问题:
1.下载该应用程序的压缩版本(编者注:不幸的是,这不再可用)。
1.通过键入以下内容解压缩应用程序:tar -zxvf oauth2.tar(4).gz
1.通过导航到oauth2/authserver
,然后键入mvn spring-boot:run
,启动authserver
应用程序。
1.导航到oauth2/resource
,然后键入mvn spring-boot:run
,启动resource
应用程序
1.导航到oauth2/ui
,然后键入mvn spring-boot:run
,启动ui
应用程序
1.打开Web浏览器并导航到http : // localhost : 8080
1.单击Login
,然后输入Frodo
作为用户,MyRing
作为密码,然后单击提交。
1.输入Pin Code
作为Pin Code
,然后单击提交。这将触发上面显示的错误。
Sping Boot 调试日志会显示大量的SYSO
,它给出了流程图中每个步骤的XSRF-TOKEN
和HttpSession
等变量的值。SYSO
帮助分割调试日志,以便更容易解释。并且所有的SYSO
都是由一个类调用其他类来完成的,因此,您可以操纵SYSO
生成类来更改控制流中任何地方的报告。SYSO
生成类的名称是TestHTTP
,其源代码可以在同一个demo
包中找到。
使用调试器
1.选择正在运行authserver
应用程序的终端窗口,然后键入Ctrl-C
以停止authserver
应用程序。
1.将三个应用程序(authserver
、resource
和ui
)作为现有maven项目导入eclipse。
1.在authserver
应用的eclipse Project Explorer中,单击展开**Maven Dependencies
文件夹**,然后向下滚动,单击以展开**Spring-Security-web...
jar**如下图中以橙子圈出的。然后滚动以查找并展开org.springframework.security.web.context
包。然后双击以打开在屏幕截图中以蓝色突出显示的HttpSessionSecurityContextRepository
类下面。在这个类中的每一行添加断点。你可能想对同一个包中的SecurityContextPersistenceFilter
类做同样的事情。这些断点将使你能够看到HttpSession
的值,在控制流结束之前,它目前变成了null
,但是需要具有可以Map到XSRF-TOKEN
的有效值,以便解析此OP。
的
1.在应用程序的demo
包中,在CustomOAuth2RequestFactory.java
中添加断点。然后Debug As... Spring Boot App
启动调试器。
1.然后重复上面的步骤6到8。您可能希望在每次新尝试之前清除浏览器的缓存。您可能希望打开浏览器开发人员工具的网络选项卡。
2条答案
按热度按时间4ktjp1zp1#
在最后一次调用
localhost :9999/uaa/oauth/token
时,您的authserver
应用中的会话不为空。不仅存在会话,而且有效会话的JSESSIONID
和csrf
令牌匹配控制流中存在的值,该值位于用户提交正确pin的点和向/oauth/token
发出失败请求的点之间。问题是有两个
JSESSIONID
值,并且选择了两个值中的错误值来进入对/oauth/token
的调用。因此,解决方案应该来自修改过滤器以删除坏的JSESSIONID
,以便可以发送正确的值。下文将进行总结:
HttpSessionListener
标识了有效的JSESSIONID
为了隔离这个问题,我创建了一个
HttpSessionListener
的实现,然后从HttpLListener
的自定义实现调用它,如下所示:字符串
然后,我在
OncePerRequestFilter
的自定义实现中调用了上述HttpSessionListener
,并将其插入到authserver
应用程序的Spring Security Filter Chain中以提供诊断信息,如下所示:型
隔离问题代码:
下面结合并总结了来自
HttpSessionListener
和web浏览器的开发者工具的诊断数据,这些诊断数据是从用户在提交pin代码视图上单击提交到浏览器从/oauth/token
端点返回拒绝之间的步骤。正如你所看到的,**有两个
JSESSIONID
值在浮动。其中一个值是正确的,而另一个值是错误的。**错误的值被传递到/oauth/token
的请求中,并导致拒绝,即使传递的csrf
是正确的。因此,这个问题的解决方案可能来自改变下面的步骤,以停止将坏JSESSIONID
放置在好JSESSIONID
的位置:型
考虑到你提供的要点,我将尝试花更多的时间来进一步隔离解决方案。但是上面的内容应该会大大缩小问题的范围。
我在它完全完成之前发布这个,因为你的赏金期限即将到期。
vojdkbi02#
你的问题解决了吗?我一直在寻找2FA和spring-security-oauth2的完整示例。很高兴你已经发布了完整的概念和完整的源代码。
我试过你的软件包,你的问题可以简单地通过改变你的AuthserverApplication.java中的一行代码来解决。
字符串
您的原始配置通过了Spring Security的身份验证链,该链返回了一个null身份验证对象。
我还建议您将CustomOAuth 2 RequestFactory的Bean创建更改为以下内容,以覆盖链中的所有OAuth2 RequestFactory
型
对于您添加的用于处理CSRF的代码,您可以简单地删除它们,例如2FA控制器:
型
请让我知道如果你想要一个完整的源代码后清理。