我想用第二个前端应用程序扩展我的jHipster单片设置,该应用程序从不同的URL访问相同的API。作为第一步,我在application.yml
中启用了CORS,并且我使用withCredentials
标志从前端发送请求。我使用会话而没有JWT身份验证。
许多方法现在按预期工作,但不是全部。飞行前(OPTIONS
请求)始终通过并按预期工作。此调用的响应包含正确的CORS头。
但是,实际请求(例如,登录的POST请求)还需要响应中的标头(Access-Control-Allow-Origin
)。此标头在我的自定义REST接口上自动设置,但未在jHipster生成的方法(如/api/authentication
或/api/logout
)上设置。它也不适用于像/api/account
这样的受Spring受安全保护的资源(仅当未登录时才401
,之后它会按正常标头正常工作)
对于退出,例如,Google Chrome会在控制台中响应以下消息,即使呼叫在“网络”标签中进行(POST
响应状态200):
XMLHttpRequest无法加载http://localhost:8080/api/logout。 No' Access-Control-Allow-Origin'标头出现在请求的资源上。起源' http://localhost:9000'因此不允许访问。
我在想,我在这里做错了什么。我想标题没有正确设置。我现在可以手动添加标题(例如在AjaxAuthenticationSuccessHandler
中),但这似乎不正确。
我正在使用相当过时的jHipster 3.7.0
版本。但是,我宁愿不更新核心项目。
您有什么想法,可能导致此问题的原因吗?
以下是对/api/logout
的POST调用的完整标题。 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

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

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***]

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

您可以使用this demo project of jHipster in version 3.7.0
重现此行为。然后,在src/main/resources/application.yml
中启用CORS设置(所有这些设置)。之后,在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>
&#13;
答案 0 :(得分:5)
您似乎遇到this CORS issue,其中Spring Security的过滤器链中的CorsFilter
之前的某个过滤器会引发错误。请求永远不会到达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)
同样在SecurityConfiguration.java
中,您可以设置@EnableWebSecurity(debug = true)
以查看每个请求的过滤器链。您可以确保CorsFilter
位于链中的CsrfFilter
和UsernamePasswordAuthenticationFilter
之前,验证所有内容是否正确:
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
CorsFilter <--------- Before CsrfFilter and UsernamePasswordAuthenticationFilter
CsrfFilter
CsrfCookieGeneratorFilter
LogoutFilter
UsernamePasswordAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
RememberMeAuthenticationFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
如果您在新客户端遇到CSRF问题,则可能需要在POST后发出GET请求以刷新CSRF令牌。可以看到JHipster如何处理这个问题的一个例子when logging out。
答案 1 :(得分:3)
看一下org.springframework.web.cors.CorsConfiguration
如何在JHipster common application properties中配置exposed-headers: "Authorization"
Spring bean。我认为您缺少以下配置行:
function OnSuccess(response) {
var data = {};
var value = [];
JSON.parse(response).forEach(function(d) {
data[d.item1] = d.y;
value.push(d.item1);
});
c3.generate({
data: {
json: [data],
keys: {
value : value
},
type: 'donut'
},
donut: {
title: "Approval",
width: 40,
label: {
show: false
}
},
color: {
pattern: ["#ff9800", "#78c350", "#f7531f"]
}
})
}
我刚刚在几小时前记录了这一点,also added this configuration by default a few hours ago。
在明天应该出的下一个版本中,您应该有一个名为Separating the front-end and the API server的新页面,该页面应该更好地解释 - 如果您找到了更好的解决方案,请不要犹豫,改进页面,它是第一个版本。