在写这学期课设时,页面没有采用jsp,前端是对床在他本子上写的,我提供了url给他,他的ajax请求后后台却返回了403,但是这时候我写的jsp测试却没有问题,开始怀疑是表单提交和ajax提交的请求类别不同,于是在网上搜,果然找到了一些线索:
- 当请求方法不是GET\POST\HEAD之一或者请求类型不是application/x-www-form-urlencoded、multipart/form-data、text/plain之一时,请求就是复杂请求
而ajax请求的类型是application/json,也就是json。这时的请求自然就是复杂请求了,至于复杂请求到底复杂在哪了,我猜是不是因为json结构可以很复杂。 - 这就涉及到安全问题了,为了安全同源策略规定浏览器对跨域的请求有不同的处理:
如果是跨域的简单请求则浏览器在请求头中加上Origin头后直接发送,Origin对应的值为请求方的域名。服务器发现请求头中有Origin字段,就会验证对应域名是否是被允许的,然后进行相应处理。**
如果是复杂请求,则浏览器会先发一个预检请求,类型为OPTIONS,询问服务器是否该域名被允许发送请求,如果允许,则浏览器收到回应后,会发送真正的请求,这一切对用户而言都是隐藏的。
服务器对跨域请求的响应头中有以下内容:
Access-Control-Allow-Origin:服务器允许跨域请求的域名
Access-Control-Expose-Headers:允许返回给跨域请求的响应头列表
Access-Control-Max-Age:预检请求返回的结果的有效时间,有效时间内浏览器不用再次向服务器发送预检请求
Access-Control-Allow-Credentials:告知浏览器当withCredentials属性设置为true时,是否可以显示跨域请求返回的内容。简单请求时,浏览器会根据此响应头决定是否显示响应的内容。预先验证请求时,浏览器会根据此响应头决定在发送实际跨域请求时,是否携带认证信息。
Access-Control-Allow-Methods:服务端支持的请求方法
Access-Control-Allow-Headers:服务端支持的请求头
知道了这些,那问题就容易解决了,我们可以在后台设置一个拦截器,拦截带有Origin字段的请求头,并给响应头中加入那些字段,设置为相应的值。
package gh.ttms.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class OptionsInterceptor implements HandlerInterceptor {
//private List<String> excludedUrls;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
if (httpServletRequest.getHeader("Origin")!=null){
httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
httpServletResponse.setHeader("Access-Control-Allow-Methods", "*");
httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
httpServletResponse.setHeader("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept");
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
// public List<String> getExcludedUrls() {
// return excludedUrls;
// }
//
// public void setExcludedUrls(List<String> excludedUrls) {
// this.excludedUrls = excludedUrls;
// }
}
tips:
- 这里有一个小问题就是如果设置了Access-Control-Allow-Credentials为true,则Access-Control-Allow-Origin不能设为*
- 跨域就是不同域之间的交互,域名不同或域名相同端口不同等都属于不同域,更多请自行百度
- 网上有说配置<mvc:cros/>的,但是这个好像对复杂请求没啥用,可能是因为对复杂请求浏览器会发送预检请求吧
- 我还试过直接针对OPTIONS类型的请求写了个Controller,如下,作用是给响应头中加那些字段,最后记得字段加进去了但跨域请求还是403,目前还不知道为啥
package gh.ttms.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletResponse;
@Controller
public class OptionsInterceptor {
@RequestMapping(value = "/**",method = RequestMethod.OPTIONS)
public void handler(HttpServletResponse response)
{
response.setHeader("Access-Control-Allow-Origin","*");
response.setHeader("Access-Control-Allow-Methods","*");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept");
}
}