jfinal解决ajax跨域问题

1 分析:

设置:getResponse().addHeader("Access-Control-Allow-Origin", "*");在不同版本的浏览器中可能报错:

    IE报错信息:http://localhost/auth/covert?code=c30643cd-fdfc-4d97-bcc9-eb74046c6d47 的 XMLHttpRequest 需要跨域资源共享(CORS)。 

    Chrome: jquery-1.11.0.js:9666 Access to XMLHttpRequest at 'http://localhost/auth/covert?code=ca36d3f6-1338-4242-8f7a-fd96e2ada46c' from origin 'http://127.0.0.1:8020' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

    Failed to load xxxxxxxxxxxxxx : 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’. Origin ‘http://127.0.0.1’ is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

jquery-1.11.0.js:9666 Access to XMLHttpRequest at 'http://localhost/auth/covert?code=4921aab6-7cf1-47f8-8c05-c618e56988b4' from origin 'http://127.0.0.1:8020' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.

2 代码实现

模拟各位网友反馈问题后调整后的代码如下:  

基于jquery的ajax访问:

	ajax:function(url,data,callback){
		var param={
			type:"post",
			async:false, // true 异步,false 同步
			url:url,
			data:data,
			dataType:"json",
			header:{
				token:"abc"
			},
			error:function(xhr,status,error){
				console.log("ajax错误:xhr",xhr);
				console.log("ajax错误:status",status);
				console.log("ajax错误:error",error);
			},
			success:function(result,status,xhr){
				callback(result);
			},
			// 下面的两行代码,就是解决跨域的关键
		    xhrFields: {withCredentials: true},
		    crossDomain: true
		};
		$.ajax(param);
	}


java代码:Controller的方法上

	/**
	 * 
	 * 使用上面生成的code置换token:code只能使用一次
	 * */
	public void covert(String code) {
//		String origin=getRequest().getHeader("Origin");
//		getResponse().addHeader("Access-Control-Allow-Origin", origin);
//		getResponse().addHeader("Access-Control-Allow-Credentials", "true");
//		getResponse().addHeader("Access-Control-Allow-Methods","POST,GET,OPTIONS,DELETE"); //支持的http 动作
//		getResponse().addHeader("Access-Control-Allow-Headers","simple headers,Accept,Accept-Language,Content-Language,Content-Type,token");  //响应头 请按照自己需求添加。
		
		String origin=getRequest().getHeader("Origin");
		getResponse().addHeader("Access-Control-Allow-Origin", origin);
		getResponse().addHeader("Access-Control-Allow-Credentials", "true");
		getResponse().addHeader("Access-Control-Allow-Methods","*"); //使用通配符
		getResponse().addHeader("Access-Control-Allow-Headers","*");  //使用通配符
		String token= TokenCache.get(code);
		String refresh_token= TokenCache.get(code+"_refresh");
		if(token==null) {
			renderJson(Response.fail("code过期,置换token失败!"));
		}else {
			renderJson(Response.ok("token", token).set("refresh_token", refresh_token));
		}
	}

通过请求,明确跨域的来源。


3 答疑

3.1 header添加token或者Authorization,做前后分离授权

ajax中添加headers,如果不是http协议支持的header,跨域时浏览器安全策略直接阻止发送(观察后台:是否收到http request)。

IE:SCRIPT7002: XMLHttpRequest: 网络错误 0x80070005, 拒绝访问。

Chrome:jquery-1.11.0.js:9666 Access to XMLHttpRequest at 'http://localhost/auth/covert' from origin 'http://127.0.0.1:8020' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.

分析:通过浏览器观察先发option请求,为什么呢?其实就是“"预请求"”,即设置Access-Control-Allow-Headers

参考:ajax跨域 自定义header问题总结 https://www.cnblogs.com/holdon521/p/6225045.html

3.2  cors-filter第三方库没有使用过

分析一下:如果Tomcat能够部署成功,理论上undertow应该也可以。

配置:cors.properties

