zoukankan      html  css  js  c++  java
  • 在tomcat集群下利用redis实现单点登陆

    场景:比如说我们要实现一个集群环境,无非是把多个项目部署到多个tomcat下,然后按照一定的算法,轮询什么的随机访问多个tomcat服务器,但是问题也会有许多,比如说,我们最开始是把登陆人的信息存放到session中,但是如果是集群的情况下,比如我第一次登陆,把信息存放到session里面,但是我第二次访问的时候,访问到了第二台服务器,第二台服务器里面没有session信息,我们还得再登陆一遍,问题显而易见,session数据共享的问题。

    解决思路:我们不要把信息存放到服务器的session中,要存放到一个公共的容器中,这个容器我们采用redis,然后我们每次取用户信息都去redis里面取,这不就解决了。问题又出现了,我们知道redis是key,value的数据结构,value是json格式化的用户对象信息,key呢?key当然要不一样了,因为许多登陆用户的的信息都会存在redis里面。别忘了,每个session都会有一个sessionId,这个sessionId是服务器给我们自动生成的。最开始我们都是用的这个,而且cookie每次访问服务器也会携带这个sessionId,这不是解决了?我们用sessionId来当key,User来当value,然后把这个sessionId写到本地的cookie中,每次访问服务器都从本地的cookie中取得这个sessionId,然后有了key,不就得到value了么。

    需要注意的是,既然是单点登陆,顾名思义就是我登陆了这个一级域名下的服务器,然后再登陆它下面的二级域名,三级域名都不需要登陆,那这个cookie存的时候也需要讲究一下,需要把这个cooike存放到顶级域名下,他下面的子域名也就能得到这个cookie了。比如说两个域名

    a.lzh.com和b.lzh.com  要实现单点登陆,就要把cookie存放到  .lzh.com  下(注意前面的点),ok。思路有了。看代码吧!!!

    代码中有许多工具类,估计提供的不全,不过如果你看懂了我上面说的,你也应该知道大概具体啥意思。因为如果把每个包含的代码都贴出来,真的是好多啊。重点是理解。我其他博客也有相关的工具类。自己找找嘛。

    补充:还有每次访问服务器都要把reidis的时间重新设置半小时。这个利用过滤器来实现。很简单。

    CookieUtil.java

    package com.mmall.util;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang.StringUtils;
    
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * Created by 敲代码的卡卡罗特
     */
    @Slf4j
    public class CookieUtil {
    
        private final static String COOKIE_DOMAIN = ".happymmall.com";   //顶级域名
        private final static String COOKIE_NAME = "mmall_login_token";   //存放在本地cookie的key
    
        //根据request从cookie中读取token
        public static String readLoginToken(HttpServletRequest request){
            Cookie[] cks = request.getCookies();
            if(cks != null){
                for(Cookie ck : cks){
                    log.info("read cookieName:{},cookieValue:{}",ck.getName(),ck.getValue());
                    if(StringUtils.equals(ck.getName(),COOKIE_NAME)){
                        log.info("return cookieName:{},cookieValue:{}",ck.getName(),ck.getValue());
                        return ck.getValue();
                    }
                }
            }
            return null;
        }
    
        //X:domain=".happymmall.com"
        //a:A.happymmall.com            cookie:domain=A.happymmall.com;path="/"
        //b:B.happymmall.com            cookie:domain=B.happymmall.com;path="/"
        //c:A.happymmall.com/test/cc    cookie:domain=A.happymmall.com;path="/test/cc"
        //d:A.happymmall.com/test/dd    cookie:domain=A.happymmall.com;path="/test/dd"
        //e:A.happymmall.com/test       cookie:domain=A.happymmall.com;path="/test"
        
        
        //写入cookie
        public static void writeLoginToken(HttpServletResponse response,String token){
            Cookie ck = new Cookie(COOKIE_NAME,token);
            ck.setDomain(COOKIE_DOMAIN);
            ck.setPath("/");//代表设置在根目录
            ck.setHttpOnly(true);
            //单位是秒。
            //如果这个maxage不设置的话,cookie就不会写入硬盘,而是写在内存。只在当前页面有效。
            ck.setMaxAge(60 * 60 * 24 * 365);//如果是-1,代表永久
            log.info("write cookieName:{},cookieValue:{}",ck.getName(),ck.getValue());
            response.addCookie(ck);
        }
    
        //删除cookie
        public static void delLoginToken(HttpServletRequest request,HttpServletResponse response){
            Cookie[] cks = request.getCookies();
            if(cks != null){
                for(Cookie ck : cks){
                    if(StringUtils.equals(ck.getName(),COOKIE_NAME)){
                        ck.setDomain(COOKIE_DOMAIN);
                        ck.setPath("/");
                        ck.setMaxAge(0);//设置成0,代表删除此cookie。
                        log.info("del cookieName:{},cookieValue:{}",ck.getName(),ck.getValue());
                        response.addCookie(ck);
                        return;
                    }
                }
            }
        }
    
    
    
    
    
    
    
    }
    View Code

    UserController.java (用户登陆。退出方法)

    /**
         * 用户登录
         * @param username
         * @param password
         * @param session
         * @return
         */
        @RequestMapping(value = "login.do",method = RequestMethod.POST)
        @ResponseBody
        public ServerResponse<User> login(String username, String password, HttpSession session, HttpServletResponse httpServletResponse){
            ServerResponse<User> response = iUserService.login(username,password);
            if(response.isSuccess()){
    
    //            session.setAttribute(Const.CURRENT_USER,response.getData());
                CookieUtil.writeLoginToken(httpServletResponse,session.getId());
                RedisShardedPoolUtil.setEx(session.getId(), JsonUtil.obj2String(response.getData()),Const.RedisCacheExtime.REDIS_SESSION_EXTIME);
    
            }
            return response;
        }
        
        
         @RequestMapping(value = "logout.do",method = RequestMethod.GET)
        @ResponseBody
        public ServerResponse<String> logout(HttpSession session,HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse){
            String loginToken = CookieUtil.readLoginToken(httpServletRequest);
            CookieUtil.delLoginToken(httpServletRequest,httpServletResponse);
            RedisShardedPoolUtil.del(loginToken);
    
    //        session.removeAttribute(Const.CURRENT_USER);
    
            return ServerResponse.createBySuccess();
        }
    View Code

    SessionExpireFilter.java  (过滤器,每次访问服务器把redis的时间重置30分钟)

    package com.mmall.controller.common;
    
    
    import com.mmall.common.Const;
    import com.mmall.pojo.User;
    import com.mmall.util.CookieUtil;
    import com.mmall.util.JsonUtil;
    import com.mmall.util.RedisShardedPoolUtil;
    import org.apache.commons.lang.StringUtils;
    
    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 java.io.IOException;
    
    /**
     * Created by 敲代码的卡卡罗特
     */
    public class SessionExpireFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;
    
            String loginToken = CookieUtil.readLoginToken(httpServletRequest);
    
            if(StringUtils.isNotEmpty(loginToken)){
                //判断logintoken是否为空或者"";
                //如果不为空的话,符合条件,继续拿user信息
    
                String userJsonStr = RedisShardedPoolUtil.get(loginToken);
                User user = JsonUtil.string2Obj(userJsonStr,User.class);
                if(user != null){
                    //如果user不为空,则重置session的时间,即调用expire命令
                    RedisShardedPoolUtil.expire(loginToken, Const.RedisCacheExtime.REDIS_SESSION_EXTIME);
                }
            }
            filterChain.doFilter(servletRequest,servletResponse);
        }
    
    
        @Override
        public void destroy() {
    
        }
    }
    View Code

    ok,代码省略许多工具类和细节,主要是思想。希望大家明白,我讲的够清楚了。

  • 相关阅读:
    devise 异步发邮件
    ubuntutweak for lucid
    960gs blueprint
    Amoeba for mysql 0.31发布(读写分离、负载均衡、Failover、数据切分)
    Google App Servlet容器转型 – 从Tomcat到Jetty
    DBeaver
    用simple from暂不用formtastic
    [SQL Server]ORDER BY的问题
    PHP pathinfo() 函数
    php中使用head进行二进制流输出,让用户下载PDF等附件的方法
  • 原文地址:https://www.cnblogs.com/coder-lzh/p/8732919.html
Copyright © 2011-2022 走看看