问题:
由于项目登陆系统新增登陆模式,需要新增接口对接登陆系统,同时为了兼容以前老登陆接口的各个功能特性,须兼容之前登陆模式。
而之前老登陆系统,为转跳访问接口,接口直接重定向。
问题出现原因:
由于需要整合新登录接口,同时支持老登陆的功能特性,因此将老登陆的功能特性实现代码提前为公共代码。
由于老登陆逻辑直接使用的是登陆系统 中的登陆页面,所以使用的是ajax验证验证码,验证成功进行页面转跳重定向。【全部操作均在登录系统中完成,不涉及ssion跨域】
而新登录使用同样方法,将出现ssion跨域。所以没有使用该方法,且使用ajax访问接口,如果是response.sendRedirect(url); 将不会进行重定向
解决办法:
新登录使用接口方式,返回json数据, app项目user_api 暴露一个接口提供前端访问(如/xxx)【json数据返回注解@ResponseBody】, /xxx接口中实现对登陆接口的访问,获取token或登陆系统返回的验证码错误信息, 判断登陆系统返回的信息,
如果是错误信息则对app返回json错误信息,
如果是正确的,开始通过token换取用户信息,进行老登陆系统的业务功能逻辑。在进行老登陆用逻辑之前,设置HttpServletResponse 的响应头 允许跨域,设置响应头白名单(具体原因后面会说)【红色为必不可少的设置】
//设置响应头 response.setHeader("Access-Control-Allow-Origin","*"); response.setHeader("Access-Control-Allow-Headers","x-requested-with,content-type,REDIRECT,CONTENTPATH"); response.setHeader("Access-Control-Expose-Headers","REDIRECT,CONTENTPATH"); |
在原来的 重定向代码前 添加ajax请求判断【ajax 请求会在请求头中夹带X-Requested-With 消息头】
//判断ajax请求 if("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))){ //前端需要判断是否是重定向 response.setHeader("REDIRECT","REDIRECT"); //需要重定向的路径 response.setHeader("CONTENTPATH",url); response.setStatus(HttpServletResponse.SC_FORBIDDEN); }else{ response.sendRedirect(url); } |
对于ajax不能进行重定向, 后面介绍原因。
设置好服务端之后,现在修改前端
【由于跨域请求 jsonp不会有ajax的请求头,所以我们需要自己添加该标记,否者服务端无法判断是否是ajax请求】
补充:JSONP调用本质上不是AJAX调用。因此,在JSONP调用中,您将看不到任何X-Requested-With头字段。
$.ajax({ type: dataType:"JSONP", headers:{'X-Requested-With':'XMLHttpRequest'}, url:', data:{ phone:params.phone,//手机号 password:params.password,//验证码 }, success:function(data){ codeError(data); }, error:function(XMLHttpRequest,textStatus,errorThrown){ if(XMLHttpRequest.status==403){ if("REDIRECT"==XMLHttpRequest.getResponseHeader("REDIRECT")){//若HEADER中含有REDIRECT说明后端想重定向, varwin=window; while(win!=win.top){ win=win.top; } win.location.href=XMLHttpRequest.getResponseHeader("CONTENTPATH");//将后端重定向的地址取出来,使用win.location.href去实现重定向的要求 } } }, }); |
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制
三个与CORS请求相关的字段,都以Access-Control-开头
Access-Control-Allow-Origin
该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。
Access-Control-Allow-Credentials
该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。
Access-Control-Expose-Headers
该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。
【】
非简单跨域请求
会发送一个预请求,检测是否与服务器相通。
"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。
服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。
服务器回应的其他CORS相关字段如下
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
Access-Control-Allow-Methods
该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
Access-Control-Allow-Headers
如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
Access-Control-Allow-Credentials
该字段与简单请求时的含义相同。
Access-Control-Max-Age
该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。
参考文档
跨域资源共享 CORS 详解
来自 <http://www.ruanyifeng.com/blog/2016/04/cors.html>
来自 <https://codeday.me/bug/20180112/116116.html>
如有错误还望指正