/oauth/token 中的 XSRF 令牌无效 [英] Invalid XSRF token at /oauth/token

查看:27
本文介绍了/oauth/token 中的 XSRF 令牌无效的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

多因素身份验证的 Spring OAuth2 实现的完整代码已上传到 .上面的 **SECOND PASS** 只是第二次运行单因素官方规范中的步骤,但在 **SECOND PASS** 期间具有更大的权限.


**日志内容:**
HTTP 请求和响应标头表明:1.) 使用正确的用户名"和密码"提交到9999/login"的 POST 导致重定向到9999/authorize?client_id=acme&redirect_uri=/login&response_type=code&state=sGXQ4v",然后是GET 9999/"安全/two_factor_authenticated`.一个 XSRF 代币在这些交易所中保持不变.2.) 使用正确的 pin 码发送到 `9999/secure/two_factor_authentication` 的 POST 发送相同的 `XSRF` 令牌,并成功重定向到 `POST 9999/oauth/authorize` 并使其成为 `TwoFactorAuthenticationFilter.doFilterInternal()` 并继续进行 `request 9999/oauth/token`,但是 `9999/oauth/token` 拒绝了该请求,因为相同的旧 XSRF 令牌与新的 `XSRF` 令牌值不匹配,该值显然是在 ** 期间创建的第一次通过**.`1.)`和`2.)`之间的一个明显区别是`2.)`中的第二个`request 9999/oauth/authorize`不包含对`9999/的第一个请求中包含的url参数authorize?client_id=acme&redirect_uri=/login&response_type=code&state=sGXQ4v` in `1.)` 中,也在 官方规范.但目前尚不清楚这是否是导致问题的原因.此外,尚不清楚如何访问参数以从TwoFactorAuthenticationController.POST"发送完整格式的请求.我对`POST 9999/secure/two_factor_authentication`控制器方法的`HttpServletRequest`中的`parameters``Map`进行了SYSO,它包含的只是`pinVal`和`_csrf`变量.您可以在文件共享站点 点击此链接阅读所有 HTTP 标头和 Spring Boot 日志.
**失败的方法:**
我试过 @RobWinch 在 Spring Security 3.2 环境中解决类似问题的方法,但该方法似乎不适用于 Spring OAuth2 的上下文.具体来说,当在下面显示的 `TwoFactorAuthenticationFilter` 代码中取消对以下 `XSRF` 更新代码块的注释时,下游请求标头确实显示不同/新的 `XSRF` 令牌值,但会抛出相同的错误.如果(AuthenticationUtil.hasAuthority(ROLE_TWO_FACTOR_AUTHENTICATED)){CsrfToken token = (CsrfToken) request.getAttribute("_csrf");response.setHeader("XSRF-TOKEN"/*"X-CSRF-TOKEN"*/, token.getToken());}**这表明`XSRF` 配置需要以`/oauth/authorize` 和`/oauth/token` 能够相互通信以及与客户端和资源应用程序通信的方式更新,以成功管理`XSRF` 令牌值.** 也许`CustomOAuth2RequestFactory` 是需要更改以完成此操作的内容.但是如何?
**相关代码:**
`CustomOAuth2RequestFactory` 的代码是:公共类 CustomOAuth2RequestFactory 扩展了 DefaultOAuth2RequestFactory {public static final String SAVED_AUTHORIZATION_REQUEST_SESSION_ATTRIBUTE_NAME = "savedAuthorizationRequest";公共 CustomOAuth2RequestFactory(ClientDetailsS​​ervice clientDetailsS​​ervice) {超级(客户端详细信息服务);}@覆盖公共授权请求 createAuthorizationRequest(地图授权参数){ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();HttpSession session = attr.getRequest().getSession(false);如果(会话!= null){AuthorizationRequest authorizationRequest = (AuthorizationRequest) session.getAttribute(SAVED_AUTHORIZATION_REQUEST_SESSION_ATTRIBUTE_NAME);如果(授权请求!= null){session.removeAttribute(SAVED_AUTHORIZATION_REQUEST_SESSION_ATTRIBUTE_NAME);返回授权请求;}}返回 super.createAuthorizationRequest(authorizationParameters);}}`TwoFactorAuthenticationFilter` 的代码是://这个类是根据:https://stackoverflow.com/questions/30319666/two-factor-authentication-with-spring-security-oauth2添加的/*** 在会话中存储 oauth 授权请求,以便它可以* 稍后由 {@link com.example.CustomOAuth2RequestFactory} 挑选* 继续授权流程.*/公共类TwoFactorAuthenticationFilter 扩展OncePerRequestFilter {private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();私有 OAuth2RequestFactory oAuth2RequestFactory;//下面这两个是作为测试添加的,以避免未定义时发生的编译错误.public static final String ROLE_TWO_FACTOR_AUTHENTICATED = "ROLE_TWO_FACTOR_AUTHENTICATED";public static final String ROLE_TWO_FACTOR_AUTHENTICATION_ENABLED = "ROLE_TWO_FACTOR_AUTHENTICATION_ENABLED";@自动连线public void setClientDetailsS​​ervice(ClientDetailsS​​ervice clientDetailsS​​ervice) {oAuth2RequestFactory = new DefaultOAuth2RequestFactory(clientDetailsS​​ervice);}私有布尔 twoFactorAuthenticationEnabled(收集权限){System.out.println(">>>>>>>>>>> 权限列表包括:");对于(GrantedAuthority 权限:权限){System.out.println("身份验证:"+authority.getAuthority());}返回 authority.stream().anyMatch(权限 -> ROLE_TWO_FACTOR_AUTHENTICATION_ENABLED.equals(authority.getAuthority()));}@覆盖protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 抛出 ServletException, IOException {System.out.println("------------------- INSIDE TwoFactorAuthenticationFilter.doFilterInternal() ------------------------");//检查用户是否没有进行双因素认证.如果 (AuthenticationUtil.isAuthenticated() && !AuthenticationUtil.hasAuthority(ROLE_TWO_FACTOR_AUTHENTICATED)) {System.out.println("++++++++++++++++++++++++++ 认证但不是两个因素+++++++++++++++++++++++++");AuthorizationRequest authorizationRequest = oAuth2RequestFactory.createAuthorizationRequest(paramsFromRequest(request));/* 检查客户端的权限 (authorizationRequest.getAuthorities()) 还是用户的权限需要两个因素验证.*/System.out.println("======================== twoFactorAuthenticationEnabled(authorizationRequest.getAuthorities()) 是:" + twoFactorAuthenticationEnabled(authorizationRequest.getAuthorities()) );System.out.println("======================== twoFactorAuthenticationEnabled(SecurityContextHolder.getContext().getAuthentication().getAuthorities()) 是:"+ twoFactorAuthenticationEnabled(SecurityContextHolder.getContext().getAuthentication().getAuthorities()));if (twoFactorAuthenticationEnabled(authorizationRequest.getAuthorities()) ||twoFactorAuthenticationEnabled(SecurityContextHolder.getContext().getAuthentication().getAuthorities())) {//在会话中保存授权请求.这允许 CustomOAuth2RequestFactory//在用户成功后将此保存的请求返回给 AuthenticationEndpoint//进行了两因素身份验证.request.getSession().setAttribute(CustomOAuth2RequestFactory.SAVED_AUTHORIZATION_REQUEST_SESSION_ATTRIBUTE_NAME, authorizationRequest);//重定向用户需要输入两步验证码的页面redirectStrategy.sendRedirect(请求,响应,ServletUriComponentsBuilder.fromCurrentContextPath().path(TwoFactorAuthenticationController.PATH).toUriString());返回;}}//下一个IF"块在未注释时不解决错误//if(AuthenticationUtil.hasAuthority(ROLE_TWO_FACTOR_AUTHENTICATED)){//CsrfToken token = (CsrfToken) request.getAttribute("_csrf");//这是要作为标头或 HTTP 参数包含的令牌的值//response.setHeader("XSRF-TOKEN", token.getToken());//}filterChain.doFilter(请求,响应);}私人地图 paramsFromRequest(HttpServletRequest 请求){Map params = new HashMap();for (Entry entry : request.getParameterMap().entrySet()) {params.put(entry.getKey(), entry.getValue()[0]);}返回参数;}}
**在您的计算机上重新创建问题:**
按照以下简单步骤,只需几分钟,您就可以在任何计算机上重现该问题:1.) 单击此链接从文件共享站点下载压缩版本的应用程序.2.) 输入以下命令解压缩应用程序:`tar -zxvf oauth2.tar(2).gz`3.) 通过导航到oauth2/authserver"然后输入mvn spring-boot:run"来启动authserver"应用程序.4.) 通过导航到 `oauth2/resource` 然后输入 `mvn spring-boot:run` 来启动 `resource` 应用程序5.) 通过导航到`oauth2/ui`然后输入`mvn spring-boot:run`来启动`ui`应用程序6.) 打开 Web 浏览器并导航到 `http ://localhost : 8080`7.) 单击登录",然后输入Frodo"作为用户和MyRing"作为密码,然后单击提交.8.) 输入5309"作为密码"并点击提交.**这将触发上面显示的错误.**您可以通过以下方式查看完整的源代码:a.) 将 Maven 项目导入您的 IDE,或通过b.) 在解压缩的目录中导航并使用文本编辑器打开.
您可以在文件共享站点 点击此链接阅读所有 HTTP 标头和 Spring Boot 日志.

