【问题标题】:How to authenticate properly in post request with fetch api?如何使用 fetch api 在发布请求中正确验证?
【发布时间】:2021-12-03 03:30:27
【问题描述】:
  • 我有一个在默认配置中启用了 Spring Security 的 SpringBoot 后端(那里没有改变)。

  • 我有以下 Rest Controller 和 Post Mapping:


@RestController
public class MyController {

    @PostMapping(value = "/sm/resrc/pth")
    public Integer postSomething(@RequestParam String someValue,@RequestParam String userId){
        System.out.println(String.format("SomeValue: %s from userid %s",someValue,userId));
        return 0;
    }

}
  • 从以下表单创建 Post 请求可以正常工作:
<form method="post" th:action="@{/sm/resrc/pth}">
     <input type="text" name="someValue">
     <input type="text" name="userId">
     <input type="hidden" name="_csrf" value="${_csrf.token}" />
     <div><input type="submit" value="Send" /></div>
</form>

它甚至可以在没有隐藏 cors 值的情况下工作。

  • 但是我需要从 JavaScript 创建一个 post 请求,这不起作用。 SpringBoot 应用程序在 localhost:8080 运行。我想,credentials 参数 'include' 用于包含所需的身份验证标头,用户已经成功输入这些标头以打开给定页面。它是否正确?我还更改了 'mode' 的值。我尝试了 'cors''same-origin''no-cors'。它只是行不通。我事件不明白为什么 cors 是一个问题,因为我正在请求来自同一来源的资源。在不使用凭证参数的情况下手动将授权标头添加到请求后,它甚至都不起作用。如图所示,我总是得到 403 状态。我的请求有什么问题?我错过了什么?
let data = {
            "some":"abc",
            "values":this.localDescription,
            "userId":userId
        };

fetch("sm/resrc/pth",
            {
                method: 'POST',
                credentials: 'include',
                mode: 'cors',
                body: data
            }
        ).then(response => console.log(response));

【问题讨论】:

    标签: javascript spring authentication fetch-api


    【解决方案1】:

    我在使用 jax-rs 进行登录示例时发现了同样的问题。在我的情况下,问题是当添加 'credentials': 'include' 到请求时,在 Rest Service API 一侧,需要在 CORS 中配置过滤标头来自:

    response.addHeader("Access-Control-Allow-Origin", "*");
    

    到:

    res.addHeader("Access-Control-Allow-Origin", "http://localhost");
    

    “http://localhost 是我的请求的来源。

    我在这篇文章中找到了有关它的信息:No 'Access-Control-Allow-Origin' header is present on the requested resource—when trying to get data from a REST API

    【讨论】:

    • 但是请求是从同一个来源发送的。所以实际上我认为 cors 应该不是问题。
    【解决方案2】:

    CORS 是一项安全功能;所以你可能不想禁用或绕过它。我费了一番功夫才知道如何正确包含标题,但最终找到了解决方案。我就是这样解决的:

    在第一步中,我将预期的 CSRF-Header-name 和令牌本身添加到生成的 html 文件的元数据中。在 spring 中使用模板框架 thyemleaf 如下所示:

    <html lang="en">
        <head>
            <meta name="_csrf_header_name" th:content="${_csrf.headerName}"/> 
            <meta name="_csrf_token" th:content="${_csrf.token}"/>
                   
        </head>
        .
        .
        .
    </html>
    
    

    结果是这样的:

    
    <meta name="_csrf_header_name" content="X-CSRF-TOKEN">
    <meta name="_csrf_token" content="14d98c88-8643-1234-5678-39473aa7890e">
    
    

    当获取请求创建时,从元标记中读取标头名称和令牌本身的值。这样,csrf 标头看起来与服务器期望的完全一样。这就是为什么我在请求中挣扎的主要原因。我不知道,我将令牌的标头名称命名错误。预期的标头/令牌名称可能因服务器而异 - 我猜;因此可能会因您的后端而有所不同。

    getCsrfToken(){
       return document.querySelector('meta[name=_csrf_token]').content;
    }
    
    getCsrfTokenName(){
       return document.querySelector('meta[name=_csrf_header_name]').content;
    }
     
    let csrfToken = this.getCsrfToken();
    let csrfTokenName = this.getCsrfTokenName();
            
    fetch('sm/resrc/pth', {
              method:'post',
              headers: new Headers([[csrfTokenName, csrfToken]])
            }).then(res =>{console.log(res);});}
    

    【讨论】:

      猜你喜欢
      • 2017-01-26
      • 1970-01-01
      • 2020-11-24
      • 1970-01-01
      • 2020-11-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多