【spring boot】https 前后端分离 跨域请求爬的坑
一·项目背景
后端 基于 spring boot搭建,所有的请求做了 https ,开始并没有做前后端分离,因为前后端分离是大势所趋,
不管以后后端开发 是否 会替代 前端开发,前后端分离会越来越流行。
所以准备把项目做成前后端分离,没想到第一步就遇到了跨域请求的坑。如果你对照了网上所有跨域请求的例子都没有成功
可以参考我的,是否和你遇到的问题一致。
前端 基于node js 搭建的 vue 项目。
二·示例
开始认为处理跨域请求是很简单的事情,不就是在 消息头里 添加一个Access-Control-Allow-Origin
配置允许跨域请求的域名嘛。
后端实现 跨域请求其实就是相当简单的。
有两种方式
1·filter
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class CorsFilter implements Filter {
final static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CorsFilter.class);
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
2·spring boot 方式/**
* 说明:
*
* @author WangBin
* @version v1.0
* @date 2018/1/21/021.
*/
@Configuration
public class CorsConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*","http://www.baidu.com/")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT")
.maxAge(3600);
}
}
Access-Control-Allow-Origin :允许跨域请求的 域名,底层接收 String 数组,传多个可以用 逗号分隔如
“http://www.baidu.com/”,"http://www.csdn.com/"
允许所有域名进行跨域访问 填写“*”,基于安全问题考虑,不建议填写 *
Access-Control-Allow-Methods:跨域请求 允许的 请求方式,允许所有填写 *
Access-Control-Max-Age:每次跨域请求前会发送一个OPTIONS请求,携带当前跨域请求的方法和域名,
去服务端检查,是否允许当前请求可以通过,如 当前请求的域名没有在Access-Control-Allow-Origin
里是不能继续发送真正的请求的。Access-Control-Max-Age 存的是当前校验的 有效时间 单位是秒
Access-Control-Allow-Headers:允许发送的内容类型,如
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
三·坑
前端错误:XMLHttpRequest cannot load http://localhost:8080/user/login.
No 'Access-Control-Allow-Origin'
header is present on the requested resource. Origin 'null' is therefore not allowed access.
既然如此简单 那么坑在哪呢。坑在哪呢,坑其实都是自己埋下的。
1·先说前端的坑吧
前端使用的 axios ,我把 request 请求做了个封装代码大致如下:
/*Created by WangBin on 2017.12.06.
名称:全局请求 拦截器 处理
说明:*/
import axios from 'axios'
import {Message} from 'element-ui'
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter);
// 创建axios实例
const service = axios.create({
//请求地址前缀
baseURL: 'https://172.18.12.36:8080/',
timeout: 10000 // 请求超时时间
})
// request拦截器
service.interceptors.request.use(config => {
//请求头部增加token信息
// config.headers['LoginToken'] = '123';
return config
}, error => {
console.log(error)
Promise.reject(error)
})
// respone拦截器
service.interceptors.response.use(
response => {
//未登录的跳转到登录页面
if (response.data.status == 9500) {
location.href="/#/login";
} else {
return response;
}
},
error => {
console.log('err' + error)
Message({
message: error.message,
type: 'error',
duration: 10 * 1000
})
return Promise.reject(error)
})
export default service
这段代码的目的 是 把所有的 request 请求发送前都做一个拦截,并可以在拦截后可以做一些处理,如添加消息头,处理参数后发送请求给后端,不详细说了。
并编写了一个 util js
/*Created by WangBin on 2017.12.06.
名称:请求封装
说明:*/
import request from '../utils/request'
export function getLoginToken() {
return localStorage.getItem("LoginToken");
// return "456";
}
/*get请求*/
export function getRequest(url,data) {
data["LoginToken"] = getLoginToken();
return request({
url: url,
method: 'get',
params: data
})
}
/*delete请求*/
export function deleteRequest(url,data) {
data["LoginToken"] = getLoginToken();
return request({
url: url,
method: 'delete',
params: data
})
}
/*POST请求*/
export function postRequest(url,data) {
data["LoginToken"] = getLoginToken();
return request({
url: url,
method: 'post',
params:data
})
}
把不同的请求 做了个封装,现在看起来 好像每个请求都是一样的 只是 method 不同,然后 在每个参数data 的基础上 添加了 LoginToken
第一个参数 传 post 地址 URL 第二个传 要提交的参数 如:
var Form = {
userName:this.userName,passWord:this.passWord}
postRequest("user/login", Form).then((response) => {
看起来似乎没什么问题,最后发现 OPTIONS 请求发送到后端 ,前端竟然报错了,404
地址肯定没写错,也是post 提交。问题 出在于,虽然 是post 请求,但是 提交的参数 是在URL 里拼接的。因为后端做了 请求拦截,
只有user/login 不会被拦截,其他的地址都会被重定向到 登录页面。所以导致了 404
根本原因是 使用axios post 方法不对,正确方式:
var params = new URLSearchParams();
params.append('userName', data.userName);
params.append('passWord', data.passWord);
postRequest("user/login",params).then((response) => {
console.log(response)
}, (response) => {
console.log(response)
}) .catch(function (response) {
console.log(response)
})
这样 便不会在 URL里拼接。
发送请求后前端竟然还报错:
XMLHttpRequest cannot load https://127.0.0.1:5211/user/login. Redirect from 'https://127.0.0.1:5211/user/login' to '
https://127.0.0.1:5211/login.html' has been blocked by CORS policy: Request requires preflight, which is disallowed
to follow cross-origin redirect.
是因为 跨域请求不允许 重定向,为什么会重定向呢,
2·后端的坑
因为 后端 在登录的时候 报错了
你是不是高兴的太早了,一个深坑 马上来了。
请求成功是 在 360 浏览器上 操作的。那么 继续做前端开发,换用 火狐浏览器,
前端
报错 errError: Network Error
经过一番测试 发现 控制台 有如下错误 ,原因是 证书 和 本地 测试域名 不匹配 导致浏览器没有信任,360浏览器貌似是没有做 这些校验
访问 我们要请求的地址:
添加信任即可。
上一篇: express框架