欢迎光临
我们一直在努力

web跨域问题终结者

自行阅读

  • web代码: 一句话: 要显式配置withCredentials=true还是false,因为不同浏览器默认值不同. 需要cookie就true
  • 浏览器: 规则执行者,给前后端进行各种限制,其实纯粹退裤子放屁.
  • 代理服务器(正向or 反向代理): 我能随便修改请求和响应
  • 服务端:
 public class WebConfig implements WebMvcConfigurer {  public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*")  .allowedMethods("*") .allowedheaders("*") .exposedHeaders("*") .maxAge(3600)  .allowCredentials(true); } } 复制代码

1.1 效果描述

划重点:

服务端必须返回特定的access-control-Allow-Headers和Access-Control-Allow-Origin,

不能是星号,否则会被浏览器拦截,认为跨域失败

 Access-Control-Allow-Credentials: true Access-Control-Allow-Headers: app-version, content-type, device-id, device-type, version-code Access-Control-Allow-Methods: POST Access-Control-Allow-Origin: http://localhost:60664 Access-Control-Max-Age: 3600 Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH Connection: keep-alive Content-Length: 0 Date: Tue, 11 Oct 2022 10:47:45 GMT Keep-Alive: timeout=60 Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers 复制代码

预检请求后的正常请求里, 服务端返回:

 Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: http://localhost:60664 Connection: keep-alive Content-Type: application/json Date: Tue, 11 Oct 2022 10:47:47 GMT Keep-Alive: timeout=60 Transfer-Encoding: chunked Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers 复制代码
 Access-Control-Allow-Headers: app-version, content-type, device-id, device-type, version-code Access-Control-Allow-Methods: POST Access-Control-Allow-Origin: * Access-Control-Max-Age: 3600 Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH Connection: keep-alive Content-Length: 0 Date: Tue, 11 Oct 2022 10:50:48 GMT Keep-Alive: timeout=60 Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers 复制代码
  • 此时,此时后台还是可以set-cookie,但web端无法在下次请求携带
  • 此时,如果web端代码设置了Credentials=true,那么这个响应会被浏览器拦截掉,cors失败
  • 此时,如果web端设置withCredentials为false,则可以请求,但不携带cookie. 下次正常请求时,后端的响应头为:
  •  Access-Control-Allow-Origin: * Connection: keep-alive Content-Type: application/json Date: Tue, 11 Oct 2022 10:55:15 GMT Keep-Alive: timeout=60 Transfer-Encoding: chunked Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers 复制代码

本质上就是修改预检请求和正常请求的响应头,欺骗浏览器.

  • 反向代理: nginx
  • 开发时localhost的正向代理: nodejs,或者dart的shelf-proxy

写法应该参照上面贴出的抓包的响应头来处理,而不是全部设置为*

spring boot cros配置里全部设置为*时,spring 有进行针对性处理. 那么我们自己编写修改响应头代码时,也要遵守对应的规范:

对shelf-proxy的改写:

修改的完全版本.

// 修改响应头 //有时后台写了Access-Control-Allow-Origin,那么server.defaultResponseHeaders的设置就会无效 //if("OPTIONS" == (clientResponse?.request?.method??"")){ Map<String, String> headers = clientResponse.headers //不能同时多个 headers.remove('access-control-allow-origin') headers.remove('access-control-allow-methods') headers.remove('access-control-allow-headers') headers.remove('access-control-expose-headers') headers.remove('access-control-max-age') headers.remove('access-control-allow-credentials') ​ ​ ​ //你请求什么,就允许什么 //access-control-request-headers 这个是chrome加了,所以在request!.headers里取不到 //Request header field app-version is not allowed by Access-Control-Allow-Headers in preflight response. //Map<String, String> reqeustHeaders = clientResponse!.request!.headers! String headerStr = clientResponse!.request! .headers['access-control-request-headers'].toString() //预检请求不会携带额外的header,所以下面拼接header没有鸟用, 要用access-control-request-headers取 //reqeustHeaders.forEach((key, value) { headerStr = headerStr+","+key //access-control-request-headers ​ if (headerStr == "null") { headerStr = "*" } clientResponse.headers['Access-Control-Allow-Headers'] = headerStr //clientResponse.headers['Access-Control-Allow-Headers'] = "*" clientResponse.headers['Access-Control-Allow-Methods'] = "*" clientResponse.headers['Access-Control-Expose-Headers'] = headerStr clientResponse.headers['Access-Control-Max-Age'] = '36000' clientResponse.headers['Access-Control-Allow-Credentials'] = 'true' //预检请求: 设置了-Allow-Credentials'] = 'true'时,两个限制: // Access-Control-Allow-Origin'] 不能为 "*" -Allow-Headers'] = "*" ​ //clientResponse.headers['Access-Control-Allow-Origin'] = "*" String original = clientResponse!.request!.headers["Referer"].toString() if(original == "null"){ original = "*" } if (original.endsWith("/")) { original = original.substring(0, original.length - 1) } clientResponse.headers['Access-Control-Allow-Origin'] = original //The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. // The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute. 复制代码

ngnix的配置

一般测试环境正式环境部署时,都是关掉spring boot里的cors配置,使用nginx重写响应头. 逻辑和上面的shelf-proxy的逻辑一致.

location /pub/(.+) { if ($http_origin ~ <允许的域(正则匹配)>) { add_header 'Access-Control-Allow-Origin' "$http_origin"; add_header 'Access-Control-Allow-Credentials' "true"; if ($request_method = "OPTIONS") { add_header 'Access-Control-Max-Age' 86400; add_header 'Access-Control-Allow-Methods' 'GET, POST,PUT, OPTIONS, DELETE'; add_header 'Access-Control-Allow-Headers' 'reqid, nid, host, x-real-ip, x-forwarded-ip, event-type, event-id, accept, content-type';  add_header 'Content-Length' 0; add_header 'Content-Type' 'text/plain, charset=utf-8'; return 204; } }  ...... } 复制代码

注意:注意:注意:

上面是add_header,要生效就要把web服务器内部自己的cors配置关掉,否则出现多个响应头. 导致跨域失败.

如果被代理的服务器是别人的,有部分这些响应头,则会造成跨域失败. 此时,应该去直接修改响应头.

修改响应头可参考:

在上面基础上修改.

不需要:

 add_header 'Access-Control-Allow-Credentials' "true"; 复制代码

此时可以设置*了:

 add_header 'Access-Control-Allow-Origin' * 复制代码

修改响应头里的set-cookie

将cookie的samesite设置为None,此时Secure也要设置为true.

域名一般设置为二级域名,比较通用

开发时,一般是本地代理服务器直接改:

void transferCookies(http.StreamedResponse clientResponse,String localHost) { String? cookie = clientResponse.headers['set-cookie'] if (cookie == null || cookie.isEmpty) { return } //服务器要发送多个 cookie,则应该在同一响应中发送多个 Set-Cookie 标头。 Cookie cookie2 = Cookie.fromSetCookieValue(cookie) cookie2.secure = true cookie2.httpOnly = false cookie2.domain = localHost clientResponse.headers['set-cookie'] = cookie2.toString() + " ​ print("reset set-cookie header from $cookie to \n ${clientResponse.headers['set-cookie']}\n") } 复制代码

上线时,一般是应用服务器写cookie时配置好

比如普通返回时:

 StpUtil.logout();  ResponseCookie cookie2 = ResponseCookie.from("navi-token","")  .httpOnly(false)  .secure(true)   .path("/")  .maxAge(10L)  .sameSite("None")  .build(); response.setHeader(HttpHeaders.SET_COOKIE, cookie2.toString()); 复制代码

又比如sa-token里配置:

 @Bean @Primary public SaTokenConfig getSaTokenConfigPrimary() { SaTokenConfig config = new SaTokenConfig(); config.setTokenName("navi-token");   config.setActivityTimeout(-1);  config.setIsConcurrent(false);  config.setIsShare(false);  config.setTokenStyle("uuid");  config.setIsLog(true); ​ SaCookieConfig cookieConfig = new SaCookieConfig(); cookieConfig.setSameSite("None"); cookieConfig.setSecure(true);  cookieConfig.setDomain(".xxxxx.tech"); ​ config.setCookie(cookieConfig); return config; } 复制代码
  • 海报
海报图正在生成中...
赞(0) 打赏
声明:
1、本博客不从事任何主机及服务器租赁业务,不参与任何交易,也绝非中介。博客内容仅记录博主个人感兴趣的服务器测评结果及一些服务器相关的优惠活动,信息均摘自网络或来自服务商主动提供;所以对本博客提及的内容不作直接、间接、法定、约定的保证,博客内容也不具备任何参考价值及引导作用,访问者需自行甄别。
2、访问本博客请务必遵守有关互联网的相关法律、规定与规则;不能利用本博客所提及的内容从事任何违法、违规操作;否则造成的一切后果由访问者自行承担。
3、未成年人及不能独立承担法律责任的个人及群体请勿访问本博客。
4、一旦您访问本博客,即表示您已经知晓并接受了以上声明通告。
文章名称:《web跨域问题终结者》
文章链接:https://www.456zj.com/18541.html
本站资源仅供个人学习交流,请于下载后24小时内删除,不允许用于商业用途,否则法律问题自行承担。

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址