跨域校验是浏览器的行为,旨在提升不同站点访问的安全性。在提升安全性的同时也增加了开发者的开发难度,为了解决跨域问题,开发者们想出了一些列解决方案,来允许跨域(更确切地说是在可控范围内允许跨域)。在前后端分离流行的今天,前端和后端都发展出了解决跨域的方法,本篇文章主要讲解后端的跨域解决方案。
1、跨域问题本地测试
在本地搭建前端、后端服务,用花生壳做内网穿透,前端接口调用指向穿透链接。即可模拟跨域场景,同时也可以在本地调试。【只要端口不同,不用做内网穿透也是可以重现跨域场景的】
image.png
2、服务端跨域解决方案
新增filter,设置响应header的跨域参数。
参数包括:
Access-Control-Allow-Origin:允许跨域的请求发起源网站。不要粗暴的写成"*",要从请求头中获取origin,明确具体的来源。
Access-Control-Allow-Credentials:允许客户端携带验证信息。若前端设置了withCredentials: true则此处必须设置为true。
Access-Control-Allow-Methods:允许跨域的方法。GET\POST\PUT\DELETE
Access-Control-Max-Age
Access-Control-Allow-Headers
在解决跨域问题时也要注意浏览器的提示,有的放矢,不要胡乱猜测,一通操作。
The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '' when the request's credentials mode is 'include'.
image.png
检查Access-Control-Allow-Origin是否设置成,改成具体的前端网址地址,或者((HttpServletRequest) servletRequest).getHeader("origin")即可。
The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'.
image.png
检查Access-Control-Allow-Credentials是否设置成true。
3、复杂请求put和delete的跨域解决方案
有些同学设置了跨域后,发现put和delete依然存在跨域,其原因是put和delete为复杂请求,复杂请求会先发起options请求,判断服务器是否支持后续的请求,所以不能简单的return,return前需要将put,delete等配置好跨域。见代码中的setHeader方法。
4、代码
随便找个目录放进去就可以了。
package pro.haichuang.ktwelve.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.RequestMethod;
import java.io.IOException;
import org.springframework.http.HttpStatus;
import pro.haichuang.ktwelve.util.L;
@Configuration
@Order(value = 0)
@WebFilter(filterName = "CorsFilterConfig", urlPatterns = "/*")
public class CorsFilterConfig implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
L.i(new Throwable().getStackTrace()[0] + "===============CorsFilterConfig执行=================");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) servletResponse;
String origin = ((HttpServletRequest) servletRequest).getHeader("origin");
res.setHeader("Access-Control-Allow-Origin", origin);
res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader("Access-Control-Max-Age", "1728000");
res.setHeader("Access-Control-Allow-Headers", "Authentication, Authorization, content-type, Accept, x-requested-with, Cache-Control");
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
String requestMethod = httpRequest.getMethod();
if (requestMethod.equals(RequestMethod.OPTIONS.name())) {
setHeader(httpRequest, httpResponse);
return;
}
filterChain.doFilter(servletRequest, res);
}
private void setHeader(HttpServletRequest request, HttpServletResponse response) {
//跨域的header设置
String origin = request.getHeader("origin");
response.setHeader("Access-Control-Allow-Origin", origin);
//这句可以解决当请求为put和delete时的跨域问题
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
//防止乱码,适用于传输JSON数据
response.setHeader("Content-Type", "application/json;charset=UTF-8");
response.setStatus(HttpStatus.OK.value());
}
@Override
public void destroy() {
}
}
网友评论