前端跨域问题

跨域定义
跨站 HTTP 请求(Cross-site HTTP request)是指发起请求的资源所在域不同于该请求所指向资源所在的域的 HTTP 请求。跨站 HTTP正常请求,但是结果被浏览器拦截了,就是跨域问题。跨域问题只有在浏览器才会出现,javascript等脚本的主动http请求才会出现跨域问题。后端获取http数据不会存在跨域问题。

怎么才算跨域?
那我我们要先理解何为同源(同域)。
如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源。不同源就是跨域。
下表给出了相对http://store.company.com/dir/page.html跨域检测的示例:

链接跨域与否原因
http://store.company.com/dir2/other.html
http://store.company.com/dir/inner/another.html
https://store.company.com/secure.html不同协议 ( https和http )
http://store.company.com:81/dir/etc.html不同端口 ( 81和80)
http://news.company.com/dir/other.html不同域名 ( news和store )

跨域情况总结如下

  • 不同协议 (https和http)
  • 不同端口 (80和81)
  • 不同域名 (news和store)

注:IE会有点不一样,更加宽松,但是前端兼容是短柄,所以不用理会IE。

为什么要有跨域限制?
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
还是安全问题,如果不限制,那么CSRF(Cross-site request forgery,中文名称:跨站请求伪造)攻击就很容易实现了。

举个例子,假设没有跨域限制:
假设你是a.com网站的管理员,你在a.com网站有一个权限是删除用户,比如说这个过程只需用你的身份登陆并且POST数据到http://a.com/deleteUser,就可以实现删除操作。
然后假设有个b.com网站被攻击了,别人种下了恶意代码,你点开的时候就会模拟跨域请求(你同时在浏览器中不同标签打开了a.comb.com网站,并且你已经登录了a.com),那么b.com的恶意代码就可以模拟对a.com的跨域请求,模拟一个用户删除操作是很简单的(当然是攻击者有意针对,而且知道了删除接口)。
通过例子可知道,这是多危险的事情,所以浏览器都会做跨域限制。

什么是预请求?
预请求就是使用OPTIONS方法。跨域请求首先需要发送预请求,即使用 OPTIONS 方法发起一个预请求到服务器,以获知服务器是否允许该实际请求。预请求的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。

跨域才会有预请求,但是不是所有跨域请求都会发送预请求的。 预请求服务器正常返回,浏览器还要判断是否合法,才会继续正常请求的。所以web服务程序需要针对options做处理,要不然OPTIONS的请求也会运行后端代码。一般预请求最好返回204(NO-Content)。

谷歌开发者工具上查看网络请求时,如果预请求是不在XHR这个分类中,可以在Other分类或者ALL中查看。

什么时候会有预请求?
一般服务器默认允许GETPOSTHEAD请求(前提跨域),所以这些请求,只要前端脚本不追加请求头,是不会有预请求发出的。这些请求叫简单请求

可以简单总结为只有GETPOSTHEAD才可能没有预请求。

大多数浏览器不支持针对于预请求重定向。如果一个预请求发生了重定向,浏览器将报告错误:

The request was redirected to 'https://example.com/foo', which is disallowed for cross-origin requests that require preflight
简单举个javascript代码例子:

有预请求:

fetch('http://localhost:3000/demo-01', {
  method: 'get',
  headers: {
    //这里定义了请求头就一定会有预请求。
    'Cache-Control': 'no-cache',
    Pragma: 'no-cache'
  },
})

无预请求:

fetch('http://localhost:3000/demo-01', {
  method: 'get'
})

跨域流程图
在这里插入图片描述
一些名称说明
跨域请求也是跨站请求,叫法不一样。
HTTP请求头也是HTTP请求首部
HTTP响应头也是HTTP响应首部

跨域解决方案
正如大家所知,出于安全考虑,浏览器会限制脚本中发起的跨站请求。但是为了能开发出更强大、更丰富、更安全的Web应用程序,开发人员渴望着在不丢失安全的前提下,Web 应用技术能越来越强大、越来越丰富。

Web 应用工作组( Web Applications Working Group )推荐了一种的机制,即跨源资源共享(Cross-Origin Resource Sharing (CORS)

跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站有权限访问哪些资源。

跨域需要设置的HTTP首部字段
实现前后端跨域请求,需要设置下面相关的HTTP响应头:

字段名必须设置与否
Access-Control-Allow-Origin
Access-Control-Allow-Credentials
Access-Control-Allow-Methods
Access-Control-Allow-Headers
Access-Control-Max-Age
Access-Control-Expose-Headers

一般只要设置好 Access-Control-Allow-Origin就可以跨域了,其他的字段都是配合使用的(其他字段有默认值)。

Access-Control-Allow-Origin
跨域允许就是这个字段设置的,默认不设置时不允许跨域。
origin参数指定一个允许向该服务器提交请求的URI.对于一个不带有credentials(即cookie)的请求,可以指定为'*',表示允许来自所有域的请求。如果想要更安全点,当然也可以指定URI。
例子
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: http://foo.example

Access-Control-Allow-Credentials
此字段是用来设置是否允许传cookie,默认为false
注意Access-Control-Allow-Credentials设置为true时,Access-Control-Allow-Origin不能为*,需要指定具体访问的域名来源。

Access-Control-Allow-Methods
默认值一般为GETHEADPOST,所以delete等方法的时候,默认会被限制。
指明资源可以被请求的方式有哪些(一个或者多个)。这个响应头信息在客户端发出预检请求的时候会被返。这就看需要了。
例子
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Methods: *

Access-Control-Allow-Headers
浏览器自身附带的请求头默认是被允许的,但是前端代码追加的请求头,在跨域的时候是要被允许才可访问。
而且浏览器本身默认自带请求头是不可修改的,如User-AgentOrigin等。
例子
Access-Control-Allow-Methods: token
Access-Control-Allow-Headers: *

Access-Control-Max-Age
这个响应头告诉我们这次预请求的结果的有效期是多久,有效期期间内的请求都不用使用预请求

Access-Control-Expose-Headers
CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定
后端设置拦截器

@Component
public class CrossDomainInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) {
        String originHeader = httpServletRequest.getHeader("Origin");
        httpServletResponse.setHeader("Access-Control-Allow-Origin", originHeader);
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
        httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", "Access-Control-Allow-Orgin,XMLHttpRequest,Accept,Authorization,authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Mx-ReqToken,X-Requested-With");
      //需要注意的是,如果要发送Cookie,即前端Access-Control-Allow-Credentials设为true
      //那么Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名
        httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpServletResponse.setHeader("Access-Control-Expose-Headers", "Accept-Ranges, Content-Encoding, Content-Length, Content-Range" + "," + "authorization");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) {
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
    }

springboot下设置跨域

/*跨域问题 springboot*/
    @Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        config.setMaxAge(3600L);
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(1);
        return bean;
    }

声明:本文转载自 https://segmentfault.com/a/1190000006727486?utm_medium=referral&utm_source=tuicool

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值