使用springBoot+jsp+reds结局单一用户问题

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不存在的情况又可以分为两种情景,一种是用户首先在其他浏览器进行了登陆,在来这个浏览器进行登陆,另一种情况   是用户真的才刚刚登陆。那么着重看第一种情况,第一种情况怎么解决呢?需要在不同浏览器的到用户的信息并进行操作。

扫描二维码关注公众号,回复: 7978935 查看本文章

    那么我是在用户登陆的时候,将用户名作为键,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的工具网上百度的话有一大堆,这里就不贴了。

  

  

猜你喜欢

转载自www.cnblogs.com/autonomy/p/11932847.html