Complete code for a Spring OAuth2 implementation of Multi-Factor Authentication has been uploaded to a file sharing site at this link. Instructions are given below to recreate the current problem on any computer in only a few minutes.

推荐答案

一个想法突然出现在我的脑海中:

One idea that popped to my head:

如果会话固定被激活,在用户认证成功后会创建一个新的会话(参见 SessionFixationProtectionStrategy).如果您使用默认的 HttpSessionCsrfTokenRepository,这当然也会创建一个新的 csrf 令牌.由于您提到了 XSRF-TOKEN 标头,因此我假设您使用了一些 JavaScript 前端.我可以想象用于登录的原始 csrf 令牌会被存储并在之后重新使用 - 这将不起作用,因为此 csrf 令牌不再有效.

If session fixation is activated, a new session is created after the user authenticated successfully (see SessionFixationProtectionStrategy). This will also of course create a new csrf token if you use the default HttpSessionCsrfTokenRepository. Since you're mentioning the XSRF-TOKEN header I assume you use some JavaScript frontend. I could imagine that the original csrf token that was used for the login is stored and reused afterwards - which would not work because this csrf token is not valid anymore.

您可以尝试禁用会话固定(http.sessionManagement().sessionFixation().none()<session-management session-fixation-protection="none"/>) 或登录后重新获取当前CSRF令牌.

You may try disabling session fixation (http.sessionManagement().sessionFixation().none() or <session-management session-fixation-protection="none"/>) or re-get the current CSRF token after login.

这篇关于/oauth/token 中的 XSRF 令牌无效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