一、跨域概念解析(@CrossOrigin)
跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器施加的安全限制。
所谓同源是指,域名,协议,端口均相同,不明白没关系,举个栗子:
http://a.yagnxigua.com/index.html 调用 http://a.yagnxigua.com/server.php (非跨域)
http://www.123.com/index.html 调用 http://www.456.com/server.php (主域名不同:123/456,跨域)
http://a.yagnxigua.com/index.html 调用 http://b.yagnxigua.com/server.php (子域名不同:abc/def,跨域)
http://a.yagnxigua.com:8080/index.html 调用 http:/a.yagnxigua.com:8081/server.php (端口不同:8080/8081,跨域)
http://a.yagnxigua.com/index.html 调用 https://a.yagnxigua.com/server.php (协议不同:http/https,跨域)
请注意:localhost和127.0.0.1虽然都指向本机,但也属于跨域,同理域名和域名对应的ip也属于跨域。
浏览器执行javascript脚本时,会检查这个脚本属于哪个页面,如果不是同源页面,就不会被执行。
二、Spring Boot跨域(@CrossOrigin)
当然这里虽然指SpringBoot但是SpringMVC也是一样的,要求在Spring4.2及以上的版本
1、@CrossOrigin使用场景要求
- jdk1.8+
- Spring4.2+
2、@CrossOrigin源码解析(网络复制)
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
String[] DEFAULT_ORIGINS = { "*" };
String[] DEFAULT_ALLOWED_HEADERS = { "*" };
boolean DEFAULT_ALLOW_CREDENTIALS = true;
long DEFAULT_MAX_AGE = 1800;
/**
* 同origins属性一样
*/
@AliasFor("origins")
String[] value() default {};
/**
* 所有支持域的集合,例如"http://domain1.com"。
* <p>这些值都显示在请求头中的Access-Control-Allow-Origin
* "*"代表所有域的请求都支持
* <p>如果没有定义,所有请求的域都支持
* @see #value
*/
@AliasFor("value")
String[] origins() default {};
/**
* 允许请求头重的header,默认都支持
*/
String[] allowedHeaders() default {};
/**
* 响应头中允许访问的header,默认为空
*/
String[] exposedHeaders() default {};
/**
* 请求支持的方法,例如"{RequestMethod.GET, RequestMethod.POST}"}。
* 默认支持RequestMapping中设置的方法
*/
RequestMethod[] methods() default {};
/**
* 是否允许cookie随请求发送,使用时必须指定具体的域
*/
String allowCredentials() default "";
/**
* 预请求的结果的有效期,默认30分钟
*/
long maxAge() default -1;
}
3、@CrossOrigin使用
Spring Boot下的请求处理控制器
package com.example.demo.controller;
import com.example.demo.domain.User;
import com.example.demo.service.IUserFind;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
//实现跨域注解
//origin="*"代表所有域名都可访问
//maxAge飞行前响应的缓存持续时间的最大年龄,简单来说就是Cookie的有效期 单位为秒
//若maxAge是负数,则代表为临时Cookie,不会被持久化,Cookie信息保存在浏览器内存中,浏览器关闭Cookie就消失
@CrossOrigin(origins = "*",maxAge = 3600)
public class UserController {
@Resource
private IUserFind userFind;
@GetMapping("finduser")
public User finduser(@RequestParam(value="id") Integer id){
.......
}
}
三、自定义跨域对象对比@crossorigin注解
```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
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;
@Order(Ordered.HIGHEST_PRECEDENCE)
@WebFilter(filterName = "crossFilter", urlPatterns = "/*")
@Component
public class CrossFilter implements Filter {
private static final Logger LOGGER = LoggerFactory.getLogger(CrossFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
LOGGER.info("crossFiler init");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest servletRequest = (HttpServletRequest) request;
HttpServletResponse servletResponse = (HttpServletResponse) response;
servletResponse.setHeader("Access-Control-Allow-Origin", servletRequest.getHeader("Origin"));
servletResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, DELETE,PATCH");
servletResponse.setHeader("Access-Control-Allow-Headers", "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,SessionToken,Cookie");
servletResponse.setHeader("Access-Control-Max-Age", "3600");
servletResponse.setHeader("Access-Control-Allow-Credentials", "true");
servletResponse.setHeader("Access-Control-Expose-Headers", "*");
//判断是否为可选择性批量操作的请求,设置状态码返回
if ("OPTIONS".equals(servletRequest.getMethod())) {
LOGGER.info("---------用户请求 options" + ((HttpServletRequest) request).getRequestURI());
servletResponse.setStatus(HttpStatus.ACCEPTED.value());
return;
}
LOGGER.info("---------用户请求" + ((HttpServletRequest) request).getRequestURI());
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
```
参考
- https://blog.csdn.net/w_linux/article/details/81142413