最近玩了下使用的websocket技术做消息推送,过程中遇到了session的问题,问题描述如下:
用户登录的时,将用户信息保存到session中,用户主页通过ws协议的方式访问服务器,访问地址:ws://127.0.0.1:8080/webSocketServer",websocket拦截器获取用户seesion的时候,发现session为null。
尝试调试过多次,session获取始终未空,问题出在哪呢?
我们来看一下session的概要:
Session概要
Session 是用于保持状态的基于 Web 服务器的方法,在 Web 服务器上保持用户的状态信息供在任何时间从任何页访问。Session 允许通过将对象存储在 Web 服务器的内存中在整个用户会话过程中保持任何对象。当我们使用用户名和密码登陆网站,系统会首先验证当前登陆用户是否合法,当合法后将用户名等相关信息保存在Session 中。登陆后点击进入某功能页面时,系统也会去判断当前你是否有访问权限,判断的方式是验证Session 中的内容是否正确。
Session是客户端与服务器端建立的会话,总是放在服务器上的,服务器会为每次会话建立一个sessionId,每个客户会跟一个sessionID对应。并不是关闭浏览器就结束了本次会话,通常是用户执行“退出”操作或者会话超时时才会结束。
session代表的是一个回话,在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。
服务器创建session出来后,会把session的id号,以cookie的形式回写给客户机,这样,只要客户机的浏览器不关,再去访问服务器时,都会带着session的id号去,服务器发现客户机浏览器带session id过来了,就会使用内存中与之对应的session为之服务。
来看一下session为null的实际案例:
服务端设置session的代码:
@RequestMapping("")
public String index(HttpServletRequest httpRequest, String nickname) {
HttpSession httpSession = httpRequest.getSession();
System.out.println("###跳转进入到了首页###");
User user = new User();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = formatter.format(new Date());
user.setId(dateString + "-" + nickname);
user.setNickname(nickname);
// 登录操作
// 判断是否是一个已经登录的用户,没有则登录
if (null != httpSession.getAttribute("loginUser")) {
// 清除旧的用户
httpSession.removeAttribute("loginUser");
}
// 将用户放入session
httpSession.setAttribute("loginUser", user);
return "redirect:/index/mainpage";
}
前端sebsocket获取session的代码:
var url = "ws://127.0.0.1:8080/"+ "webSocketServer";
ws = websocket.util.getWs(url);
websocket.util.onopen(ws);
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map attributes) throws Exception {
System.out.println("握手之前····");
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession(false);
//如果用户已经登录,允许聊天
if(session !=null && session.getAttribute("loginUser")!=null){
//获取登录的用户
User loginUser=(User)session.getAttribute("loginUser") ;
//将用户放入socket处理器的会话(WebSocketSession)中
attributes.put("loginUser", loginUser);
System.out.println("Websocket:用户[ID:" + (loginUser.getId() + ",Name:"+loginUser.getNickname()+"]要建立连接"));
}else{
//用户没有登录,拒绝聊天
//握手失败!
System.out.println("--------------握手已失败...");
return false;
}
}
return super.beforeHandshake(request, response, wsHandler, attributes);
}
通过beforeHandShake方法获取session为null,发现使用ip的方式,不是同一个sessionId,得到的session自然为null,改为如下方式进行请求就可以了:
<%
String path = request.getContextPath();
String basePath = request.getServerName() + ":" + request.getServerPort() + path + "/";
String baseUrlPath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
var url = "ws://" + path + "webSocketServer";
ws = websocket.util.getWs(url);
websocket.util.onopen(ws);
目前很多项目采用的是前后台分离的情况,关于前后端分离的session情况,笔人也测试过,目前前后台分离分2种情况部署:
一、在同一个服务器(tomcat)下部署,同源
服务A:服务端代码:
@RequestMapping(value = "test", method = RequestMethod.GET)
@ResponseBody
public String test(HttpServletRequest request, HttpServletResponse httpResponse) {
/*httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8080");
httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
*/
//判断,如果没有session,则跳到登录页面
HttpSession session = request.getSession();
if(null != session.getAttribute("loginUser")){
User user = (User) session.getAttribute("loginUser");
System.out.println("session 不为空");
return "session 不为空 nicknage :" + user.getNickname();
}else{
System.out.println("session ---------------------------- null");
return "session ---------------------------- null";
}
}
客户端A:前端代码:
主界面