JAVA WEB之跨域问题以及解决方案(上)

目录

一、产生跨域的原因

二、错误描述

三、HTTP访问控制(CORS)

四、解决方案

      1、@CrossOrigin 注解
      2、设置头部信息
      3、配置拦截器
      4、web.xml配置
      5、HttpClient 转发请求
      6、spring-context.xml配置

五、带token的跨域问题解决方案


JAVA WEB之跨域问题以及解决方案(下)

一、产生跨域的原因

我们在项目开发中,经常会使用前后端分离的技术,把前端代码和后端代码分别部署到不同的服务器上,这样在数据交互的时候,由于浏览器的同源策略,就会产生跨域问题。

同源策略,它是由Netscape提出的一个著名的安全策略。
现在所有支持JavaScript 的浏览器都会使用这个策略。
所谓同源是指,域名,协议,端口相同。
如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。

同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收。

http://www.a.com:8080/b.js

二、错误描述

Failed to load https://example.com/: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘https://anfo.pl’ is therefore not allowed access. If an opaque response serves your needs, set the request’smode to ‘no-cors’ to fetch the resource with CORS disabled.
在这里插入图片描述
在这里插入图片描述

三、HTTP访问控制(CORS)

跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个指定域名上的Web应用被准许访问来自不同源服务器上的指定的资源。

  • 简单请求:不会触发 CORS 预检请求(GET、HEAD、POST)
  • 预检请求:必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。(PUT、DELETE、CONNECT、OPTIONS、TRACE PATCH)
  • HTTP 响应首部字段:Access-Control-Allow-Origin、Access-Control-Expose-Headers、Access-Control-Max-Age、Access-Control-Allow-Credentials、Access-Control-Allow-Methods、Access-Control-Allow-Headers(在下文使用时,再做详细介绍)
  • HTTP 请求首部字段:Origin、Access-Control-Request-Method、Access-Control-Request-Headers(在下文使用时,再做详细介绍)

更多CORS的细节…

四、解决方案

1、@CrossOrigin 注解
  • 适用于jdk 1.8及以上
  • @CrossOrigin:允许所有ip跨域访问
  • @CrossOrigin(origins=“xxx.xxx.xxx.xxx”):只允许指定ip跨域

直接在Controller类或者方法上添加

@CrossOrigin
@Controller
public class BookController {
	//do something...
}

2、设置头部信息

首先要了解一下以下几个名词:

1、 CacheController(http协议的缓存控制),常见的值有:

  • no-cache:告诉浏览器、缓存服务器,不管本地副本是否过期,使用资源副本前,一定要到源服务器进行副本有效性校验。
  • must-revalidate:告诉浏览器、缓存服务器,本地副本过期前,可以使用本地副本;本地副本一旦过期,必须去源服务器进行有效性校验。
  • no-store:用于防止重要的信息被无意的发布,在请求消息中发送将使得请求和响应消息都不使用缓存。
  • 注意:仅适用于http 1.1版本

2、Pragma:

  • no-cache:指示请求或响应消息不能缓存。即,资源不能从cache中获取,而必须回源获取。
  • 兼容http 1.0 和 http 1.1版本

3、Expires:浏览器会在指定过期时间内使用本地缓存,指明应该在什么时候认为文档已经过期,从而不再缓存它,时间为格林威治时间GMT。

4、Access-Control-Allow-Origin:指定允许访问该资源的外域 URI,它的值可为:

  • 一个完整的域名(例如,http://www.baidu.com)
  • " * " :允许任意域名

接下来是实现代码:

1、定义Controller类的父类,对头部信息进行设置

package test;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

public class SetHeaderController {

    protected void writeJsonResponse(Object responseObj, HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");

        //禁用缓存,确保网页信息是最新数据
        // HTTP 1.1
        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        // HTTP 1.0
        //response.setHeader("Pragma", "no-cache");

        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setDateHeader("Expires", 0);
        PrintWriter writer = getWriter(response);
        writeJsonResponse(responseObj, writer);
    }

    protected PrintWriter getWriter(HttpServletResponse response) {
        if (null == response) {
            return null;
        }

        PrintWriter writer = null;
        try {
            writer = response.getWriter();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return writer;
    }


    protected void writeJsonResponse(Object responseObj, PrintWriter writer) {
        if (writer == null || responseObj == null) {
            return;
        }
        try {
            writer.write(JSON.toJSONString(responseObj, SerializerFeature.DisableCircularReferenceDetect));
        } finally {
            writer.flush();
            writer.close();
        }
    }
}

实现功能的Controller调用代码:
Message类是自己写的,用于封装返回给前端的信息

package test;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import po.Message;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@Controller
public class BookController extends SetHeaderController {

    @RequestMapping("/addBook.do")
    public void addBook(HttpSession httpSession, HttpServletRequest httpServletRequest, HttpServletResponse
                        httpServletResponse){
        Message message = new Message();
        //do something...
        writeJsonResponse(message, httpServletResponse);
    }
}

3、配置拦截器

首先介绍拦截器使用到的几个名词:

1、Access-Control-Allow-Methods:首部字段用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法。

2、Access-Control-Max-Age:指定了preflight请求的结果能够被缓存多久。(以秒为单位)

3、Access-Control-Allow-Headers: 用于预检请求的响应。其指明了实际请求中允许携带的首部字段。

4、Access-Control-Allow-Credentials :指定了当浏览器的credentials设置为true时是否允许浏览器读取response的内容。

  • 当用在对preflight预检测请求的响应中时,它指定了实际的请求是否可以使用credentials。
  • 请注意:简单 GET 请求不会被预检;如果对此类请求的响应中不包含该字段,这个响应将被忽略掉,并且浏览器也不会将相应内容返回给网页。

Access-Control-Allow-Origin已经在前面解释过,这里就不再赘述。

拦截器代码:

package test;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CORSFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        HttpServletResponse resp = (HttpServletResponse) response;

        resp.setContentType("text/html;charset=UTF-8");
        
        resp.setHeader("Pragma","No-cache");
        resp.setHeader("Cache-Control","no-cache");
        resp.setHeader("Access-Control-Allow-Origin", "*");
        resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, HEAD, DELETE, PUT");
        resp.setHeader("Access-Control-Max-Age", "3600");
        resp.setHeader("Access-Control-Allow-Headers",
                "X-Requested-With, Content-Type, Authorization, Accept, Origin, User-Agent, Content-Range, Content-Disposition, Content-Description");

        resp.setDateHeader("Expires", -10);
        chain.doFilter(request, resp);
    }

    @Override
    public void init(FilterConfig filterConfig) {}

    @Override
    public void destroy() {}
}

在web.xml中声明该过滤器:

<filter>
	<filter-name>cors</filter-name>
	<filter-class>test.CORSFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>cors</filter-name>
	<!-- 开放的接口前缀 -->
	<url-pattern>/*</url-pattern>
</filter-mapping>
  • 注意:放在其他过滤器的前面

JAVA WEB之跨域问题以及解决方案(下)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章