• 使用springBoot+jsp+redis解决单一用户问题


    1.需求

      1.1 情景为单一域名。

      1.2 当用户在浏览器页面登陆成功后,如果这时再在同一个浏览器打开一个登陆页面进行登陆,那么就需要删除前一个页面的登陆的信息。

        这时再在前一个页面进行操作,就会发现需要再一次的登陆。在以上情况下,如果打开另一个浏览器页面惊喜登陆,需要把之前的浏览器登陆信息删除,

        当用户在前一个浏览器进行操作时会发现需要再一次的登陆,同样的,如果用户在这个浏览器进行了登陆,那么另外一个浏览器的登陆信息需要被删除。

    2 .分析解决

        在这里首先设定用户admin在一个浏览器上已经登陆过了,登陆过之后就会存入一些信息到cookie和redis中。那么这里该怎么存呢?

      因为条件是必须保证单一登陆,类似于PC上的QQ,所以这里先考虑在同一浏览器上的情况,既然是在同一浏览器了,那么需要保证单一性就比较好写了。

        我的写法是用户登陆后将用户名作为cookie的键,使用uuid或者雪花算法随机生成一个值,写入到前端,在以cookie的值作为键,再随机生成一个数值作为  值存入到redis中,保证当前用户只在一个地方进行了登陆。这里选择将用户id做为cookie的键的原因是因为后面还需要考虑到不同浏览器的问题。

        用户admin第一次登陆成功后,就有信息存在在redis中和cookie中了。当admin在打开同一浏览器的一个页面进行登陆时,就可以把cookie带入到请求中,  在请求的过程中,使用拦截器将请求拦截下来,因为是登陆请求,那么就肯定可以获取到用户名,在这里就可以根据用户名获取到Cooke的值,而根据cookie      的值就可以获取到redis的值,在新页面上登陆后,把redis中的信息删除和cookie删除。

        这个时候逻辑还是有问题的,我们是无法确定用户已经登陆过了,前面的假设是为了更好理解。也就是说我们是无法确定cookie是否存在,那么我们就需要

      在拦截器里加判断,判断cookie是否存在,如果存在那么就可以肯定用户是已经登陆过的了,只需要做上面的操做即可。这个时候需要加一点新的逻辑,用户在  同一个浏览第二次登陆时,会把第一次的登陆信息删掉,而这时后用户再去第一次登陆过的页惊喜操作,拦截会把请求拦截下来,在删除前一次登陆的信息后,  重定向到登陆页面,拦截器不放行,不继续执行loginController。

        以上分析是基于在同一个浏览器的情况下,当不在同一浏览器时是什么情况呢?首先可以肯定的是cookie肯定是不存在的,如果是在同一个浏览器下登陆   cookie怎么可能不存在呢?但是cookie不存在的情况又可以分为两种情景,一种是用户首先在其他浏览器进行了登陆,在来这个浏览器进行登陆,另一种情况   是用户真的才刚刚登陆。那么着重看第一种情况,第一种情况怎么解决呢?需要在不同浏览器的到用户的信息并进行操作。

        那么我是在用户登陆的时候,将用户名作为键,cookie的值作为value存入了redis。因为通过cookie的值就可以操作用户的唯一信息了。在cookie不存在的  情况下再进行判断,如果用户在Reids中有信息,则意味着他前面已经登陆过了,若不存在直接放行,如果已经登陆过了,删除之前的信息,在进行登陆。

    3. 主要代码

    拦截器

    在使用拦截器时需要注意的点是,因为我需要在拦截中使用jedis,当拦截器的执行顺序在注入bean之前。所以需要做一些操作:参考,当然,代码里已经改好了。

    package com.los.sso.interceptor;
    
    import com.los.sso.jedis.JedisDao;
    import org.springframework.util.StringUtils;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    public class LoginIntecerptor implements HandlerInterceptor {
    
    
        private JedisDao jedisDao;
    
        public LoginIntecerptor(JedisDao jedisDao) {
            this.jedisDao = jedisDao;
        }
    
        ////进入Controller之前执行该方法
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
            System.out.println("---》登陆拦截请求器");
            /**
             *
             *  case1:
             *      cookie存在且和redis中的相同
             *      同一浏览器重复登陆,删除上一次登陆信息后放行
             *  case2:
             *      resis中的token不存在
             *      可能是打开了另一个浏览器进行了登陆,删除上一次登陆后的信息,在放行
             *      如果信息不存在,则是另一个账号在进行登陆,直接放行
             *
             */
            //获取用户已存在redis中的令牌
            //拿到sessionid
            //拿到用户登陆名
            String username = request.getParameter("username");
            //Object username1 = request.getAttribute("username");
            if(StringUtils.isEmpty(username)){//用户名为空时
                request.setAttribute("status",404);
                response.sendRedirect("login");
                return false;
            }
    
            boolean flag = true;
            String redisvalue = "";
            //是否存在token
            Cookie[] cookies = request.getCookies();
            for (Cookie cookie:cookies) {
                if(cookie.getName().equals(username)){
                    redisvalue = cookie.getValue(); //token的值是redis中用户信息的key
                    flag = false;//存在token
                }
            }
            //根据token的值判断是否是在同一浏览器是第二次登陆
            if(!flag){  //token存在的情况
                String value = jedisDao.getValue(redisvalue);
                if(!StringUtils.isEmpty(value)){//不为空时,
                    jedisDao.delValue(redisvalue);  //删除上一次登陆存在redis中的信息
                    for (Cookie cookie:cookies) {
                        if(cookie.getName().equals(username)){
                            cookie.setValue(null);
                            response.addCookie(cookie);
                            break;
                        }
                    }
                    //进入登陆controller
                    response.sendRedirect("index");
                    return false;
                }
                //response.sendRedirect("index");
                return true;
            }else {
                /**
                 *     token不存在的情况
                 *    在浏览器上从未登陆过,但也有可能在其他浏览器登陆过了,需要删除存在redis中的值。
                 */
                //首先判断该用户是否已经登陆过
                String islogin = jedisDao.getValue(username);
                if(jedisDao.getValue(username)!=null&&!jedisDao.getValue(username).equals("")){
                    jedisDao.delValue(jedisDao.getValue(username));
                    //清空params
                    //清空Attribute
                    response.sendRedirect("index");
                    return false;
                }
                //另外一个账号登陆
                return true;
            }
        }
    
        //处理请求完成后视图渲染之前的处理操作
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
    
        }
    
        //视图渲染之后的操作
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    
        }
    }

    注册拦截器

    package com.los.sso.config;
    
    import com.los.sso.interceptor.LoginIntecerptor;
    import com.los.sso.jedis.JedisDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
    
        @Autowired
        private JedisDao jedisDao;
    
        //需要被拦截的路径
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            String[] addPathPatterns = {
                    "/in_login"
            };
            //不需要拦截的路径
            String [] excludePathPaterns={
                    "/index"
            };
    
            //注册一个拦截器
            registry.addInterceptor(new LoginIntecerptor(jedisDao))
                    .addPathPatterns(addPathPatterns)
                    .excludePathPatterns(excludePathPaterns);
            //如果有多个拦截器只需要在添加一遍就行
        }
    
    }

    pom.xml 信息

     <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
            <!-- 添加jedis依赖 -->
            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
                <version>3.0.1</version>
            </dependency>
            <!-- servlet 依赖. -->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <scope>provided</scope>
            </dependency>
            <!-- jstl依赖 -->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>jstl</artifactId>
            </dependency>
            <!--SpringBoot默认不支持JSP,需要在项目中添加相关的依赖-->
            <dependency>
                <groupId>org.apache.tomcat.embed</groupId>
                <artifactId>tomcat-embed-jasper</artifactId>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jdbc</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    
        <build>
            <resources>
                <!-- 打包时将jsp文件拷贝到META-INF目录下-->
                <resource>
                    <!-- 指定处理哪个目录下的资源文件 -->
                    <directory>src/main/webapp</directory>
                    <!--注意此次必须要放在此目录下才能被访问到-->
                    <targetPath>META-INF/resources</targetPath>
                    <includes>
                        <include>**/**</include>
                    </includes>
                </resource>
            </resources>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>

    yml 配置信息

    server:
      port: 8989
      servlet:
        jsp:
          init-parameters:
            development: true
    spring:
      main:
        allow-bean-definition-overriding: true
      mvc:
        view:
          prefix: /WEB-INF/jsp/
          suffix: .jsp
      datasource:
        url: jdbc:mysql://localhost:3306/sys
        username: root
        password: sasa
        driver-class-name: com.mysql.jdbc.Driver
      jpa:
        database: MYSQL
        show-sql: true
        hibernate:
          ddl-auto: update
        properties:
          hibernate:
            dialect:  org.hibernate.dialect.MySQL5Dialect
      redis:
        host: 你的ip
        password:
        jedis:
          pool:
            max-total: 200

    实体类,使用sprigdataJpa根据实体类生成数据库表。

    import lombok.Data;
    import javax.persistence.*;
    
    @Data
    @Entity
    @Table(name = "ssologin")
    public class User {
    
        @Id
        @Column(name = "id")
        private int id;
    
        @Column(name = "username")
        private String username;
    
        @Column(name = "password")
        private String password;
    }

    项目结构

      关于一些Jedis的工具网上百度的话有一大堆,这里就不贴了。

      

      

  • 相关阅读:
    [备忘录]Download Google Drive Files using wget
    RangeNet++ spheracal projection的理解
    k8s容器的命名规则
    5分钟让你知道什么是PKI
    Kubernetes 学习15 kubernetes 认证及serviceaccount
    Kubernetes 学习10 Service资源
    SQL优化技巧
    一文看懂 MySQL 高性能优化技巧实践
    MySQL表的碎片整理和空间回收小结
    详谈 MySQL Online DDL
  • 原文地址:https://www.cnblogs.com/autonomy/p/11932847.html
Copyright © 2020-2023  润新知