在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;
                }
            }
        }
    }

}

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();
    }

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() {

    }
}

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

原文地址:https://www.cnblogs.com/coder-lzh/p/8732919.html

时间: 2024-08-01 08:03:41

在tomcat集群下利用redis实现单点登陆的相关文章

用redis实现tomcat集群下的session共享

上篇实现了 LINUX中NGINX反向代理下的TOMCAT集群(http://www.cnblogs.com/yuanjava/p/6850764.html) 这次我们在上篇的基础上实现session 共享问题 Nginx机器:192.168.1.108 两台tomcat机器分别是:192.168.1.168 192.168.1.178 一:测试session共享问题 在原index.jsp页面添加如下代码 SessionId:<%= session.getId() %> <% Stri

集群下使用redis统一session处理

pom依赖(快照版): <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session</artifactId> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>

利用Redis发布订阅完成tomcat集群下的消息通知

博主是刚入职半年的新手,如果有说的不对的地方请各位大佬见谅! 这是博主的第一篇博客,可能排版以及一些描述有不合理的地方还请勿喷,希望大家尽可能的多给我这样的新手一些鼓励让我能在写博客的道路上走下去. 进入正题,首先开发背景 近期公司的一些项目上出现了内存溢出的问题,究其原因是缓存的数据量太大导致jvm内存溢出,产品的架构上比较老所以针对缓存这块,领导叫我去重构移植到Redis中,博主之前并没有学习过Redis以及关于分布式系统的并发问题,所以也是对我的一次挑战,还好没有辜负领导的期望在期望时间之

Tomcat集群环境下session共享方案梳理(1)-通过memcached(MSM)方法实现

对于web应用集群的技术实现而言,最大的难点就是:如何能在集群中的多个节点之间保持数据的一致性,会话(Session)信息是这些数据中最重要的一块.要实现这一点, 大体上有两种方式: 一种是把所有Session数据放到一台服务器上或者数据库中,集群中的所有节点通过访问这台Session服务器来获取数据: 另一种就是在集群中的所有节点间进行Session数据的同步拷贝,任何一个节点均保存了所有的Session数据. Tomcat集群session同步方案有以下几种方式: 1)使用tomcat自带的

高性能站点架构之负载均衡 Nginx+tomcat+redis实现tomcat集群

上一篇文章给大家讲了Nginx的安装,那么这篇文章为大家讲一下Nginx+Tomcat实现负载均衡. 先说说为什么要用ngnix 做负载均衡.事实上做负载均衡的最出名的莫过于F5了.F5是在硬件设施,动辄几万,几十万,几百万不等,对于一般的小公司来说,这也是一笔非常大的开销.所以能尽量的使用软件.还是使用软件,效果上尽管会差一些,可是还是可以起到一定的作用的. 环境准备 三台装有centos6.5 系统的机器.当中两台机器上装有tomcat7,一台机器上装有nginx3.0.2,至于详细怎样安装

Centos下Apache+Tomcat集群--搭建记录

一.目的 利用apache的mod_jk模块,实现tomcat集群服务器的负载均衡以及会话复制,这里用到了<Cluster>. 二.环境 1.基础:3台主机,系统Centos6.5,4G内存,50G硬盘. yum源已更换为阿里源(如何更换可参考博客的另外一篇文章) 2.软件:development tools,jdk-7u9-linux-x64.rpm;源码编译软件apr-util-1.5.4.tar.gz,apr-1.5.2.tar.gz,tomcat-connectors-1.2.40-s

Tomcat集群+Nginx+Redis服务搭建

由于公司新业务突然上来了,单个Tomcat实例已经不能满足业务发展的需要了,只能通过搭建集群来解决问题了.所以就出现了下面的内容: 1.Redis保存Session信息 为了保存Session信息在集群中可用,所以session信息不能保存Tomcat中,因为如果是这样的话要想实现Session信息的共享就必须通过Session复制的方式来同步Tomcat之间的Session信息,这样比较费力费力而且性能消耗比较大,所以选择通过redis来实现Session信息的保存和共享. 1.1Redis的

Nginx+tomcat集群使用redis共享session

一 :nginx负载均衡 当Tomcat当做独立的Servlet容器来运行时,可看做是能运行Java Servlet的独立Web服务器. 此外 Tomcat还可以作为其他Web服务器进程内或者进程外的Servlet容器,从而与其他Web服务器集成(Apache Nginx IIS 等) 集成的意义在于:对于不支持运行java Servlet的其他Web服务器,可以通过集成Tomcat来提供运行Servlet的功能. ----------Tomcat与JavaWeb开发技术详解(孙卫琴的一本书)

Java企业级电商项目实战 Tomcat集群与Redis分布式

本套课程包含:java电商项目实战课程 Tomcat集群视频教程 Redis项目实战课程课程目录1-1 课程导学1-2 大型Java项目架构演进解析1-3 一期课程与问答服务回顾1-4 一期项目结构和代码回顾1-5 课程使用系统及技术版本介绍(一期+二期)1-6 二期项目初始化第2章 Lombok框架集成及原理解析本章会对Lombok框架进行介绍,同时会讲解Lombok的原理.并手把手领着小伙伴们实战,引入Lombok以及IDE安装Lombok插件.然后会带着大家实战Coding,讲解@Data