如何在使用 fetch api 的 post 请求中正确验证?

How to authenticate properly in post request with fetch api?


@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;
    }

}
<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 值,它也能正常工作。

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));

我在使用 jax-rs 做登录示例时发现了同样的问题。在我的例子中,问题是当添加 'credentials': 'include' 到请求时,在 Rest 服务 API 的一侧,需要在 CORS 中配置 Filter the header from:

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

至:

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

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

我在这个 post 中找到了有关它的信息:

CORS 是一项安全功能;所以你可能不想禁用或绕过它。我努力寻找如何正确包含 header 的方法,但最终找到了解决方案。我就是这样解决的:

在第一步中,我将预期的 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">

创建提取请求时,header 名称的值和令牌本身会从元标记中读取。这样 csrf header 看起来就像服务器期望的那样。这是主要原因,也是我为这个请求苦苦挣扎的原因。我不知道,我将令牌的 Header 名称命名为错误。预期的 Header/Token 名称可能因服务器而异 - 我猜;因此可能会有所不同,具体取决于您的后端。

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);});}