服务端Filter解决跨域方法

问题一、浏览器是先执行请求还是先判断跨域?

浏览器请求–>判断响应中是否有允许跨域–>发现不允许跨域,阻止跨域

说明:当执行跨域请求时,浏览器会提示当前接口不被允许,这说明浏览器已发出了当前请求,但是它的的响应内容被拦截;如果在Response header中的Access-Control-Allow-Origin设置的允许访问源不包含当前源,则拒绝数据返回给当前源。

问题二、判断当前请求是否是跨域请求?

通过查看当前请求的Request Headers 中是否存在Origin属性,当前属性存储的是当前域的信息

问题三、什么是简单请求和非简单请求?

说明:浏览器在发送跨域请求时会先判断当前请求是不是简单请求,如果是简单请求浏览器则会先执行请求,再判断是否支持跨域;如果是非简单请求它会先发送一个预检命令(即OPTIONS请求),检查通过后再把当前请求发出去。

(1)简单请求:方法为GET、HEAD、POST的请求,并且请求头(header)里面没有自定义头;Content-Type为text/plain、multipart/form-data、application/x-www-form-urlencoded。

(2)非简单请求:方法为PUT、DELETE的请求,发送JSON格式的ajax请求、带自定义请求头的ajax请求。

例如:发送JSON格式数据的ajax请求

复制代码
$.ajax({
  type : “post”,
  url: url,
  contentType : “application/json;charset=utf-8”,
  data: JSON.stringify({name: “小明”}),
  success: function(json){
    var result = json;
  }
});
复制代码
问题四:Access-Control-Allow-Headers是什么?有什么作用?

响应头部 Access-Control-Allow-Headers 用于 preflight request (预检请求)中,列出了将会在正式请求的 Access-Control-Expose-Headers 字段中出现的首部信息。简单首部,如 simple headers、Accept、Accept-Language、Content-Language、Content-Type (只限于解析后的值为 application/x-www-form-urlencoded、multipart/form-data 或 text/plain 三种MIME类型(不包括参数)),它们始终是被支持的,不需要在这个首部特意列出。

问题五:Access-Control-Max-Age是什么?
  浏览器的同源策略,就是出于安全考虑,浏览器会限制从脚本发起的跨域HTTP请求(比如异步请求GET, POST, PUT, DELETE, OPTIONS等等),所以浏览器会向所请求的服务器发起两次请求,第一次是浏览器使用OPTIONS方法发起一个预检请求,第二次才是真正的异步请求,第一次的预检请求获知服务器是否允许该跨域请求:如果允许,才发起第二次真实的请求;如果不允许,则拦截第二次请求。Access-Control-Max-Age用来指定本次预检请求的有效期,单位为秒,,在此期间不用发出另一条预检请求。

例如:res.addHeader(“Access-Control-Max-Age”, “3600”),表示隔60分钟才发起预检请求

问题六:Access-Control-Allow-Origin:*是否满足所有跨域场景?

带Cookie值的跨域请求,此时Access-Control-Allow-Origin:*无法满足支持跨域请求。

复制代码
$.ajax({
  type : “get”,
  url: url,
  xhrFields:{
    withCredentials:true
  },
  success: function(json){
    var result = json;
  }
});
复制代码

说明:

(1)当请求带有Cookie信息时,Access-Control-Allow-Origin的值必须要跟请求域的信息完全相同,不能使用通配符"*";所以后台设置可以通过直接获取请求信息中Access-Control-Allow-Origin值作为响应信息中Access-Control-Allow-Origin的值

复制代码
HttpServletResponse res = (HttpServletResponse) response;
HttpServletRequest req = (HttpServletRequest) request;
String origin = req.getHeader(“Origin”);
if (!org.springframework.util.StringUtils.isEmpty(origin)) {
  //带cookie的时候,origin必须是全匹配,不能使用*
  res.addHeader(“Access-Control-Allow-Origin”, origin);
}
复制代码
  (2)当请求带有Cookie信息的跨域请求时,Access-Control-Allow-Credentials的值设置为true

// enable cookie
res.addHeader(“Access-Control-Allow-Credentials”, “true”);

注意:

(1)、带Cookie的跨域时,Access-Control-Allow-Origin不能写星号("*"),必须要写具体的域名

(2)、发送的Cookie必须是被调用方域名的Cookie,而不是调用方域名的Cookie

问题七、带自定请求头的跨域请求如何设置?

JQuery的ajax请求如何设置自定义请求头方法:

复制代码
$.ajax({
  type : “get”,
  url: url,
  headers:{
    “sessionId” : “123456”
  },
  beforeSend: function(xhr){
    xhr.setRequestHeader(“token”,“654321”)
  },
  success: function(json){
    var result = json;
  }
});
复制代码
 服务端可设置支持所有自定义请求头

String headers = req.getHeader(“Access-Control-Request-Headers”);
// 支持所有自定义头
if (!org.springframework.util.StringUtils.isEmpty(headers)) {
  res.addHeader(“Access-Control-Allow-Headers”, headers);
}

在被调用方解决跨域(支持跨域)通过自定义Filter来实现:

