1、为什么要session共享
分布式开发项目中,用户通过浏览器登录商城,实际上会被转发到不同的服务器,当用户登录进入服务器A,session保存了用户的信息,用户再次点击页面被转发到服务器B,这时问题来了,服务器B没有该用户的session信息,无法验证通过,用户被踢回到登录页面,这样体验效果非常不好,甚至无法验证用户,购物车里面商品都不存在了。
2、利用redis解决方案
用户第一次进入商城首页,给一个CSESSIONID,(不用JSESSIONID的原因),用户添加商品,各种需要记录的操作,都与这个CSESSIONID关联起来;
当使用登录操作时候,将这个用户的信息,如用户名等存入到redis中,通过K_V,将CSESSIONID加一个标志作为key,将用户信息作为value;
当用户点击页面被转发到其他服务器时候,在需要验证是否同一个用户时,就可以从redis中取出value,进行验证用户信息,实现共享。
3、具体案例及实现java代码
1、用户登录时候将csessionid存到redis
-
@RequestMapping(value="/login.aspx",method=RequestMethod.POST)
-
public String login(String username, String password, String returnUrl, Model model
-
, HttpServletRequest request, HttpServletResponse response) {
-
// 判断用户名不能为空
-
if (StringUtils.isNotBlank(username)) {
-
// 判断密码不为空
-
if (StringUtils.isNotBlank(password)) {
-
// 用户名必须正确nu
-
Buyer buyer = buyerService.selectBuyerByusername(username);
-
if (null !=buyer) {
-
// 密码必须正确
-
if (encodePassword(password).equals(buyer.getPassword())) {
-
// 保存用户名到session中 保存用户名到redis key == jsessionid
-
//注意不能用jsessionid,他被request操作,用csession
-
String csessionid = RequestUtils.getCSESSIONID(request, response);
-
sessionProvider.setAttribute(csessionid, buyer.getUsername());
-
// 回跳到之前的访问页面
-
return "redirect:" + returnUrl;
-
}else {
-
model.addAttribute("error", "密码必须正确");
-
}
-
}else {
-
model.addAttribute("error", "用户名必须正确");
-
}
-
} else {
-
model.addAttribute("error", "密码不能为空");
-
}
-
}else {
-
model.addAttribute("error", "用户名不能为空");
-
}
-
return"login";
-
}
-
/**
-
* 获取请求中的csessionid,如果有直接使用,
-
* 没有的话创建一个csessionid,并保存到response,添加到浏览器
-
* @param request
-
* @param response 用于最后将cookie存到浏览器,所以需要response
-
* @return
-
*/
-
public static String getCSESSIONID(HttpServletRequest request,HttpServletResponse response) {
-
// 1、获取Cookies (里面有jsession 购物车。。。)
-
Cookie[] cookies = request.getCookies();
-
if (null !=cookies && cookies.length>0) {
-
for (Cookie cookie : cookies) {
-
// 2、从 Cookie中获取CSESSIONID
-
if (cookie.getName().equals("CSESSIONID")) {
-
// 3、如果有 直接使用
-
return cookie.getValue();
-
}
-
}
-
}
-
// 4、判断如果没有,创建一个CSESSIONID 保存CESSIONID到cookie,保存Cookie写回浏览器
-
String csessionid = UUID.randomUUID().toString().replaceAll("-", "");
-
Cookie cookie = new Cookie("CSESSIONID", csessionid);
-
//设置路径
-
cookie.setPath("/");
-
//设置Cookie的存活时间 立即销毁0 关闭浏览器销毁-1 到时间了再消失>0 前提:没有清理Cookie
-
cookie.setMaxAge(-1);
-
response.addCookie(cookie);
-
return csessionid;
-
}
2、写一个SessionProviderImpl类,将CSESSIONID信息存入redis
-
/**
-
* 远程Session 保存到Redis中
-
* @author Administrator
-
*
-
*/
-
//@Service("sessionProvider") 实现类的实例化交给spring管理,可以设置存活时间session.xml
-
public class SessionProviderImpl implements SessionProvider{
-
-
@Autowired
-
private Jedis jedis;
-
-
/**
-
* 保存用户到session
-
*/
-
@Override
-
public void setAttribute(String key, String value) {
-
// 保存用户名到redis key == jsessionid
-
//保存
-
jedis.set(key + ":" + Constants.BUYER_SESSION, value);
-
// 时间30分钟
-
jedis.expire(key + ":" + Constants.BUYER_SESSION, Constants.SESSION_TIME);
-
-
}
-
-
/**
-
* 获取用户名
-
*/
-
@Override
-
public String getAttribute(String key) {
-
String username = jedis.get(key + ":" + Constants.BUYER_SESSION);
-
if (null !=username) {
-
//不等于null,再设置一次session存活时间,保证session生存时间从最后一次登录计算
-
jedis.expire(key + ":" + Constants.BUYER_SESSION, Constants.SESSION_TIME);
-
return username;
-
}
-
return null;
-
}
-
-
// 保存验证码到session
-
// 获取验证码
-
-
/**
-
* 退出登录 让redis中session消失
-
* @param key
-
*/
-
public void logout(String key) {
-
jedis.del(key + ":" + Constants.BUYER_SESSION);
-
}
-
-
}
-
<!--session的实例化,不通过注解@service("sessionProvider")实现,改为交给spring管理 -->
-
<beanid="sessionProvider"class="cn.dapeng.core.service.user.SessionProviderImpl">
-
<!-- <property name="exp" value="1800"/> -->
-
</bean>
3、验证用户只需要通过CSESSIONID去查询用户的信息,比如验证登录,验证购物车。。
-
/**
-
* 判断是否登录,返回登录标志0,未登录;1,已登录
-
* @param request
-
* @param response
-
*/
-
@RequestMapping(value="/isLogin.aspx")
-
@ResponseBody
-
public MappingJacksonValue isLogin(String callback, HttpServletRequest request , HttpServletResponse response) {
-
Integer result = 0; //默认0,未登录
-
String username = sessionProvider.getAttribute(RequestUtils.getCSESSIONID(request, response));
-
if (null !=username) {
-
result = 1;
-
}
-
/*// json 的返回方法,但是受到跨域问题,因此改为jsonp
-
response.setContentType("application/json;charset=UTF-8");
-
try {
-
response.getWriter().write(result);
-
} catch (IOException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}*/
-
//返回jsonp,callback里接收ajax传过来的MappingJacksonValue
-
MappingJacksonValue mjv = new MappingJacksonValue(result);
-
mjv.setJsonpFunction(callback);
-
return mjv;
-
}
-
<li class="tool-item tool-active is-like "><a href='javascript:void(0)'>
1、为什么要session共享
分布式开发项目中,用户通过浏览器登录商城,实际上会被转发到不同的服务器,当用户登录进入服务器A,session保存了用户的信息,用户再次点击页面被转发到服务器B,这时问题来了,服务器B没有该用户的session信息,无法验证通过,用户被踢回到登录页面,这样体验效果非常不好,甚至无法验证用户,购物车里面商品都不存在了。
2、利用redis解决方案
用户第一次进入商城首页,给一个CSESSIONID,(不用JSESSIONID的原因),用户添加商品,各种需要记录的操作,都与这个CSESSIONID关联起来;
当使用登录操作时候,将这个用户的信息,如用户名等存入到redis中,通过K_V,将CSESSIONID加一个标志作为key,将用户信息作为value;
当用户点击页面被转发到其他服务器时候,在需要验证是否同一个用户时,就可以从redis中取出value,进行验证用户信息,实现共享。
3、具体案例及实现java代码
1、用户登录时候将csessionid存到redis
-
@RequestMapping(value="/login.aspx",method=RequestMethod.POST)
-
public String login(String username, String password, String returnUrl, Model model
-
, HttpServletRequest request, HttpServletResponse response) {
-
// 判断用户名不能为空
-
if (StringUtils.isNotBlank(username)) {
-
// 判断密码不为空
-
if (StringUtils.isNotBlank(password)) {
-
// 用户名必须正确nu
-
Buyer buyer = buyerService.selectBuyerByusername(username);
-
if (null !=buyer) {
-
// 密码必须正确
-
if (encodePassword(password).equals(buyer.getPassword())) {
-
// 保存用户名到session中 保存用户名到redis key == jsessionid
-
//注意不能用jsessionid,他被request操作,用csession
-
String csessionid = RequestUtils.getCSESSIONID(request, response);
-
sessionProvider.setAttribute(csessionid, buyer.getUsername());
-
// 回跳到之前的访问页面
-
return "redirect:" + returnUrl;
-
}else {
-
model.addAttribute("error", "密码必须正确");
-
}
-
}else {
-
model.addAttribute("error", "用户名必须正确");
-
}
-
} else {
-
model.addAttribute("error", "密码不能为空");
-
}
-
}else {
-
model.addAttribute("error", "用户名不能为空");
-
}
-
return"login";
-
}
-
/**
-
* 获取请求中的csessionid,如果有直接使用,
-
* 没有的话创建一个csessionid,并保存到response,添加到浏览器
-
* @param request
-
* @param response 用于最后将cookie存到浏览器,所以需要response
-
* @return
-
*/
-
public static String getCSESSIONID(HttpServletRequest request,HttpServletResponse response) {
-
// 1、获取Cookies (里面有jsession 购物车。。。)
-
Cookie[] cookies = request.getCookies();
-
if (null !=cookies && cookies.length>0) {
-
for (Cookie cookie : cookies) {
-
// 2、从 Cookie中获取CSESSIONID
-
if (cookie.getName().equals("CSESSIONID")) {
-
// 3、如果有 直接使用
-
return cookie.getValue();
-
}
-
}
-
}
-
// 4、判断如果没有,创建一个CSESSIONID 保存CESSIONID到cookie,保存Cookie写回浏览器
-
String csessionid = UUID.randomUUID().toString().replaceAll("-", "");
-
Cookie cookie = new Cookie("CSESSIONID", csessionid);
-
//设置路径
-
cookie.setPath("/");
-
//设置Cookie的存活时间 立即销毁0 关闭浏览器销毁-1 到时间了再消失>0 前提:没有清理Cookie
-
cookie.setMaxAge(-1);
-
response.addCookie(cookie);
-
return csessionid;
-
}
2、写一个SessionProviderImpl类,将CSESSIONID信息存入redis
-
/**
-
* 远程Session 保存到Redis中
-
* @author Administrator
-
*
-
*/
-
//@Service("sessionProvider") 实现类的实例化交给spring管理,可以设置存活时间session.xml
-
public class SessionProviderImpl implements SessionProvider{
-
-
@Autowired
-
private Jedis jedis;
-
-
/**
-
* 保存用户到session
-
*/
-
@Override
-
public void setAttribute(String key, String value) {
-
// 保存用户名到redis key == jsessionid
-
//保存
-
jedis.set(key + ":" + Constants.BUYER_SESSION, value);
-
// 时间30分钟
-
jedis.expire(key + ":" + Constants.BUYER_SESSION, Constants.SESSION_TIME);
-
-
}
-
-
/**
-
* 获取用户名
-
*/
-
@Override
-
public String getAttribute(String key) {
-
String username = jedis.get(key + ":" + Constants.BUYER_SESSION);
-
if (null !=username) {
-
//不等于null,再设置一次session存活时间,保证session生存时间从最后一次登录计算
-
jedis.expire(key + ":" + Constants.BUYER_SESSION, Constants.SESSION_TIME);
-
return username;
-
}
-
return null;
-
}
-
-
// 保存验证码到session
-
// 获取验证码
-
-
/**
-
* 退出登录 让redis中session消失
-
* @param key
-
*/
-
public void logout(String key) {
-
jedis.del(key + ":" + Constants.BUYER_SESSION);
-
}
-
-
}
-
<!--session的实例化,不通过注解@service("sessionProvider")实现,改为交给spring管理 -->
-
<beanid="sessionProvider"class="cn.dapeng.core.service.user.SessionProviderImpl">
-
<!-- <property name="exp" value="1800"/> -->
-
</bean>
3、验证用户只需要通过CSESSIONID去查询用户的信息,比如验证登录,验证购物车。。
-
/**
-
* 判断是否登录,返回登录标志0,未登录;1,已登录
-
* @param request
-
* @param response
-
*/
-
@RequestMapping(value="/isLogin.aspx")
-
@ResponseBody
-
public MappingJacksonValue isLogin(String callback, HttpServletRequest request , HttpServletResponse response) {
-
Integer result = 0; //默认0,未登录
-
String username = sessionProvider.getAttribute(RequestUtils.getCSESSIONID(request, response));
-
if (null !=username) {
-
result = 1;
-
}
-
/*// json 的返回方法,但是受到跨域问题,因此改为jsonp
-
response.setContentType("application/json;charset=UTF-8");
-
try {
-
response.getWriter().write(result);
-
} catch (IOException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}*/
-
//返回jsonp,callback里接收ajax传过来的MappingJacksonValue
-
MappingJacksonValue mjv = new MappingJacksonValue(result);
-
mjv.setJsonpFunction(callback);
-
return mjv;
-
}
-
<li class="tool-item tool-active is-like "><a href='javascript:void(0)'>