Angular 禁止通过 SSL 发送 PUT 请求
Angular PUT request is Forbidden over SSL
我正在使用 Angular 5 作为带有 Spring Boot REST 服务器的前端。如果不使用 SSL,一切都可以正常工作。当我切换到 SSL 时,最终一切正常。它适用于 GET 请求,但到目前为止我无法通过 PUT 请求。
我猜这是某种 CORS 问题,因为 GET 是一个简单的请求而 PUT 显然不是 (CORS reference),但我不知道如何解决这个问题。
在我的 Spring 启动休息控制器上,我有注释 @CrossOrigin("*")
,所以我认为这不是问题所在,但我不确定。
另一个难题是身份验证是通过 CAS 服务器处理的。我在 CAS 属性中添加了以下配置。这些是允许 GET 请求工作的最后一部分,但我不确定要对它们进行哪些更改(如果有的话)以处理 PUT 请求:
cas.httpWebRequest.cors.enabled=true
cas.httpWebRequest.cors.allowOrigins[0]=*
cas.httpWebRequest.cors.allowMethods[0]=*
cas.httpWebRequest.cors.allowHeaders[0]=*
这是我的请求 headers 和响应:
要求:
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.5
Connection: keep-alive
Content-Length: 1975
Content-Type: application/json
Cookie: JSESSIONID=117D9345E985D824E46…BF32; io=gLhCcBoZrfNcppioAAAB
Host: localhost:4200
Referer: https://localhost:4200/sales/proposals/dashboard
User-Agent: Mozilla/5.0 (Windows NT 10.0; …) Gecko/20100101 Firefox/57.0
响应(状态码 - 403 禁止访问):
access-control-allow-origin: *
cache-control: no-cache, no-store, max-age=0, must-revalidate
content-length: 56
content-type: application/json;charset=UTF-8
date: Wed, 03 Jan 2018 16:39:14 GMT
expires: 0
pragma: no-cache
strict-transport-security: max-age=31536000 ; includeSubDomains
x-content-type-options: nosniff
X-Firefox-Spdy: h2
x-frame-options: DENY
x-powered-by: Express
x-xss-protection: 1; mode=block
angular 服务在 https://localhost:4200
上 运行。
spring 引导服务在 https://localhost:8493
上 运行。
CAS 服务 运行 https://localhost:8443
。
我在任何日志中都没有看到任何错误消息。我希望能够理解为什么 PUT 请求被禁止,然后如何修复它以便 PUT 请求也可以工作。谢谢!
编辑:添加 Spring 引导安全配置
<http pattern="/**" entry-point-ref="casEntryPoint">
<intercept-url pattern="/api/holidays" access="permitAll"/>
<intercept-url pattern="/api/unit**" access="permitAll"/>
<intercept-url pattern="/**" access="isAuthenticated()" />
<custom-filter ref="casAuthenticationFilter" before="CAS_FILTER"/>
<csrf/>
</http>
<global-method-security pre-post-annotations="enabled"/>
<!-- CAS Config -->
<beans:bean id="casEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<beans:property name="loginUrl" value="${cas.server.host.login_url}"/>
<beans:property name="serviceProperties" ref="serviceProperties"/>
</beans:bean>
<beans:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
<beans:property name="service" value="${app.server.host.url}login/cas"></beans:property>
</beans:bean>
<beans:bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager"/>
</beans:bean>
<beans:bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<beans:property name="ticketValidator" ref="ticketValidator"></beans:property>
<beans:property name="serviceProperties" ref="serviceProperties"></beans:property>
<beans:property name="key" value="Key"></beans:property>
<beans:property name="authenticationUserDetailsService" ref="userDetailsWrapper"/>
</beans:bean>
<beans:bean id="userDetailsWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<beans:property name="userDetailsService" ref="userDetails"></beans:property>
</beans:bean>
<ldap-user-service id="userDetails"
server-ref="ldapServer"
group-search-base="ou=ERPGroups,OU=MyBusiness"
group-search-filter="(member={0})"
user-search-base="ou=SBSUsers,OU=Users,OU=MyBusiness"
user-search-filter="(sAMAccountName={0})" />
<ldap-server id="ldapServer" url="${ldap.urls}/${ldap.base}" manager-dn="${ldap.username}" manager-password="${ldap.password}" />
<beans:bean id="ticketValidator" class="org.jasig.cas.client.validation.Cas30ServiceTicketValidator">
<beans:constructor-arg value="${cas.server.host.url}"></beans:constructor-arg>
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="casAuthenticationProvider" />
</authentication-manager>
编辑:添加 Angular 代理配置
{
"/api": {
"target" : "https://localhost:8493",
"changeOrigin": true,
"secure" : false
}
}
尝试在服务器端添加此 header Access-Control-Allow-Credentials: true
如果这对您没有帮助并且其他 post 请求按预期工作,恐怕您的部分代码有误。
HTTP 请求可能会修改服务器上的某些内容,如 PUT、POST、DELETE 和 PATCH,如果事先未告知,则不知道是否允许执行请求。
他们可以通过阅读允许他们修改所请求资源的早期请求的 headers 知道,或者他们将在之前触发 OPTIONS 请求。这称为 'preflight' 请求。
如果您还没有实现 OPTIONS 方法来响应 CORS headers,这就是您需要做的。
查看here了解更多详情。
你为什么不在你的 Spring 启动应用程序上试试这个?
HttpHeaders headers = new HttpHeaders();
headers.add("Access-Control-Allow-Origin", "*");
headers.add("Access-Control-Allow-Credentials", "true");
headers.add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS, HEAD");
headers.add("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
return new ResponseEntity<>(service.update(input), headers, HttpStatus.OK);
可以在这里找到更多阅读材料https://spring.io/blog/2015/06/08/cors-support-in-spring-framework
我遇到了类似的问题,我的前端在 Angular4 中,Back-end 在 Spring 中,我使用的身份验证是基于 JWT authentication.I 我的 POST 请求,但我想它也适用于 PUT,因为两者都属于 "not- so-simple request"(pre-flight 请求的别名)。
我的 UI 工作了一段时间,然后出现以下错误:
无法加载 https://localhost:8080/myApp/services/stationCollection/viewCollected。请求的资源上不存在 'Access-Control-Allow-Origin' header。
分析后,我了解到 pre-flight 请求已通过,完全成功,这是被拒绝访问服务器的主要请求(您可以在网络选项卡中查看chrome 开发工具并获取详细信息)。正在发送的 CORS 请求类型是“not-so-simple 请求”。事实上,如果您使用 Rest Client/curl 向同一台服务器发送相同的请求,一切正常。
当你在网上浏览这个错误时,你发现解决方案丢失了,所有导致在 server-side 配置 CORS,但事实是我已经实现了 CORS。
可能的解决方案:
所有这些促使我找到了同样的解决方案,经过大量搜索,我发现这个问题可能有破解/替代方法,如以下博客中的最佳定义:
https://laracasts.com/discuss/channels/laravel/cros-access-control-allow-origin-not-in-the-headers
博客基本上要求您在从 UI 传递到 Front-end 的所有请求之间创建一个中间件,如果您知道您的应用程序是真实的,请添加
myOptions.headers.append('Access-Control-Allow-Origin', '*');
在中间件。希望对你有帮助。
更改 Spring 配置以允许 CORS 可能不是一个好主意。我所做的是安装一个 nginx 服务器并将其用作反向代理。下面是我的示例 nginx.conf:
# Spring backend
location /api
{
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://localhost:8493;
}
# Angular front end
location /
{
root www/apps;
try_files $uri $uri/ /index.html;
}
在您的 spring 引导安全配置中,我看到 csrf 已启用。但是在您的请求中,我没有看到任何 cookie XSRF-TOKEN
和 header X-XSRF-TOKEN
。这就是为什么服务器不接受请求并以 403 响应进行响应的原因。
您的浏览器可能没有创建 SSL 连接。当浏览器发现不正确的证书或自签名证书或由不受信任的证书颁发机构签名的证书时,通常会发生这种情况。
如果您使用 chrome 浏览器,您可以通过启用 allow-insecure-localhost
属性 为本地主机启用此功能以进行测试。在地址栏中输入这个URL,点击启用并重启Chrome。
chrome://flags/#allow-insecure-localhost
请参阅此 link 了解有关在 chrome 和 firefox 中启用它的更多详细信息。
https://improveandrepeat.com/2016/09/allowing-self-signed-certificates-on-localhost-with-chrome-and-firefox/
我认为您的请求中应该有一个 X-XSRF-TOKEN 令牌 header 因为在您的安全配置中启用了 csrf。
对我来说这很有效:
constructor(private http: HttpClient, private tokenExtractor: HttpXsrfTokenExtractor) {
}
然后提取一个令牌
const token = this.tokenExtractor.getToken() as string;
并将其添加到 header
this.http.post<any>(url, body, {headers: new HttpHeaders().set('X-XSRF-TOKEN', token)})
希望这对您有所帮助。
CORS 由浏览器强制执行,但在这种情况下,您将从服务器获得 403。正如其他人所提到的,您的请求似乎缺少 CSRF header,这可能就是服务器拒绝您的请求的原因。我不知道为什么当你切换到 HTTPS 时开始发生这种情况,但我可以告诉你你的配置中有 <csrf/>
但没有匹配的 header。
为了验证这个理论,您可以通过更改来禁用 CSRF 保护:
<csrf/>
至:
<csrf disabled="true"/>
从 Spring 开始 4 默认启用 CSRF 保护,因此仅删除 <csrf/>
是不够的。
一旦您确认 CSRF 是问题并且您不再收到 403,您应该以 Angular supports. It's an important protection that will protect your users. Angular expects a cookie named XSRF-TOKEN
which it will send back with X-XSRF-TOKEN
header. Those are configurable, but should match Spring's default if you're using latest Spring and latest Angular. Follow Spring documentation 添加的方式重新打开它:
<http>
<!-- ... -->
<csrf token-repository-ref="tokenRepository"/>
</http>
<b:bean id="tokenRepository"
class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
p:cookieHttpOnly="false"/>
我正在使用 Angular 5 作为带有 Spring Boot REST 服务器的前端。如果不使用 SSL,一切都可以正常工作。当我切换到 SSL 时,最终一切正常。它适用于 GET 请求,但到目前为止我无法通过 PUT 请求。
我猜这是某种 CORS 问题,因为 GET 是一个简单的请求而 PUT 显然不是 (CORS reference),但我不知道如何解决这个问题。
在我的 Spring 启动休息控制器上,我有注释 @CrossOrigin("*")
,所以我认为这不是问题所在,但我不确定。
另一个难题是身份验证是通过 CAS 服务器处理的。我在 CAS 属性中添加了以下配置。这些是允许 GET 请求工作的最后一部分,但我不确定要对它们进行哪些更改(如果有的话)以处理 PUT 请求:
cas.httpWebRequest.cors.enabled=true
cas.httpWebRequest.cors.allowOrigins[0]=*
cas.httpWebRequest.cors.allowMethods[0]=*
cas.httpWebRequest.cors.allowHeaders[0]=*
这是我的请求 headers 和响应:
要求:
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.5
Connection: keep-alive
Content-Length: 1975
Content-Type: application/json
Cookie: JSESSIONID=117D9345E985D824E46…BF32; io=gLhCcBoZrfNcppioAAAB
Host: localhost:4200
Referer: https://localhost:4200/sales/proposals/dashboard
User-Agent: Mozilla/5.0 (Windows NT 10.0; …) Gecko/20100101 Firefox/57.0
响应(状态码 - 403 禁止访问):
access-control-allow-origin: *
cache-control: no-cache, no-store, max-age=0, must-revalidate
content-length: 56
content-type: application/json;charset=UTF-8
date: Wed, 03 Jan 2018 16:39:14 GMT
expires: 0
pragma: no-cache
strict-transport-security: max-age=31536000 ; includeSubDomains
x-content-type-options: nosniff
X-Firefox-Spdy: h2
x-frame-options: DENY
x-powered-by: Express
x-xss-protection: 1; mode=block
angular 服务在 https://localhost:4200
上 运行。
spring 引导服务在 https://localhost:8493
上 运行。
CAS 服务 运行 https://localhost:8443
。
我在任何日志中都没有看到任何错误消息。我希望能够理解为什么 PUT 请求被禁止,然后如何修复它以便 PUT 请求也可以工作。谢谢!
编辑:添加 Spring 引导安全配置
<http pattern="/**" entry-point-ref="casEntryPoint">
<intercept-url pattern="/api/holidays" access="permitAll"/>
<intercept-url pattern="/api/unit**" access="permitAll"/>
<intercept-url pattern="/**" access="isAuthenticated()" />
<custom-filter ref="casAuthenticationFilter" before="CAS_FILTER"/>
<csrf/>
</http>
<global-method-security pre-post-annotations="enabled"/>
<!-- CAS Config -->
<beans:bean id="casEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<beans:property name="loginUrl" value="${cas.server.host.login_url}"/>
<beans:property name="serviceProperties" ref="serviceProperties"/>
</beans:bean>
<beans:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
<beans:property name="service" value="${app.server.host.url}login/cas"></beans:property>
</beans:bean>
<beans:bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager"/>
</beans:bean>
<beans:bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<beans:property name="ticketValidator" ref="ticketValidator"></beans:property>
<beans:property name="serviceProperties" ref="serviceProperties"></beans:property>
<beans:property name="key" value="Key"></beans:property>
<beans:property name="authenticationUserDetailsService" ref="userDetailsWrapper"/>
</beans:bean>
<beans:bean id="userDetailsWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<beans:property name="userDetailsService" ref="userDetails"></beans:property>
</beans:bean>
<ldap-user-service id="userDetails"
server-ref="ldapServer"
group-search-base="ou=ERPGroups,OU=MyBusiness"
group-search-filter="(member={0})"
user-search-base="ou=SBSUsers,OU=Users,OU=MyBusiness"
user-search-filter="(sAMAccountName={0})" />
<ldap-server id="ldapServer" url="${ldap.urls}/${ldap.base}" manager-dn="${ldap.username}" manager-password="${ldap.password}" />
<beans:bean id="ticketValidator" class="org.jasig.cas.client.validation.Cas30ServiceTicketValidator">
<beans:constructor-arg value="${cas.server.host.url}"></beans:constructor-arg>
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="casAuthenticationProvider" />
</authentication-manager>
编辑:添加 Angular 代理配置
{
"/api": {
"target" : "https://localhost:8493",
"changeOrigin": true,
"secure" : false
}
}
尝试在服务器端添加此 header Access-Control-Allow-Credentials: true
如果这对您没有帮助并且其他 post 请求按预期工作,恐怕您的部分代码有误。
HTTP 请求可能会修改服务器上的某些内容,如 PUT、POST、DELETE 和 PATCH,如果事先未告知,则不知道是否允许执行请求。
他们可以通过阅读允许他们修改所请求资源的早期请求的 headers 知道,或者他们将在之前触发 OPTIONS 请求。这称为 'preflight' 请求。
如果您还没有实现 OPTIONS 方法来响应 CORS headers,这就是您需要做的。
查看here了解更多详情。
你为什么不在你的 Spring 启动应用程序上试试这个?
HttpHeaders headers = new HttpHeaders();
headers.add("Access-Control-Allow-Origin", "*");
headers.add("Access-Control-Allow-Credentials", "true");
headers.add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS, HEAD");
headers.add("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
return new ResponseEntity<>(service.update(input), headers, HttpStatus.OK);
可以在这里找到更多阅读材料https://spring.io/blog/2015/06/08/cors-support-in-spring-framework
我遇到了类似的问题,我的前端在 Angular4 中,Back-end 在 Spring 中,我使用的身份验证是基于 JWT authentication.I 我的 POST 请求,但我想它也适用于 PUT,因为两者都属于 "not- so-simple request"(pre-flight 请求的别名)。
我的 UI 工作了一段时间,然后出现以下错误:
无法加载 https://localhost:8080/myApp/services/stationCollection/viewCollected。请求的资源上不存在 'Access-Control-Allow-Origin' header。
分析后,我了解到 pre-flight 请求已通过,完全成功,这是被拒绝访问服务器的主要请求(您可以在网络选项卡中查看chrome 开发工具并获取详细信息)。正在发送的 CORS 请求类型是“not-so-simple 请求”。事实上,如果您使用 Rest Client/curl 向同一台服务器发送相同的请求,一切正常。
当你在网上浏览这个错误时,你发现解决方案丢失了,所有导致在 server-side 配置 CORS,但事实是我已经实现了 CORS。
可能的解决方案:
所有这些促使我找到了同样的解决方案,经过大量搜索,我发现这个问题可能有破解/替代方法,如以下博客中的最佳定义:
https://laracasts.com/discuss/channels/laravel/cros-access-control-allow-origin-not-in-the-headers
博客基本上要求您在从 UI 传递到 Front-end 的所有请求之间创建一个中间件,如果您知道您的应用程序是真实的,请添加
myOptions.headers.append('Access-Control-Allow-Origin', '*');
在中间件。希望对你有帮助。
更改 Spring 配置以允许 CORS 可能不是一个好主意。我所做的是安装一个 nginx 服务器并将其用作反向代理。下面是我的示例 nginx.conf:
# Spring backend
location /api
{
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://localhost:8493;
}
# Angular front end
location /
{
root www/apps;
try_files $uri $uri/ /index.html;
}
在您的 spring 引导安全配置中,我看到 csrf 已启用。但是在您的请求中,我没有看到任何 cookie XSRF-TOKEN
和 header X-XSRF-TOKEN
。这就是为什么服务器不接受请求并以 403 响应进行响应的原因。
您的浏览器可能没有创建 SSL 连接。当浏览器发现不正确的证书或自签名证书或由不受信任的证书颁发机构签名的证书时,通常会发生这种情况。
如果您使用 chrome 浏览器,您可以通过启用 allow-insecure-localhost
属性 为本地主机启用此功能以进行测试。在地址栏中输入这个URL,点击启用并重启Chrome。
chrome://flags/#allow-insecure-localhost
请参阅此 link 了解有关在 chrome 和 firefox 中启用它的更多详细信息。 https://improveandrepeat.com/2016/09/allowing-self-signed-certificates-on-localhost-with-chrome-and-firefox/
我认为您的请求中应该有一个 X-XSRF-TOKEN 令牌 header 因为在您的安全配置中启用了 csrf。 对我来说这很有效:
constructor(private http: HttpClient, private tokenExtractor: HttpXsrfTokenExtractor) {
}
然后提取一个令牌
const token = this.tokenExtractor.getToken() as string;
并将其添加到 header
this.http.post<any>(url, body, {headers: new HttpHeaders().set('X-XSRF-TOKEN', token)})
希望这对您有所帮助。
CORS 由浏览器强制执行,但在这种情况下,您将从服务器获得 403。正如其他人所提到的,您的请求似乎缺少 CSRF header,这可能就是服务器拒绝您的请求的原因。我不知道为什么当你切换到 HTTPS 时开始发生这种情况,但我可以告诉你你的配置中有 <csrf/>
但没有匹配的 header。
为了验证这个理论,您可以通过更改来禁用 CSRF 保护:
<csrf/>
至:
<csrf disabled="true"/>
从 Spring 开始 4 默认启用 CSRF 保护,因此仅删除 <csrf/>
是不够的。
一旦您确认 CSRF 是问题并且您不再收到 403,您应该以 Angular supports. It's an important protection that will protect your users. Angular expects a cookie named XSRF-TOKEN
which it will send back with X-XSRF-TOKEN
header. Those are configurable, but should match Spring's default if you're using latest Spring and latest Angular. Follow Spring documentation 添加的方式重新打开它:
<http>
<!-- ... -->
<csrf token-repository-ref="tokenRepository"/>
</http>
<b:bean id="tokenRepository"
class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
p:cookieHttpOnly="false"/>