(1)在SpringBoot工程创建自定拦截器CrosFilter,如图:

复制代码
package *;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.tomcat.util.buf.StringUtils;

public class CrosFilter implements Filter {

@Override
public void init(FilterConfig filterConfig) throws ServletException {
    // TODO Auto-generated method stub
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    // TODO Auto-generated method stub

    HttpServletResponse res = (HttpServletResponse) response;
    HttpServletRequest req = (HttpServletRequest) request;
    String origin = req.getHeader("Origin");
    if (!org.springframework.util.StringUtils.isEmpty(origin)) {
        //带cookie的时候,origin必须是全匹配,不能使用*
        res.addHeader("Access-Control-Allow-Origin", origin);            
    }
    res.addHeader("Access-Control-Allow-Methods", "*");
    String headers = req.getHeader("Access-Control-Request-Headers");
    // 支持所有自定义头
    if (!org.springframework.util.StringUtils.isEmpty(headers)) {
        res.addHeader("Access-Control-Allow-Headers", headers);            
    }
    res.addHeader("Access-Control-Max-Age", "3600");
    // enable cookie
    res.addHeader("Access-Control-Allow-Credentials", "true");
    chain.doFilter(request, response);
}

@Override
public void destroy() {
    // TODO Auto-generated method stub
}

}
复制代码
(2)注册自定拦截器CrosFilter,作用于全局:

复制代码
package *;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;

import com.fasterxml.jackson.databind.ser.impl.FilteredBeanPropertyWriter;

@SpringBootApplication
public class AjaxserverApplication {

public static void main(String[] args) {
    SpringApplication.run(AjaxserverApplication.class, args);
}

@Bean
public FilterRegistrationBean registerFilter() {
    FilterRegistrationBean bean = new FilterRegistrationBean();
    bean.addUrlPatterns("/*");
    bean.setFilter(new CrosFilter());
    return bean ;
}

}
复制代码
(3)、创建接口服务层

复制代码
package *;

import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {

@GetMapping("/get")
public ResultBean get() {
    System.out.println("TestController.get()");
    return new ResultBean("get ok");
}

@PostMapping("/postJson")
public ResultBean postJson(@RequestBody User user) {
    System.out.println("TestController.postJson()");

    return new ResultBean("postJson " + user.getName());
}

@GetMapping("/getCookie")
public ResultBean getCookie(@CookieValue(value = "cookie1") String cookie1) {
    System.out.println("TestController.getCookie()");
    return new ResultBean("getCookie " + cookie1);
}

@GetMapping("/getHeader")
public ResultBean getHeader(@RequestHeader("x-header1") String header1,
        @RequestHeader("x-header2") String header2) {
    System.out.println("TestController.getHeader()");
    
    return new ResultBean("getHeader " + header1 + " " + header2);
}

}
复制代码
(4)、前端接口调用测试

复制代码
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
// 请求的接口的前缀 // http://localhost:8080/test
var base = “/http://localhost:8080/test”;
//测试模块
describe(“ajax跨越完全讲解”, function() {
  // 测试方法
  it(“get请求”, function(done) {
  // 服务器返回的结果
var result;
$.getJSON(base + “/get”).then(function(jsonObj) {
  result = jsonObj;
});
// 由于是异步请求,需要使用setTimeout来校验
setTimeout(function() {
  expect(result).toEqual({
  “data” : “get ok”
});
// 校验完成,通知jasmine框架
done();
 }, 100);
  });
  
  //此处测试采用的是jasmine测试框架
   …

});

参考资料

[^1 ]跨域讲解学习二(服务端Filter解决跨域方法)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JSONP 是一种解决跨域问题的常用方法,它利用了 script 标签不受同源策略限制的特性。具体实现方法如下: 1. 服务器端需要提供一个回调函数,该函数的名称由客户端通过 URL 参数传递。 2. 客户端通过动态创建 script 标签的方式向服务器发送请求,并传递回调函数名称作为 URL 参数。 3. 服务器端将返回的数据包装在回调函数中,并将其作为 JavaScript 代码返回给客户端。 4. 客户端接收到响应后,会自动执行回调函数,从而获取到服务器返回的数据。 以下是一个使用 JSONP 解决跨域问题的示例: 服务端代码(假设回调函数名称为callback): ```python import json def jsonp(request): data = { 'name': '张三', 'age': 20, 'gender': '男' } callback = request.GET.get('callback') response_data = json.dumps(data) response = f"{callback}({response_data})" return HttpResponse(response, content_type='application/javascript') ``` 客户端代码: ```javascript function jsonp(url, callback) { const script = document.createElement('script'); script.src = `${url}&callback=${callback}`; document.body.appendChild(script); } function callback(data) { console.log(data); } jsonp('http://example.com/jsonp', 'callback'); ``` 在上面的示例中,客户端调用 jsonp 函数时,会向 http://example.com/jsonp 发送一个跨域请求,并指定回调函数名称为 callback。服务端返回的数据会被包裹在 callback 函数中,并以 JavaScript 代码的形式返回给客户端。客户端接收到响应后,会自动执行 callback 函数,从而获取到服务器返回的数据。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值