关于集群状况下session共享问题

关于集群状态下session共享问题解决

问题: 集群状态下,登录时,如果使用传统的方式将session记录到本地,那么当访问其他服务器时,无法获取到session,又需要进行登录。
解决办法:使用redis解决,将 session保存在redis服务器(key,value格式保存),用户登录时,服务器都去redis中进行查询session

这里写图片描述

redis保存session的存储格式

本地session的过程和格式:
    Request.getSession.getAttr(Constants.USER_NAME);
    第一步: cookie 中 JESSIONID 
            K : JESSIONID
            V :  Session
    而 session: 格式: K : V
           K:  Constants.USER_NAME
           V:  用户对象
           K:  验证码:名称
           V: 验证码值
在redis中以 K,V的格式进行存储,因为key唯一,所以通过拼接。
    用户名:
    K : JESSIONID:Contants.USER_NAME
    V : 用户名或用户对象(用户对象需要转json字符串)            
    验证码:
    K: JESSIONID:Contants.CODE_NAME
    V: 验证码的值

redis保存session的过期时间设置

这里写图片描述

浏览器访问不同服务器时

问题当用户进行请求时,经过负载均衡,传到 tomcat01,从本地查找session,如果找不到request请求就创建一个JSESSIONID。当用户下次请求tomcat02,从本地查找session,还是找不到,则request请求又创建一个JSESSIONID,会覆盖之前的JSESSIONID。

解决办法: 因为JSESSIONID会被request请求使用,所有我们自定义一个CSESSIONID(随意起名)

第一次访问时: 自己创建一个CSESSIONID(模拟JSESSIONID的创建) UUID 36 去掉 - 32位 然后保存在cookie中,第二次访问从cookie中取出CSESSIONID。

代码实现:SessionProvider.java

保存用户名或验证码到Redis中,实现session共享
注意事项: session的有效时间,是从最后一次请求开始计算时间。

package cn.jay.core.service.user;

import org.springframework.beans.factory.annotation.Autowired;

import cn.jay.common.web.Constants;
import redis.clients.jedis.Jedis;

/**
 * 保存用户名或验证码到Redis中
 * Session共享
 * @author lx
 *
 */
public class SessionProviderImpl implements SessionProvider{

    @Autowired
    private Jedis jedis;
    private Integer exp = 30;// 可以修改session的有效时间
    public void setExp(Integer exp) {
        this.exp = exp;
    }

    @Override
    public void setAttribuerForUsername(String name, String value) {
        // TODO Auto-generated method stub
        //保存用户名到Redis中
        // K : CSESSIONID:Constants.USER_NAME   == name
        jedis.set(name + ":" +  Constants.USER_NAME, value);
        //时间 
        jedis.expire(name + ":" +  Constants.USER_NAME, 60*exp);
    }

    @Override
    public String getAttributeForUsername(String name) {
        // TODO Auto-generated method stub
        //fbb2016
        String value = jedis.get(name + ":" +  Constants.USER_NAME);
        if(null != value){
            //时间  这里就是每次请求重置有效时间
            jedis.expire(name + ":" +  Constants.USER_NAME, 60*exp);
        }
        return value;
    }

}
手动实例化: 目的在配置文件中手动设置 session有效时间
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:task="http://www.springframework.org/schema/task"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd 
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-4.0.xsd 
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-4.0.xsd 
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://www.springframework.org/schema/task
        http://www.springframework.org/schema/task/spring-task-4.0.xsd
        http://code.alibabatech.com/schema/dubbo        
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- Session提供类实例化 : 可以在配置文件中修改session的默认有效时间 -->
    <bean id="sessionProvider" class="cn.itcast.core.service.user.SessionProviderImpl">
        <!-- 设置Session时间  默认30分钟 -->
        <property name="exp" value="60"/>
    </bean> 
</beans>
创建RequestUtil工具类,获取、创建 CSESSIONID
package cn.jay.common.utils;

import java.util.UUID;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 获取CSESSIONID
 * @author lx
 *
 */
public class RequestUtils {

    //获取
    public static String  getCSESSIONID(HttpServletRequest request,HttpServletResponse response){
        //1:取出Cookie
        Cookie[] cookies = request.getCookies();
        if(null != cookies && cookies.length > 0){
            for (Cookie cookie : cookies) {
                //2: 判断COokie中是否有CSESSIONID
                if("CSESSIONID".equals(cookie.getName())){
                    //3:有  直接使用
                    return cookie.getValue();
                }
            }
        }
        //4:没有  创建一个CSESSIONID   并保存到COOKIE中  同时 把此COOKIe写回浏览器  使用此生成的CSESSIONID 
        String csessionid = UUID.randomUUID().toString().replaceAll("-", "");
        Cookie cookie = new Cookie("CSESSIONID",csessionid);
        //设置 cookie存活时间      -1  0   >0  
        // -1  关闭浏览器后销毁  0 立刻销毁   >0 这个时间结束后销毁
        cookie.setMaxAge(-1);
        //设置路径
        cookie.setPath("/");
        //设置跨域  这是京东域名:   www.jd.com search.jd.com  item.jd.com
        //cookie.setDomain(".jd.com"); // 括号中根跨域的规则
        response.addCookie(cookie);
        return csessionid;

    }
}
登录代码中使用 – 登录成功后保存到redis服务器。并返回到之前的页面:
@Autowired
    private BuyerService buyerService;
    @Autowired
    private SessionProvider sessionProvider;
    //提交登陆 
    @RequestMapping(value = "/login.aspx",method=RequestMethod.POST)
    public String login(String username,String password,String returnUrl,
            HttpServletRequest request,HttpServletResponse response, Model model){
        //1:用户名不能为空
        if(null != username){
            //2:密码不能为空
            if(null != password){
                //3:用户名必须正确
                Buyer buyer = buyerService.selectBuyerByUsername(username);
                if(null != buyer){
                    //4:密码必须正确
                    if(buyer.getPassword().equals(encodePassword(password))){
                        //5:保存用户名到Session中(Redis中)  
                        sessionProvider.setAttribuerForUsername(RequestUtils.getCSESSIONID(request, response), buyer.getUsername());
                        //6:跳转到之前访问页面
                        return "redirect:" + returnUrl;
                    }else{
                        model.addAttribute("error", "密码必须正确");
                    }
                }else{
                    model.addAttribute("error", "用户名必须正确");
                }
            }else{
                model.addAttribute("error", "密码不能为空");
            }
        }else{
            model.addAttribute("error", "用户名不能为空");
        }
        return "login";
    }
登录时传的URL

这里对后面的url进行了编码,处理遇到中文是无法传参和中文乱码问题
这里写图片描述
做法就像jd这样,登录是记录之前正在浏览的页面url,就是登录请求后面拼接原来页面的请求。
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值