JHipster:CORS在表单登录时失败(实际请求,不是在飞行前)

ssc*_*ep3 16 spring spring-security cors jhipster

我想用第二个前端应用程序扩展我的jHipster单片设置,该应用程序从不同的URL访问相同的API.作为第一步,我已经启用了CORS application.yml并且我从前端发送带有withCredentials标志的请求.我正在使用会话而没有JWT身份验证.

许多方法现在按预期工作,但不是全部.飞行前(OPTIONS请求)始终通过并按预期工作.此调用的响应包含正确的CORS头.

但是,实际请求(例如,POST登录请求)还需要Access-Control-Allow-Origin响应中的header().此标头在我的自定义REST接口上自动设置,但它没有在jHipster生成的方法上设置,如/api/authentication/api/logout.它也不适用于受Spring受安全保护的资源/api/account(仅在未登录的情况下401,之后按预期使用正确的标头)

例如,对于注销,Google Chrome会在控制台中对以下消息作出反应,即使呼叫在"网络"选项卡中POST响应(响应状态200):

XMLHttpRequest无法加载http:// localhost:8080/api/logout.请求的资源上不存在"Access-Control-Allow-Origin"标头.原产地" 的http://本地主机:9000 "因此不允许访问.

我想知道,我在这里做错了什么.我想标题没有正确设置.我现在可以手动添加标题(例如在...中AjaxAuthenticationSuccessHandler),但这似乎不正确.

我正在使用相当过时的jHipster版本3.7.0.但是,我宁愿不更新核心项目.

你有什么想法,可能导致这个问题的原因是什么?



以下是POST调用的完整标题/api/logout.该OPTIONS调用按预期工作,但在POST响应Access-Control-Allow-Origin中缺少标头:

OPTIONS 请求

OPTIONS /api/logout HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Access-Control-Request-Method: POST
Origin: http://localhost:9000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36
Access-Control-Request-Headers: x-csrf-token
Accept: */*
DNT: 1
Referer: http://localhost:9000/
Accept-Encoding: gzip, deflate, br
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4,de-CH;q=0.2,it;q=0.2
Run Code Online (Sandbox Code Playgroud)

OPTIONS 响应

HTTP/1.1 200 OK
Access-Control-Allow-Headers: x-csrf-token
Date: Mon, 11 Sep 2017 13:54:57 GMT
Connection: keep-alive
Access-Control-Allow-Origin: http://localhost:9000
Vary: Origin
Access-Control-Allow-Credentials: true
Content-Length: 0
Access-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS
Access-Control-Max-Age: 1800
Run Code Online (Sandbox Code Playgroud)

POST 请求

POST /api/logout HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 0
Pragma: no-cache
Cache-Control: no-cache
Accept: application/json, text/plain, */*
Origin: http://localhost:9000
X-CSRF-TOKEN: [***token removed in this snippet***]
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36
DNT: 1
Referer: http://localhost:9000/
Accept-Encoding: gzip, deflate, br
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4,de-CH;q=0.2,it;q=0.2
Cookie: [***removed cookies in this snippet***]
Run Code Online (Sandbox Code Playgroud)

POST 响应

HTTP/1.1 200 OK
Expires: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Set-Cookie: CSRF-TOKEN=null; path=/; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:00 GMT
Set-Cookie: JSESSIONID=[***removed jsessionID in this snippet***]; path=/; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:00 GMT
Set-Cookie: remember-me=null; path=/; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:00 GMT
X-XSS-Protection: 1; mode=block
Pragma: no-cache
Date: Mon, 11 Sep 2017 13:54:57 GMT
Connection: keep-alive
X-Content-Type-Options: nosniff
Content-Length: 0
Run Code Online (Sandbox Code Playgroud)


再生产

您可以通过在版本中3.7.0使用jHipster的这个演示项目来重现此行为.然后,启用CORS设置(所有这些)src/main/resources/application.yml.之后,创建一个新用户localhost:8080并激活它.最后,尝试使用来自另一个端口(例如,简单节点服务器或xampp)的以下JS代码段进行身份验证.您也可以尝试进行简单的POST调用/api/account,这将导致401错误.请参阅Google Chrome控制台以获取错误消息.

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.16.2/axios.js"></script>
<script type="text/javascript">
var Http = axios.create({
  baseURL: 'http://localhost:8080/api',
});

Http.interceptors.request.use(function (config) {
  config.xsrfCookieName = 'CSRF-TOKEN';
  config.xsrfHeaderName = 'X-CSRF-TOKEN';
  config.withCredentials = true;
  return config;
});

var credentials = {
  username: 'test-user',
  password: 'test123',
  rememberMe: true
};

Http.post('authentication', 'j_username=' + credentials.username +
'&j_password=' + credentials.password +
'&remember-me=' + credentials.rememberMe +
'&submit=Login');
</script>
Run Code Online (Sandbox Code Playgroud)

Jon*_*ell 5

看起来你遇到了这个CORS问题,其中CorsFilterSpring Security的过滤器链之前的一个过滤器引发了错误.请求永远不会到达CorsFilter,导致响应中缺少CORS头.这就是为什么Chrome会抱怨控制台中缺少的标题,即使它是一个不同的错误.

您需要将CorsFilter之前的内容放在一起CsrfFilter,UsernamePasswordAuthenticationFilter以便在这些过滤器出现问题时,响应仍然会获得CORS标头.要完成此操作,请将以下代码添加到SecurityConfiguration.java:

// import CorsFilter
import org.springframework.web.filter.CorsFilter;
...
...
// inject for JHipster v3 apps, add to constructor for JHipster v4 apps
@Inject
private CorsFilter corsFilter;
...
...
// add this line in the configure method before ".exceptionHandling()"
.addFilterBefore(corsFilter, CsrfFilter.class)
Run Code Online (Sandbox Code Playgroud)

同样在SecurityConfiguration.java,您可以设置@EnableWebSecurity(debug = true)为每个请求查看过滤器链.您可以通过确保CorsFilter它在链中CsrfFilterUsernamePasswordAuthenticationFilter链中之前验证一切是否正确:

Security filter chain: [
    WebAsyncManagerIntegrationFilter
    SecurityContextPersistenceFilter
    HeaderWriterFilter
    CorsFilter     <--------- Before CsrfFilter and UsernamePasswordAuthenticationFilter
    CsrfFilter
    CsrfCookieGeneratorFilter
    LogoutFilter
    UsernamePasswordAuthenticationFilter
    RequestCacheAwareFilter
    SecurityContextHolderAwareRequestFilter
    RememberMeAuthenticationFilter
    AnonymousAuthenticationFilter
    SessionManagementFilter
    ExceptionTranslationFilter
    FilterSecurityInterceptor
]
Run Code Online (Sandbox Code Playgroud)

如果您在新客户端遇到CSRF问题,则可能需要在POST后发出GET请求以刷新CSRF令牌.在退出时可以看到JHipster如何处理这个问题的一个例子.