cors.allowGenericHttpRequests=true
cors.allowOrigin=*
cors.allowSubdomains=false
cors.supportedMethods=GET, PUT, HEAD, POST, DELETE, OPTIONS
cors.supportedHeaders=Origin, X-Requested-With, Content-Type, Accept, Authorization
cors.exposedHeaders=
cors.supportsCredentials=true
cors.maxAge=3600

UndertowServer启动:

		UndertowServer server = UndertowServer.create(AppConfig.class);
		//配置shiro
		server.configWeb(webBuilder -> {
			webBuilder.addListener(EnvironmentLoaderListener.class.getName());
			webBuilder.addFilter("ShiroFilter", ShiroFilter.class.getName());
			webBuilder.addFilterInitParam("ShiroFilter", "configPath", "classpath:shiro.ini");
			webBuilder.addFilterUrlMapping("ShiroFilter", "/*", DispatcherType.REQUEST);
			webBuilder.addFilterUrlMapping("ShiroFilter", "/*", DispatcherType.FORWARD);
			webBuilder.addFilterUrlMapping("ShiroFilter", "/*", DispatcherType.INCLUDE);
			webBuilder.addFilterUrlMapping("ShiroFilter", "/*", DispatcherType.ERROR);
			// webBuilder.addFilterInitParam("ShiroFilter", "key", "value");
			
			webBuilder.addFilter("CorsFilter", CORSFilter.class.getName());
			webBuilder.addFilterInitParam("CorsFilter", "cors.configurationFile", "cors.properties");
			webBuilder.addFilterUrlMapping("CorsFilter", "/*", DispatcherType.REQUEST);
			webBuilder.addFilterUrlMapping("CorsFilter", "/*", DispatcherType.FORWARD);
			webBuilder.addFilterUrlMapping("CorsFilter", "/*", DispatcherType.INCLUDE);
			webBuilder.addFilterUrlMapping("CorsFilter", "/*", DispatcherType.ERROR);
		});
		
		server.start();


评论区

JFinal

2019-08-12 20:55

这个分享值得收藏,下次有人再问我有关 ajax 跨域的问题,直接给这个链接即可,谢谢分享

happyboy

2019-08-13 09:23

直接使用jsonp解决跨域不行吗,━━∑( ̄□ ̄*|||━━

yunyun

2019-08-14 02:30

还是不行 ajax 已经设置了
xhrFields: {withCredentials: true}
crossDomain: true
Controller中也设置了
String origin=getRequest().getHeader("Origin");
getResponse().addHeader("Access-Control-Allow-Origin", origin);
getResponse().addHeader("Access-Control-Allow-Credentials", "true");
还是报错
Access to XMLHttpRequest at 'http://127.0.0.1/login' from origin 'http://127.0.0.1:8080' has been blocked by CORS policy: Request header field access_token is not allowed by Access-Control-Allow-Headers in preflight response.
搞了几天了 什么办法都试了 就是解决不了

yunyun

2019-08-14 02:36

@JFinal web.xml 中配置cors-filter 可以解决跨域 换成undertow启动 加载cors-filter 也不起作用 用这篇文章得办法 也没用 老大有没有undertow 跨域的demo 不然我就只能打war包 用tomcat这唯一的办法了

yunyun

2019-08-14 02:38

或者用Nginx

dafeizi

2019-08-14 09:57

servlet同理的,这类问题

Psbye

2019-08-14 16:58

建议部分再优化,针对在IE下也是有问题
基于layui也有类似跨域问题
https://fly.layui.com/jie/41645/#item-1560759146253

,crossDomain: true == !(document.all)

guocw998

2019-08-15 11:35

@yunyun 配置:Access-Control-Allow-Headers设置允许的请求头,access_token是你自定义的,须设置允许。不设置你会发现请求发送了2次,第一次是options请求,失败返回302,成功返回202。具体原理参考:https://www.w3.org/TR/2014/REC-cors-20140116/#cross-origin-request-with-preflight-0

bufanui

2019-08-16 10:51

其实可以在拦截器里配置cors,针对浏览器探测请求options可以单独处理.

李小小小

2019-09-26 19:22

@yunyun 遇到同样的问题,老哥有没有解决方案?

yunyun

2019-09-30 11:47

@李小小小 用Nginx了 反正怎么配置都不行