前几天遇到个由于经验不足,导致找了很久才找到的问题。就是我在我的电脑虚拟机上编写的java服务端接口,在做登录与检查登录接口时用到了session储存用户数据,当在html放在我的项目里面时,能够很好地获取到session值,但是把html放到前端人员自己的电脑上时session不能获取到对应的值。
网上查询后知道这是前后端页面分离导致的问题。具体的解决办法有很多网友都已经说的很好的了。具体解决可以看这里https://blog.csdn.net/woshiyeguiren/article/details/79194003。
然后打个广告:从刚开始解除java到今天差不多刚好两周了,做了个自己公司用的pbr材质库。感兴趣的朋友可以来看看点击打开链接
2018.10.24补充:
虽然之前刚开始接触java时踩过这个坑,但还是没想到之后遇到相同的问题还会花费这么长时间,针对几种情况的处理方法进行详细的补充下。
跨域问题在web开发中非常常见,我们项目部署的时比如把前端代码放到一个服务器(oss),后端代码放到另一个服务器两者之间没有做任何任何转发以及配置就会有跨域问题。其次由于采用接口方式开发,必定会导致前端代码在前端人员电脑上,后端代码放在后端人员的电脑上。这样一来也会导致跨域问题。但是在做接口对接时,让前端人员直接访问到后端人员的电脑,存在问题可以一目了然的反应给双方人员,直接修改这样的方式应该是非常快速的开发流程。所以我们不考虑部署到线上服务器是否会存在跨域问题,我们都需要解决第二种情况导致的跨域问题,以及session丢失问题。
①:后台中,我们编写一个统一的请求返回接口,对header进行设置。再返回请求数据时统一调用
// 返回json字符串数据
void post_jsondata(HttpServletRequest request, HttpServletResponse response, JSONObject output)
throws ServletException, IOException {
response.setContentType("text/html");
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");//是否支持跨域
response.setHeader("Access-Control-Allow-Methods", "GET,POST");
PrintWriter out = response.getWriter();
out.println(output.toString());
out.flush();
out.close();
// super.doPost(request, response);
}
②:前端页面代码里我们在ajax请求里面带上 xhrFields: {withCredentials: true}这个属性,表示提供cookie信息
我们在桌面建立一个html文件来访问我们的后端java代码,模拟前后页面分离的情况,就会产生session丢失的问题。
我在遇到这个问题整过修改过程是这样的:
①:不做任何设置时使用ajax直接访问,报错403 Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 403.很明显是说的跨域了,后端拒绝了请求。
②:我们针对跨域问题进行解决,在后端contorller加上注解“@CrossOrigin(origins = "*", maxAge = 3600)”,允许所有请求,这样可以便可以访问到后端方法。
③:但是新问题出现了,我们每一次请求,只要后台调用了request.getSession()(或者springmvc中的直接把session当做参数传递进来的)都会产生一个新的session。
④:我们原生java写法时,前端ajax带上了 xhrFields: {withCredentials: true}这个属性,同样把它加上。一测试报错了,大致还是报错跨域的问题。emmmmm上面不是已经允许跨域了么,这又是什么鬼。查询了资料后,大致是说“@CrossOrigin(origins = "*", maxAge = 3600)”这个注解只适用于get请求。
⑤:我们原生的java都能解决,那换上框架肯定也是可以的。查找资料后发现,只是我们需要的是每一次对response进行header设置。根据网上说的我们可以对每次请求进行拦截,在进入contorller之前进行设置,那么问题肯定就解决了。
具体写法:①新建一个拦截器类,拦截每次请求设置response的header。②:在webxml里面注册这个拦截器 ③:注意前端ajax加上“xhrFields: {withCredentials: true}”
////////////////拦截器类///////////////////
package com.xunshi.TimerEvent;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author liangpeng
* @ClassName: AuthorityInterceptor拦截器,拦截每次请求
* @Description: 前后端分离导致session不一致问题
* @date 2018年10月18日
*/
public class AuthorityInterceptor implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setContentType("textml;charset=UTF-8");
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "0");
response.setHeader("Access-Control-Allow-Headers",
"Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("XDomainRequestAllowed", "1");
chain.doFilter(req, res);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
///////////////////webxml配置,添加注册///////////////////////////////////////
AuthorityInterceptor
com.xunshi.TimerEvent.AuthorityInterceptor
AuthorityInterceptor
/*
以上两种是针对基本的ajax请求,针对微信小程序由于小程序不能使用ajax进行http的请求,所以微信小程序的跨域session问题处理有点不同。
具体做法是:①后端再第一次小程序访问后返回给小程序sessionid(最好加密),其它不用配置什么。②:小程序每次请求带上sessionid,java后台会自动根据sessionid判断是哪一个客户端,找到对应的session。
wx.request({
url: getApp().globalData.url + 'User/ShowOneCar/' + res.data.car_id,
header: { "Cookie": "JSESSIONID=" + wx.getStorageSync("sessionID") },//带上这句话,sessionID是登陆后后端返回存在缓存里面的
success: function (rr) {
console.log(rr);
if (rr.data.state) {
that.setData({
carinfo: rr.data.thiscar
});
}
}
})
针对小程序的session问题后台不需要建立拦截器进行设置,ajax确不行有点迷糊。
不过用到session来储存或多或少会有很多问题,安全性以及服务器压力都存在问题。但目前还不知道有什么更加简便的方式来储存用户登录状态,先用着。