跨域问题的产生
前台调用后台接口,因为前台和后台不在同一个域内产生跨越问题。
- 浏览器限制
- 跨域
- XHR(XMLHttpRequest)请求
三个条件同时产生才能发生跨域问题。
解决思路
从浏览器角度解决
使用不检查跨域的方式启动浏览器
- **\chrome --disable-web-security --user-data-dir=g:\temp3
不存在任何的跨域问题,可以自由访问。
此种解决方法说明,跨域是浏览器限制的行为,跟后台没有任何的关系。
针对XHR使用JSONP访问
JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。
JSONP是一种约定,非官方框架。
前端使用代码
- //每个测试用例的超时时间
- jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
- //每个请求接口的前缀
- var base = "http://localhost:8080/test";
- //测试模块
- describe("ajax跨域测试",function () {
- //测试方法
- it("jsonp请求",function (done) {
- //服务器返回结果
- var result;
- $.ajax({
- url: base + "/get1",dataType: "jsonp",success: function (json) {
- result = json;
- }
- });
- //由于是异步请求,需要使用setTimeout来校验
- setTimeout(function () {
- expect(result).toEqual({
- "data": "get1 ok"
- });
- //校验通过,通知jasmine框架
- done();
- },100);
- });
- });
- package com.fwj.ajax;
- import org.springframework.web.bind.annotation.ControllerAdvice;
- import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice;
- @ControllerAdvice
- public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice{
- public JsonpAdvice(){
- super("callback");
- }
- }
使用Jsonp后端需要做改动,需要返回非Json对象。
关于Jsonp成功的原因
- jsonp发送的请求是script请求,区别于xhr请求,不存在跨域的问题。
- 普通的xhr请求返回的是json数据对象,jsonp返回的是一个js脚本。
- 请求URL的不同,请求带有callback字段
原理:jsonp请求中,对应后台中约定的callback,json代码的内容就是callback的值作为函数名,返回的的数据作为函数的参数。
JSONP弊端
被调用方解决跨域
服务器端实现
被调用发-Filter解决方案。
【简单请求】:
方法为GET,HEAD,POST,并且请求header里面无自定义头,Content-Type为text/plain,multipart/form-data,application/x-www-form-urlencoded中的一种。
浏览器对于简单是先执行拿到数据再判断。
【非简单请求】:
除简单请求之外都是非简单请求。
工作中常见的非简单请求:
1. put.delete方法的ajax请求。
2. 发送json格式的ajax请求。
3. 带自定义头的ajax请求。
浏览器对于非简单请求是先发送预检命令,然后再执行
在后端增加Filter配置,在响应头中添加需要的Header。
- import javax.servlet.*;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- public class CrossFilter implements Filter {
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- }
- @Override
- public void doFilter(ServletRequest servletRequest,ServletResponse servletResponse,FilterChain filterChain) throws IOException,ServletException {
- HttpServletResponse response = (HttpServletResponse) servletResponse;
- HttpServletRequest request= (HttpServletRequest) servletRequest;
- String origin = request.getHeader("Origin");
- if(org.springframework.util.StringUtils.isEmpty(origin)){
- //允许所有的远程请求
- response.addHeader("Access-Control-Allow-Origin","*");
- }
- String headers = request.getHeader("Access-Control-Request-Headers");
- if(org.springframework.util.StringUtils.isEmpty(headers)) {
- //允许自定义头,网页可以请求各种内容类型
- response.addHeader("Access-Control-Allow-Headers",headers);
- }
- //允许所有的请求方式
- response.addHeader("Access-Control-Allow-Methods","*");
- //允许OPTIONS预检命令缓存3600秒
- response.addHeader("Access-Control-Max-Age","3600");
- //使Cookie生效
- response.addHeader("Access-Control-Allow-Credentials","true");
- //放行
- filterChain.doFilter(servletRequest,response);
- }
- @Override
- public void destroy() {
- }
- }
将此Filter加入Spring配置文件。
- package com.fwj.ajax;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.boot.web.servlet.FilterRegistrationBean;
- import org.springframework.context.annotation.Bean;
- @SpringBootApplication
- public class AjaxApplication {
- public static void main(String[] args) {
- SpringApplication.run(AjaxApplication.class,args);
- }
- @Bean
- public FilterRegistrationBean registerFilter(){
- FilterRegistrationBean bean =new FilterRegistrationBean();
- bean.addUrlPatterns("/*");
- bean.setFilter(new CrossFilter());
- return bean;
- }
- }
关于OPTIONS预检命令
OPTIONS它用于获取当前URL所支持的方法。若请求成功,则它会在HTTP头中包含一个名为“Allow”的头,值是所支持的方法,如“GET,POST”。
关于带Cookie类型的跨域
使用Cookie时”Access-Control-Allow-Methods”,不能为 *,必须使用全匹配。
Access-Control-Allow-Methods 需要为调用方的域名并且,Cookie需要存在于被调用方的域中。
并且需要设置
- Access-Control-Allow-Credentials : true
前端代码如下
- //测试方法
- it("getCookie请求",function (done) {
- //服务器返回结果
- var result;
- $.ajax({
- type:"get",url: base + "/getCookie",xhrFields:{
- withCredentials:true
- },100);
- });
Nginx配置
配置原理图
- server{
- listen 80;
- server_name b.com;
- location/{
- proxy_pass http://localhost:8080/;
- add_header Access-Control-Allow-Methods *;
- add_header Access-Control-Max-Age 3600;
- add_header Access-Control-Allow-Credentials true;
- add_header Access-Control-Allow-Origin $http_origin;
- add_header Access-Control-Allow-Headers &http_access_control_request_headers;
- if ($request_method = OPTIONS){
- return 3600;
- }
- }
- }
Nginx技巧:
APACHE配置
略
Spring框架解决方案
在需要跨域的Controller类或者需要跨域的方法上增加注解
- @CrossOrigin
调用方解决跨域
Nginx配置
- server{
- listen 80;
- server_name a.com;
- location/{
- proxy_pass http://localhost:8081/;
- }
- location/ajaxserver{
- proxy_pass http://localhost:8080/test/;
- }
- }
调用方需要访问前缀地址 “/ajaxserver”
- eg:http:/a.com/ajaxserver/get1
APACHE配置
略