重命名:CSRF with Angular and Spring ("Request method 'DELETE' not supported")
Renamed: CSRF with Angular and Spring ("Request method 'DELETE' not supported")
我正在尝试调用控制器的删除方法:
Spring:
@RestController
@RequestMapping("/api")
@CrossOrigin
public class Controller
{
@DeleteMapping("thing/item/{name}")
public void deleteThing(@PathVariable String name)
{
System.out.println("CALL"); // Never called!!!
}
}
Angular 7:
deleteTemplate(name: string) {
const url = `host/api/thing/item/${name}`;
return this.http.delete(url);
}
我找到了一些关于包含选项的信息:
httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
deleteTemplate(name: string) {
const url = `${host}/api/thing/item/${name}`; // host is not relevant, same path with GET works.
return this.http.delete(url, this.httpOptions);
}
但我认为甚至不需要它,因为我发送的所有内容都在 link 本身(没有 json)。
每次都是:
WARN 15280 --- [io-8443-exec-10] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'DELETE' not supported]
如何解决这个问题?
发送 DELETE 时出现 405 错误和前面的 OPTIONS(带有 200 代码)。它到达服务器并出现此错误。
编辑
以上是经过简化但仍然无法运行的代码示例。我理解你的意见,但 host 可以是任何东西,在这里无关紧要。它现在是本地主机,如前所述 - 它在到达端点的意义上工作。
为了检查我是否错了,我这样做了:
@Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;
this.requestMappingHandlerMapping.getHandlerMethods()
.forEach((e, k) -> System.out.println(e + " OF " + k));
启动后,它打印了我期望的所有内容,包括:
{GET /api/thing/item/{name}} OF public myPackage.Thing myPackage.Controller.getThing(java.lang.String)
{DELETE /api/thing/item/{name}} OF public void myPackage.Controller.deleteThing(java.lang.String)
有什么想法吗?
在路径的开头添加斜杠 /。
@DeleteMapping("/thing/item/{name}")
该死的。原来问题出在 CSRF 中,主要是在隐藏的 Angular 文档和误导性的 Spring 过滤日志中。
我花了大约 10 小时才让它工作,但仅限于生产环境(其中 Angular 与 host/port 与 Spring 相同)。我会尝试让它在开发中工作,但这需要基本上重写 Angular 提供的绑定到其默认值的整个模块。
现在关于问题:
当我们想使用 CSRF 时,一切都从 Spring 安全开始(我们确实这样做了)。显然,如果 CsrfFilter
不能匹配它期望的 CSRF 令牌并退回到 /error
,它就会爆炸。这一切都在没有任何特定日志消息的情况下发生,但很简单Request method 'DELETE' not supported
,从我的问题我们知道它存在。
这不仅适用于 DELETE
,而且适用于所有操作请求 (non-GET/HEAD
)。
这都指向"How to setup CSRF with Angular?"。那么,我们开始吧:
它默认工作。基于 CSRF 的 Cookie-Header 机制。
等等,还有更多!
Spring 需要为 Cookie-Header 机制配置 bo。很高兴我们得到了:
@Override
protected void configure(HttpSecurity http) throws Exception
{
http.csrf().csrfTokenRepository(this.csrfRepo());
}
private CookieCsrfTokenRepository csrfRepo()
{
CookieCsrfTokenRepository repo = new CookieCsrfTokenRepository();
repo.setCookieHttpOnly(false);
return repo;
}
内置于 spring 中,实际上使用与 Angular 相同的 cookie/header 名称。但是 repo.setCookieHttpOnly(false);
怎么了?好吧......另一个糟糕的记录。你只需要将它设置为 false,否则 Angular 的一方将无法工作。
同样 - 这仅适用于生产版本,因为 Angular 不支持外部链接。更多信息:
所以是的......为了让它在具有 2 个服务器的开发本地主机中工作,我需要第一个重新创建 https://angular.io/api/common/http/HttpClientXsrfModule 机制来拦截非默认请求。
编辑
根据 JB Nizet 的评论,我设计了适用于开发和生产的设置。确实代理很棒。在启用 CSRF 和 SSL 的情况下(在我的例子中),您还需要在 Angular CLI 上启用 SSL,否则 Angular 将无法使用启用 SSL 的代理后端的 CSRF,因此无法工作。
"serve": {
"options": {
"proxyConfig": "proxy.conf.json",
"sslKey": "localhost.key",
"sslCert": "localhost.crt",
"ssl": true
}
}
请注意,如果找不到,证书和密钥将自动生成:)
对于 Spring 后端,您不需要相同的证书。我在那里使用单独的 JKS。
干杯!
我正在尝试调用控制器的删除方法:
Spring:
@RestController
@RequestMapping("/api")
@CrossOrigin
public class Controller
{
@DeleteMapping("thing/item/{name}")
public void deleteThing(@PathVariable String name)
{
System.out.println("CALL"); // Never called!!!
}
}
Angular 7:
deleteTemplate(name: string) {
const url = `host/api/thing/item/${name}`;
return this.http.delete(url);
}
我找到了一些关于包含选项的信息:
httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
deleteTemplate(name: string) {
const url = `${host}/api/thing/item/${name}`; // host is not relevant, same path with GET works.
return this.http.delete(url, this.httpOptions);
}
但我认为甚至不需要它,因为我发送的所有内容都在 link 本身(没有 json)。
每次都是:
WARN 15280 --- [io-8443-exec-10] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'DELETE' not supported]
如何解决这个问题?
发送 DELETE 时出现 405 错误和前面的 OPTIONS(带有 200 代码)。它到达服务器并出现此错误。
编辑
以上是经过简化但仍然无法运行的代码示例。我理解你的意见,但 host 可以是任何东西,在这里无关紧要。它现在是本地主机,如前所述 - 它在到达端点的意义上工作。
为了检查我是否错了,我这样做了:
@Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;
this.requestMappingHandlerMapping.getHandlerMethods()
.forEach((e, k) -> System.out.println(e + " OF " + k));
启动后,它打印了我期望的所有内容,包括:
{GET /api/thing/item/{name}} OF public myPackage.Thing myPackage.Controller.getThing(java.lang.String)
{DELETE /api/thing/item/{name}} OF public void myPackage.Controller.deleteThing(java.lang.String)
有什么想法吗?
在路径的开头添加斜杠 /。
@DeleteMapping("/thing/item/{name}")
该死的。原来问题出在 CSRF 中,主要是在隐藏的 Angular 文档和误导性的 Spring 过滤日志中。
我花了大约 10 小时才让它工作,但仅限于生产环境(其中 Angular 与 host/port 与 Spring 相同)。我会尝试让它在开发中工作,但这需要基本上重写 Angular 提供的绑定到其默认值的整个模块。
现在关于问题:
当我们想使用 CSRF 时,一切都从 Spring 安全开始(我们确实这样做了)。显然,如果 CsrfFilter
不能匹配它期望的 CSRF 令牌并退回到 /error
,它就会爆炸。这一切都在没有任何特定日志消息的情况下发生,但很简单Request method 'DELETE' not supported
,从我的问题我们知道它存在。
这不仅适用于 DELETE
,而且适用于所有操作请求 (non-GET/HEAD
)。
这都指向"How to setup CSRF with Angular?"。那么,我们开始吧:
它默认工作。基于 CSRF 的 Cookie-Header 机制。
等等,还有更多!
Spring 需要为 Cookie-Header 机制配置 bo。很高兴我们得到了:
@Override
protected void configure(HttpSecurity http) throws Exception
{
http.csrf().csrfTokenRepository(this.csrfRepo());
}
private CookieCsrfTokenRepository csrfRepo()
{
CookieCsrfTokenRepository repo = new CookieCsrfTokenRepository();
repo.setCookieHttpOnly(false);
return repo;
}
内置于 spring 中,实际上使用与 Angular 相同的 cookie/header 名称。但是 repo.setCookieHttpOnly(false);
怎么了?好吧......另一个糟糕的记录。你只需要将它设置为 false,否则 Angular 的一方将无法工作。
同样 - 这仅适用于生产版本,因为 Angular 不支持外部链接。更多信息:
所以是的......为了让它在具有 2 个服务器的开发本地主机中工作,我需要第一个重新创建 https://angular.io/api/common/http/HttpClientXsrfModule 机制来拦截非默认请求。
编辑
根据 JB Nizet 的评论,我设计了适用于开发和生产的设置。确实代理很棒。在启用 CSRF 和 SSL 的情况下(在我的例子中),您还需要在 Angular CLI 上启用 SSL,否则 Angular 将无法使用启用 SSL 的代理后端的 CSRF,因此无法工作。
"serve": {
"options": {
"proxyConfig": "proxy.conf.json",
"sslKey": "localhost.key",
"sslCert": "localhost.crt",
"ssl": true
}
}
请注意,如果找不到,证书和密钥将自动生成:) 对于 Spring 后端,您不需要相同的证书。我在那里使用单独的 JKS。
干杯!