前言
问题
//获取SessionId
String sessionId = request.getSession().getId();
//获取验证码
String validCode = getValidCode();
//以SessionId作为Key,验证码作为值,存到Redis上
RedisHelper.set(sessionId,validCode);
校验验证码
public boolean validCode(String code){
String sessionId = request.getSession().getId();
String codeValue = RedisHelper.get(sessionId);
return codeValue.equals(code);
}
一切都是那么简单,那么容易。在本地测试完成,没任何问题,好!部署上线。
环境不同,问题就产生了。
生产环境是多台服务器的,用Nginx转发。如果每次请求如果没法在同一台服务器上,SessionId就会变来变去。SessionId的生成流程如下:
@Controller
@RequestMapping("home")
public class HomeController {
@ResponseBody
@GetMapping("getsessionid")
public String getSessionId(HttpServletRequest request, HttpServletResponse response)
{
String sessionId = request.getSession().getId();
return sessionId;
}
}
第一次请求:
第二次请求:
Request类内的getSession(bool create)
我们看一下doGetSession(create); 我们一般看源码,对doxxx类似的方法要特别留意一下,能做事的,一般都在这个方法里头,其他的一般都是准备工作。这里是一般情况,也不一定准确
我们再看看这里是哪里调用到,打个断点。查看一下调用栈,看看往上的调用类与方法。
从这里可以看出SessionId是从Cookies来的。并且cookies名称为:JSESSIONID。而这个Id是可以通过request.getRequestedSessionId()得到。
通过request.getRequestedSessionId()得到客户端传过来的SessionId,就不必要再从服务端生成一个SessionId了。这样即使用用户第二次请求打不到第一次请求的服务器上,也没有关系,因为我已经有了Sessionid了,不需要再次生成一个SessionId。
通过整理,我们可以写一个拿SessionId的方法。代码如下:
/**
* 获取sessionId
* @param request 请求
* @param response 响应
* @return sessionId
*/
public static String getSessionId(HttpServletRequest request, HttpServletResponse response) {
// request为空返回null
if (Objects.isNull(request) || Objects.isNull(response)){
return null;
}
// 优先取请求里的JSSESSIONID
String sessionId = request.getRequestedSessionId();
// 如果为空,需要新建一个
if (StringHelper.isNullOrEmpty(sessionId)){
HttpSession httpSession = request.getSession();
// 为空返回null
if (Objects.isNull(httpSession)){
return null;
}
sessionId = httpSession.getId();
}
return sessionId;
}
其实这个问题的解决思路还是比较简单明了的,有些人可能通过度娘,找一下,找到request.getRequestedSessionId()来得到Id就可以解决问题了,但深挖问题的根源,session是怎样产生的,request.getRequestedSessionId()与request.getSession().getId()有什么区别?在生成session之前,服务器还做了哪些事情,这些等等,都是看了代码才知道的。找问题不找根源就不知道原理