前后端分离下跨域以及相关问题

文章分为三部分:

一:前后端分离跨域session不变问题(在不使用缓存数据库 如:redis  的情况下保证session不变)

二:Ajax跨域的同时传递身份信息

三:跨域情况下返回前台正常,但是ajax一直进入error函数

 

第一部分:

在不使用redis做session缓存的情况下,如何发送验证码呢?同时保证在一定时间内用户注册时sessionid不会改变?

我在一个注册发送验证码时,需要将验证码存储到session中,但是在跨域请求情况下,前台的ajax必须传递身份信息才能保证每次请求方法时的sessionid是一样的,否则在验证时永远也不找不到session中存储的验证码,下面是我的发送邮件的Controller层

 /**
     * 发送验证码
     * @return  发送成功返回Constant.CODE_YES  失败返回Constant.CODE_NO
     */
    @CrossOrigin
//    @RequestMapping(value="sendIdentityCode/{email}", method = RequestMethod.GET)
//    public String sendIdentityCode(@PathVariable String email){
    @RequestMapping("sendIdentityCode")
    @ResponseBody
    public String sendIdentityCode(
            @RequestParam("email") String email, HttpServletResponse response){
        super.setResponseOrigin(request,response);
        System.out.println("请求"+request.getSession().getId());
        String res=sendEmailServiceImpl.sendEmail(Constant.MY_163EMAIL,email);
        if(!(Constant.CODE_NO).equals(res)){
            HttpSession httpSession=request.getSession();
            //session 5分钟后删除验证码
            //request.getSession().setAttribute(Constant.IDENTITY_CODE, res);  此方法错误
            httpSession.setAttribute(Constant.IDENTITY_CODE, res);
            try {
                //TimerTask实现5分钟后从session中删除验证码Constant.IDENTITY_CODE
                final Timer timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        System.out.println(httpSession.getAttribute(Constant.IDENTITY_CODE));
                        httpSession.removeAttribute(Constant.IDENTITY_CODE);
                        System.out.println("验证码删除成功..");
                        timer.cancel();
                    }}, 15 * 1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return Constant.CODE_YES;
        }
        return Constant.CODE_NO;
    }

这个方法很多地方你不必在意,重要的是http是无状态请求,当响应完成后,返回到前台页面后,request就重置为null了,所以这个时候你需要将你的验证码 存储到session中:

httpSession.setAttribute(Constant.IDENTITY_CODE, res);  这个方法是将验证码存储到session中

super.setResponseOrigin(request,response); 这个方法是我在超类中写的一个方法,设置响应内容的,后面介绍他的作用

httpSession.removeAttribute(Constant.IDENTITY_CODE);  在timerTask中删除之前发送验证码存储在session中的验证码,这一步很重要,因为在没有session缓存数据库redis,或者说在没有做session共享的情况下,如何保证在注册时用户点击注册请求注册方法是的sessionid也是这个sessionid呢?这就要涉及到第二部分了

第二部分

需要在ajax的请求中设置响应的参数,如下

function get_Identity_Code(){
		 var email=$("#r_email").val();
		 $.ajax({
			url:"http://ip:端口/项目名/login/sendIdentityCode",
			type:"GET",
			dataType:"json",
			data:{"email":email},
			//processData: true,    //对象型要指定  序列化  true非序列化
			//contentType : false,   //对象型要指定
			//支持跨域
			async:true,
			timeout : 60000,
			xhrFields: {
                withCredentials: true
            },
            crossDomain: true,
			error:function(error){
				console.log("发送失败error");
			},
//			error: function(XMLHttpRequest, textStatus, errorThrown) {
//                      alert(XMLHttpRequest.status);
//                      alert(XMLHttpRequest.readyState);
//                      alert(textStatus);
//                  },
			success:function(result){	
				if(result=='200'){
			    	$("#r_email").css("color","lawngreen");
			    	console.log("发送成功!");
				}else{
			    	$("#r_email").css("color","red");
			    	$("#r_email").text("邮箱已被注册,你个大傻子");
			    	console.log("发送失败!");
			    	shake(); 	
				}	
			}
	   });

重要的是

            xhrFields: {
                withCredentials: true
            },
            crossDomain: true,

这时你会发现,返回前台数据业也取到可,状态也是200,但是ajax函数却一直进入error函数,参见第三部分

第三部分

这时会办一个错误

The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*'

摘抄了一些解释:来自:https://www.jianshu.com/p/c115e4a24977

当请求的凭据模式为include时,相应中的Access-Control-Allow-Origin标头的值不能是通配符 "*"

如在请求定义中设置withCredentials标志,则会在请求中传递cookie等。
如果服务器返回任何set-cookie响应头, 那么必须返回Access-Control-Allow-Credentials: true, 否则将不会在客户端上创建 cookie
如果你这样设置,你需要同时指定了确切的Access-Control-Allow-Origin响应头,因为Access-Control-Allow-Origin: 不具有凭证兼容

--->当请求中携带cookie时, Access-Control-Allow-Origin必须要有确切的指定, 不能是通配符(*), 而withCredentials是跨域安全策略的一个东西

附加:

也就是说Access-Control-Allow-Credentials设置为true的情况下
Access-Control-Allow-Origin不能设置为*

ps: 关于指定域名 可以在后端用个array类似的存一个白名单域名列表
如果有请求 先判断 Origin 是否在白名单里 然后再动态设置 Access-Control-Allow-Origin

最草根的解决办法,我们最喜欢:

在返回之前,设置response

这就是我的super方法:

/**
     * 解决在没有缓存数据库情况下的session问题
     * @param request
     * @param response
     */
    protected void setResponseOrigin(HttpServletRequest request,HttpServletResponse response){
        response.setHeader("Access-Control-Allow-Credentials", "true");  //传递身份信息
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Methods", "POST,PUT,GET");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers","Origin, X-Requested-With, Content-Type, Accept");
    }

当然这部分内容也可以写在你的过滤器或者拦截器中

你可能感兴趣的:(Java,Error)