springcloud+redis+session实现单IP登录

2019-10-19  本文已影响0人  Lugton
本文介绍使用redis+session在springcloud框架下实现以下功能:
1.跨域共享session(使用redis+session)
2.历史访问量和实时访问量统计(使用HttpSessionListener)
3.单点登录(使用redis+session)

如何建立Springcloud工程在此不多加赘述。

Step1:跨域共享Session

由于SpringCloud为多模块项目,在前后端分离的情况下难免会有跨域请求接口的情况,如果代码中使用了session缓存数据,在跨域请求时会出现SessionID不一致,这样就获取不到想要的数据。使用SpringSessionRedis可以解决这一问题,实现跨域访问。步骤比较简单:

1.加入依赖

<!-- redis + session -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

2.配置yml文件

redis:
    database: 0
    host: localhost
    port: 6379
    password:
    timeout: 20000
    jedis:
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 8
        min-idle: 0
  session:
    store-type: redis

3.编写配置类

@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 300) //配置过期时间
public class RedisSessionConfig {
}

这样在不同域中便可以实现Session共享了。

Step2:Web历史访问量和实时访问量统计

这一步使用监听器对HttpSession实现监听,在监听到有Session创建或过期时进行记录。使用Redis存放历史访问量。
首先要先知道ServletContext和Servlet的关系。一个web应用对应一个ServletContext实例,这个实例是应用部署启动后,servlet容器为应用创建的。ServletContext实例包含了所有servlet共享的资源信息。通过提供一组方法给servlet使用,用来和servlet容器通讯,比如获取文件的MIME类型、分发请求、记录日志等。Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。


1.首先我们创建一个ServletContextListener

@Component
public class ServletContextListener implements javax.servlet.ServletContextListener {

    private Logger logger=LoggerFactory.getLogger(this.getClass());

    private Map<String, HttpSession> userMap = new HashMap<>();

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        logger.info("===========usermap初始化===========");
        servletContextEvent.getServletContext().setAttribute("userMap",userMap);
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}

这部分实现了在程序初始化时创建一个用于存放在线人数的HashMap,并将这个HashMap存放到ServletContext中。

2.创建HttpSessionListener

@Component
public class HttpSessionListener implements javax.servlet.http.HttpSessionListener {

    @Autowired
    RedisUtil redisUtil;

    @Autowired
    private StringRedisTemplate redisTemplate;

    private Logger logger=LoggerFactory.getLogger(this.getClass());

     /**
     *@description session创建监听
     *@author lugton
     *@date 2019/10/19
     *@return 
     */
    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {


        HttpSession session=httpSessionEvent.getSession();
        ServletContext servletContext=session.getServletContext();

        Map<String, HttpSession> userMap= (Map<String, HttpSession>) servletContext.getAttribute("userMap");
        try {
            String value=redisUtil.get("TotalCount",0);
            Integer count= Integer.parseInt(value);
            //判断userMap中是否存在该Session,若不存在,则访客+1
            if (!userMap.containsKey(session.getId())){
                userMap.put(session.getId(),session);//新访客存入userMap
                logger.info("============新访客进入===========");
                count=count+1;//有新访客 历史访问+1
                redisUtil.set("TotalCount",(count).toString(),0);//历史访问量存入redis
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        servletContext.setAttribute("userMap",userMap);

    }


    /**
     *@description session过期监听
     *@author lugton
     *@date 2019/10/19
     *@return
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        //获取session
        HttpSession session = se.getSession();
        ServletContext servletContext=session.getServletContext();
        Map<String, HttpSession> userMap= (Map<String, HttpSession>) servletContext.getAttribute("userMap");
        //判断当前过期的Session是否在userMap中
        if (userMap.containsKey(session.getId())){
            userMap.remove(session.getId());//移除
            logger.info("==============访客离开=============");
        }

        servletContext.setAttribute("userMap",userMap);
    }
}

3.redisUtil的代码如下

这部分本打算用JedisPool但总是获取不到连接池的资源,故弃之,各位有好的建议欢迎留言指出。

@Component
public class RedisUtil {

    private Logger logger=LoggerFactory.getLogger(this.getClass());


//    @Autowired
//    JedisPool jedisPool;

    /**
     *@description 根据数据库和key获取value
     *@author lugton
     *@date 2019/10/19
     *@return
     */
    public String get(String key,int indexdb) {
        Jedis jedis = null;
        String value = null;
        try {
            jedis = new Jedis("localhost",6379);
            jedis.select(indexdb);
            value = jedis.get(key);
            logger.info("历史访客数量为:"+value);
        } catch (Exception e) {

            logger.error(e.getMessage());
        }finally {
//            jedisPool.returnResource(jedis);
            jedis.close();
        }
        return value;
    }

    /**
     *@description 存入数据
     *@author lugton
     *@date 2019/10/19
     *@return
     */
    public String set(String key, String value,int indexdb) {
        Jedis jedis = null;
        try {
//            jedis = jedisPool.getResource();
            jedis=new Jedis("localhost",6379);
            jedis.select(indexdb);
            return jedis.set(key, value);
        } catch (Exception e) {

            logger.error(e.getMessage());
            return "0";
        }finally {
            jedis.close();
        }
    }
}

这样,就可以在redis中获得历史访问量,在userMap.size中获得实时用户量。


Step3:实现单IP登陆

在用redis共享session时,就想到了可以用redis存放当前在线用户,实现单IP登陆,思路比较简单,如果有不足之处欢迎各位在评论中指出。

1.登陆控制层

 /**
     *@description 登陆
     *@author lugton
     *@date 2019/10/16
     *@return
     */
    @RequestMapping("/login")
    public Map<String,Object> Login(@RequestParam("username") String userName, @RequestParam("password") String password, HttpServletRequest request, HttpServletResponse response){
        Map<String, Object> map = new HashMap<>();
        User user = userDao.findUserByUserName(userName);
        
       //判断该用户是否已存在redis中
        String loginSessionId=redisTemplate.opsForValue().get(user.getUserName());
        //加密为md5
//        password= Md5Util.StringInMd5(password);
        if (user!=null){
            //如果该用户在redis中存在,判断其sessionID和当前Session是否相同,
            //如果不同,则提示错误
            if (loginSessionId!=null&&(!loginSessionId.equals(request.getSession().getId()))){
                map.put("message", "该用户已登陆");
                map.put("result", false);
            }else {
                if (password.equals(user.getPassWord())){
                    request.getSession().setAttribute("user", user);

                    
                    //将该用户存入redis中
redisTemplate.opsForValue().set(user.getUserName(),request.getSession().getId());                          
 
                    map.put("message", "登陆成功");
                    map.put("result", true);

                }else {
                    map.put("message", "密码错误!");
                    map.put("result", false);
                }
            }
        }else {
            map.put("message", "该用户不存在!");
            map.put("result", false);
        }

        logger.info(String.valueOf(map.get("message")));

        return map;

    }

2.Session过期时,将Redis中存放的对应用户清空

@Override
    public void sessionDestroyed(HttpSessionEvent se) {
            /////////////////////////////////
            //重复部分在此省略
            User user= (User) session.getAttribute("user");
            if (user!=null){
                redisTemplate.opsForValue().set(user.getUserName(),"",1,TimeUnit.SECONDS);
            }
}

这样就实现了单点登陆的功能。经测试此功能能够实现,因为是自己想的方法,难免有不足之处,欢迎大家指出!

上一篇 下一篇

猜你喜欢

热点阅读