同源策略与跨域问题解决

同源策略与跨域问题解决

一、同源策略

  • 同源指的是"三个相同"
    协议相同
    域名相同
    端口相同

如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的

举个例子:

下表给出了相对http://a.bbb.com/dir/page.html同源检测的示例:

URL结果原因
http://a.bbb.com/dir2/other.html成功
http://a.bbb.com/dir/inner/another.html成功
https://a.bbb.com/secure.html失败不同协议 ( https和http )
http://a.bbb.com:81/dir/etc.html失败不同端口 ( 81和80)
http://a.opq.com/dir/other.html失败不同域名 ( bbb和opq)
  • 不同源导致的结果
1) Cookie、LocalStorage 和 IndexDB 无法读取。
#IndexedDB是一种使用浏览器存储大量数据的方法,它创造的数据可以被查询,并且可以离线使用。

(2) DOM 无法获得。

(3) AJAX 请求不能发送。

二、跨域问题解决

1、跨域问题

由同源策略导致的的AJAX请求失败

例如:页面路径为: http://127.0.0.1:8848/fileTest/test.html

​ 后台接口地址为:http://localhost:8080/sayHello

产生的结果:

在这里插入图片描述

此即为跨域请求失败

2、 CORS 解决跨域

​ CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

在此之前,需要知道简单请求、复杂请求

简单请求:

某些请求不会触发 CORS 预检请求。本文称这样的请求为“简单请求”,请注意,该术语并不属于 Fetch (其中定义了 CORS)规范。若请求满足所有下述条件,则该请求可视为“简单请求”:

复杂请求:

​ 与前述简单请求不同,“需预检的请求”要求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。当请求满足下述任一条件时,即应首先发送预检请求:

2.1 当请求为简单请求时

若需要跨域,则需要在请求头里添加Origin字段

在这里插入图片描述
请求至后台若符合后台请求规则则可成功请求,并且后台会返回相应的规则

在这里插入图片描述

2.2 当请求为复杂请求时

​ 当请求为复杂请求时,浏览器会在请求前先发送一个OPTIONS请求

OPTIONS是一个预检请求,与简单请求不同的是它会额外携带两个参数:
Access-Control-Request-Method:该次请求的请求方式
Access-Control-Request-Headers:该次请求的自定义请求头字段

在这里插入图片描述
OPTIONS发送至后台若后台成功响应,则继续发送复杂请求,若响应失败则不会发送复杂请求

]
继续发送复杂请求

在这里插入图片描述

2.3 OPTIONS注意事项

(1)、OPTIONS不会携带body若后台方法上使用@RequestBody注解,则会报错:

org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing:

例如:

在这里插入图片描述
此时需要在@RequestBody后添加参数required=false,允许body为空

在这里插入图片描述

即可解决该问题

​ (2)、OPTIONS请求不应被过多的发送,因为它也是一个http请求也会占用一部分资源,相应的一种解决方法

在响应中添加缓存字段,以减少OPTIONS请求

response.setHeader("Access-Control-Max-Age", "3600");
2.5 允许跨域的后台服务配置

(1)、在Java web工程中加如下列过滤器即可

package com.demo.origin.config;

import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.logging.Logger;

/**
 * @author : Daniel
 * Time : 13:31
 * Effect :Please describe the function of this class
 * @date : 2019/11/20
 */
@WebFilter(filterName="corsFilter",urlPatterns="/*")
@Component
public class CorsFilter implements Filter {
    private static final Logger logger = Logger.getLogger("CorsFilter");
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("过滤器初始化");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        //指定允许其他域名访问,一般用法(*,指定域,动态设置),3是因为*不允许携带认证头和cookies
        //response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        //允许的请求类型(这里只配置了常用的几种,可根据实际情况进行追加)
        response.setHeader("Access-Control-Allow-Methods", "POST,GET,PUT,DELETE,OPTIONS");
        //预检结果缓存时间
        response.setHeader("Access-Control-Max-Age", "3600");
        //允许的请求头字段
        response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token,Access-Control-Allow-Headers,Authorization");
        //是否允许后续请求携带认证信息(cookies),该值只能是true,否则不返回
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("XDomainRequestAllowed","1");
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        filterChain.doFilter(request,response);
    }

    @Override
    public void destroy() {
        logger.info("结束");
    }
}

(2)、在nginx中配置

location / {  
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS,PUT';
    add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
    add_header Access-Control-Allow-Credentials 'true';
    add_header Access-Control-Max-Age '3600';
    add_header XDomainRequestAllowed '1';

    if ($request_method = 'OPTIONS') {
        return 204;
    }
} 
3、使用反向代理解决跨域问题

​ 使用反向代理工具解决跨域问题:如Nginxnode.js

​ 以Nginx为例:

​ 在Nginx的配置文件中加入:

location / {
    proxy_pass http://localhost:8080;
}

location /fileTest {
    proxy_pass http://localhost:8848;
}

启动Nginx再以

http://127.0.0.1/fileTest/test.html 地址访问页面

http://127.0.0.1/sayHello 访问后台接口

发现可正常请求

在这里插入图片描述

3.1 原因

​ 上述以

​ http://127.0.0.1/fileTest/test.html 地址访问页面

​ http://127.0.0.1/sayHello 访问后台接口

的方式遵循了,浏览器的同源策略自然不存在跨域

3.2 原理

​ Nginx代理了页面请求和接口请求,所以页面请求和接口请求都在Nginx源内由Nginx去完成页面请求和接口的请求即:http://127.0.0.1:80内

​ 所以浏览器访问页面和接口都是以http://127.0.0.1:80来进行访问的,满足了同源策略自然不存在跨域之说

图解:

跨域:

在这里插入图片描述

Nginx代理,满足同源策略

在这里插入图片描述

Nginx反向代理其实是一种欺骗浏览器的方法

参考

 [1]: http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html	"浏览器同源政策及其规避方法"
 [2]: http://www.ruanyifeng.com/blog/2012/09/xmlhttprequest_level_2.html	"XMLHttpRequest Level 2 使用指南"
 [3]: http://www.ruanyifeng.com/blog/2016/04/cors.html	"跨域资源共享 CORS 详解"
 [4]: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS	"HTTP访问控制(CORS)"
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值