reactjs - 从 Nginx 提供 Spring REST API,CORS 和 CSRF 的问题
问题描述
我已经为此苦苦挣扎了 8 个小时,我意识到这不是我自己能想到的。
我正在尝试在 DigitalOcean Ubuntu 上使用 Nginx 提供使用 Spring Boot 实现的 REST API。我已经实现了一个简单的 JWT 身份验证以及在每个请求上传递一个 CSRF 令牌。我有一个用 ReactJS 实现的前端。当我将它部署到 Heroku 时,整个系统运行良好,但当时后端也服务于前端,所以 CORS 真的不是什么大问题。我应该为“客户端”进行部署,他的 webhotel 不支持 Spring Boot,因此我需要在不同的位置提供前端和后端服务。
基本上我遇到了多个问题:
- 打开应用程序时,我的前端代码找不到 CSRF 令牌
- 如果我(出于测试目的)甚至不尝试在登录时传递 CSRF 令牌,我会遇到 CORS 错误:“访问 XMLHttpRequest at ' https://ip.of.my.droplet.on.digitalocean/api/ auth/login '来自源' https://url.of.my.frontend.on.heroku.com ' 已被 CORS 策略阻止:对预检请求的响应未通过访问控制检查:不允许重定向预检请求。”
我一直在谷歌搜索并尝试不同类型的配置,以至于我什至不知道我的修复是否真的导致了更多问题,或者只是没有做任何事情。
我的 Spring 安全配置
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.requiresChannel()
.requestMatchers(r -> r.getHeader("X-Forwarded-Proto") != null)
.requiresSecure()
.and()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/api/**").permitAll()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/static/**").permitAll()
.antMatchers("/**").permitAll()
.anyRequest().authenticated();
Spring CORS 配置
registry.addMapping("/**")
.allowedOrigins([URL to the frontend for testing purposes])
.allowedMethods("GET", "POST", "OPTIONS", "DELETE", "PUT", "PATCH")
.allowedHeaders("authorization, content-type, content-length, xsrf-token, credentials")
.allowCredentials(true)
.exposedHeaders("xsrf-token")
.maxAge(3600);
Nginx 服务配置
server {
listen 443 ssl;
listen [::]:443 ssl;
include snippets/self-signed.conf;
include snippets/ssl-params.conf;
server_name [IP of my DigitalOcean droplet here];
location /api {
proxy_pass http://localhost:8080/api;
proxy_set_header Origin [URL to the frontend deployed on Heroku for testing purposes];
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_pass_header X-XSRF-TOKEN;
}
}
server {
listen 80;
listen [::]:80;
server_name [IP of my DigitalOcean droplet here];
return 302 https://$server_name$request_uri;
}
前端的axios配置
const CSRF_TOKEN = document.cookie.match(new RegExp('XSRF-TOKEN=([^;]+)'))[1];
instance = axios.create({
withCredentials: true,
baseURL: '[ip-address of my DigitalOcean droplet]/api',
headers: {
'X-XSRF-TOKEN': CSRF_TOKEN,
'Access-Control-Allow-Methods': 'PATCH, DELETE, POST, GET, OPTIONS, PUT'
}
});
我在这里非常初学者,所以我什至不确定这是否正确。我只知道当它全部部署到 Heroku 时它起作用了,所以至少其中一些应该是正确的。