Access to XMLHttpRequest at ‘http://127.0.0.1:8080/xxx’ from origin ‘http://192.168.1.103:8096’ has
但是这个方法明显也不适合我,第一是用的手机APP,用ionic,ionic.config.json当中没有配置代理的情况下访问A和B这两个后台只有B会出现跨域问题,说明不是这个问题。豁然开朗,因为发送请求时,会先发送一个OPTIONS,而这个配置,因为没有登录的原因,禁止使用,而前台的OPTIONS请求没有得到相应,就不能继续发送其他类似GET或者POST请求,这样就会出现跨域问题。而且,前台发生
原文链接:https://www.longkui.site/error/access-control-allow-origin/5734/
0.背景
手机APP一个页面请求一个后台看不到数据,本地调试后发现有个跨域问题,然后整个问题一边排查一边修改,经历1.5天后终于找到问题所在,写这篇文章简单总结一下。
技术栈:
后台:springMVC+mybatis
前台:angular
APP:ionic
架构:后台有两个,我们假设分布是后台A,后台B。手机APP访问后台A时,一切正常,手机APP访问后台B时出现跨域报错:
Response to preflight request doesn’t pass access control check: It does not have HTTP ok status.
1.解决方法—前端
一般跨域问题都改后端,但是为了排查问题也尝试去改了改前端。
1.在前端请求时加入请求头 Access-Control-Allow-Origin。经过测试没有用
- 设置代理地址,比如angular项目有proxy.config.json,我们可以参考下面这样设置:
{
“/”: {
“target”: “http://127.0.0.1:8080/”
}
}
上面就表示前台把所有的请求都转发到127.0.0.1:8080。但是这个方法明显也不适合我,第一是用的手机APP,用ionic,ionic.config.json当中没有配置代理的情况下访问A和B这两个后台只有B会出现跨域问题,说明不是这个问题。
2.测试后台
从上面基本可以断定不是前台的问题,大概率是后台问题,于是我在我在本地启动了一个空白网站,地址是:http://192.168.1.103:8096,浏览器打开这个网站,然后打开F12,参考下面代码粘贴上去:
var xhr = new XMLHttpRequest();
xhr.open(‘GET’, ‘http://127.0.0.1:8080/xxx’);
xhr.setRequestHeader(“Authorization”,‘123’);
xhr.send(null);
xhr.onload = function(e) {
var xhr = e.target;
console.log(xhr.responseText);
}
然后回车测试后台。
A.修改请求参数
一开始以为上面这样的代码请求头会有问题,于是给上面的请求头加上了Access-Control-Allow-Origin。变成下面这样:
var xhr = new XMLHttpRequest();
xhr.open(‘GET’, ‘http://127.0.0.1:8080/xxx’);
xhr.setRequestHeader(“Authorization”,‘123’);
xhr.setRequestHeader(“Access-Control-Allow-Origin”,‘*’);
xhr.send(null);
xhr.onload = function(e) {
var xhr = e.target;
console.log(xhr.responseText);
}
结果报错:
Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response.
B.修改Filter
网络上大部分的文章都是修改这个地方,这个文件定义在web.xml中,
<filter>
<filter-name>CORSFilter</filter-name>
<filter-class>com.xinhai.cms.controller.CorsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CORSFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
然后参考下面的写法:
import org.apache.commons.lang.StringUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CorsFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpServletRequest request = (HttpServletRequest) servletRequest;
response.setHeader("Access-Control-Allow-Origin", "*");
if (StringUtils.equalsIgnoreCase(request.getMethod(), "OPTIONS")) {
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept,X-Requested-With");
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {}
}
网上的很多文章基本都说增加 Access-Control-Allow-Origin 这个。
修改后,对我的问题并没有效果,还是报错。
C.使用注解
一些文章说是增加@CrossOrigion注解解决跨域问题,要求是spring 4.2以上的版本比如下面这样:
直接写在类上
@CrossOrigin(origins="*",maxAge=3600)
@RestController
public class LoginController {}
也可以写在方法上
@CrossOrigin("http://localhost:5173")
@GetMapping("/test")
public String test(){
return "test";
}
经过测试,对我的问题并没有效果
D.实现WebMvcConfigurer接口
@Configuration
public class WebMvcOriginConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")// 对所有请求进行跨域
.allowedOriginPatterns("http://localhost:5173")
.maxAge(-1)
.allowedMethods("*");
}
}
经过测试,对我的问题没有效果。
E.排查问题
上面大部分主流的方法都试了一遍,均没有效果,于是开始一点一点排查问题,先在CorsFilter中打上断点,发现根本不会进入断点。于是把所有的Filter全部打上断点开始观察,发现没有进入CorsFilter,进入了XssFilter,在单步运行后,代码不能继续往下执行,但是可以从调试信息中可以找到HttpServletRequest从中中看到前台发过来的请求,这表示前台的请求发送到后台了,但是在CorsFilter之前在某个地方被拦截或者被修改了。
而且,前台发生请求时,会发送两次请求,一次是options,options通过后发送get或者post请求。
在此想到web.xml有多个过滤器,应该一层一层的分析,看看先执行哪个。
此时,刚好看到一篇文章,说是项目中使用了Spring Security,参考这篇文章:
https://blog.csdn.net/java0506/article/details/120620447文章中说是要 新增Spring Security配置,改成下面这样:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors(AbstractHttpConfigurer::disable)
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(item -> item.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeRequests(req -> req
//非普通请求(比如请求新增了自定义头部信息,比如Jwt头),会发送预检Option请求,这里直接让他通过
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
.antMatchers("/user/biz/login").permitAll()
.anyRequest().authenticated())
.addFilterBefore(jwtSecurityFilter, UsernamePasswordAuthenticationFilter.class)
.httpBasic(AbstractHttpConfigurer::disable)
;
}
本来以为找到了解决办法,结果发现我们的项目中早就重写里面的DelagetingFilterProxy,而且A和B都用的同一个Filter。既然A使用这个Filter没有跨域问题,那么同理B也不会因为这个产生跨域问题。
继续排查问题….
还是查找web.xml中的配置问题,终于找到了一个配置,如下:
<security-constraint>
<web-resource-collection>
<web-resource-name>NoAccess</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint/>
</security-constraint>
security-constraint一般是用来授权和身份验证,那么上面这段配置是什么意思呢?
上面这段配置表示HTTP OPTIONS 和TRACE方法只能由经过身份验证的用户使用。
其中:
OPTIONS方法用来描述了目标资源的通信选项,会返回服务器支持预定义URL的HTTP策略。
TRACE方法用于沿着目标资源的路径执行消息环回测试;它回应收到的请求,以便客户可以看到中间服务器进行了哪些(假设任何)进度或增量。
豁然开朗,因为发送请求时,会先发送一个OPTIONS,而这个配置,因为没有登录的原因,禁止使用,而前台的OPTIONS请求没有得到相应,就不能继续发送其他类似GET或者POST请求,这样就会出现跨域问题。
解决办法,要么修改或注释掉这个配置(对本文APP适用)
要么登录成功后再使用(对本文APP问题不适用)
3.后记
实际测试中,在跨域时发现请求localhost:8080 和127.0.0.1:8080 的报错不一样,从192.168.1.103:8096请求localhost:808时会报错:
No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
后来发现这个是后台自己写的一个Filter导致的。
另外,测试过程中发现一个报错:
The request client is not a secure context and the resource is in more-private address space local
解决办法是打开chrome浏览器,输入 chrome://flags, 找到Bolck insecure private network requests 这个选项,将它改为Disabled。
(这个对于本文APP问题没有作用)
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)