SpringBoot结合Redis实现维护客户端登录状态

1、需求

当用户未登录时,只允许访问index/login资源,否则直接查询数据,会过滤到登录页面要求登录。
由于这里用到Redis做缓存数据库,包括保存token以及查询数据缓存等,所以需要提前安装好Redis,如果还没有安装的,可以参考一下这篇文章:Windows系统本地安装Redis并设置服务自启动(图文)

2、实现

2.1、创建数据库表,以及添加测试数据

User表

CREATE TABLE `user` (
  `uid` varchar(50) NOT NULL COMMENT '用户id',
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `password` varchar(50) NOT NULL COMMENT '密码',
  `name` varchar(50) DEFAULT NULL COMMENT '姓名',
  PRIMARY KEY (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

插入数据:

INSERT INTO `user` VALUES ('1', 'lmf', 'lumingfei', '路明非');
INSERT INTO `user` VALUES ('10', 'hly', 'huiliyi', '绘梨衣');
INSERT INTO `user` VALUES ('2', 'lmz', 'lumingze', '路鸣泽');
INSERT INTO `user` VALUES ('3', 'czh', 'chuzihang', '楚子航');
INSERT INTO `user` VALUES ('30', 'xm', 'xiami', '夏弥');
INSERT INTO `user` VALUES ('31', 'sq', 'suqian', '苏茜');
INSERT INTO `user` VALUES ('8', 'yzs', 'yuanzhisheng', '源稚生');
INSERT INTO `user` VALUES ('80', 'yzn', 'yuanzhinv', '源稚女');
INSERT INTO `user` VALUES ('81', 'scy', 'shichuiying', '矢吹樱');
INSERT INTO `user` VALUES ('82', 'zblz', 'zuobolongzhi', '佐伯龙治');
INSERT INTO `user` VALUES ('9', 'ks', 'kaisa', '凯撒');
INSERT INTO `user` VALUES ('90', 'cmt', 'chenmotong', '陈墨瞳');

创建Movie表

CREATE TABLE `movie` (
  `movie_id` varchar(50) NOT NULL COMMENT '电影编号',
  `movie_name` varchar(20) DEFAULT NULL COMMENT '电影名称',
  `movie_price` decimal(10,2) DEFAULT NULL COMMENT '价格',
  `movie_release_time` varchar(20) DEFAULT NULL COMMENT '电影上映时间',
  `movie_shelf_time` varchar(20) DEFAULT NULL COMMENT '电影下架时间',
  `movie_recommended_level` int(11) DEFAULT NULL COMMENT '推荐等级',
  `movie_language` varchar(10) DEFAULT NULL COMMENT '电影语种',
  PRIMARY KEY (`movie_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

插入数据:

INSERT INTO `movie` VALUES ('1', '海上钢琴师', '99.90', '1900-09-09 09:09:09', '2020-20-20 20:20:20', '10', '英语');
INSERT INTO `movie` VALUES ('10', '鬼灭之刃', '99.90', '1909-09-09 19:29:59', '2020-20-20 20:20:20', '10', '日语');
INSERT INTO `movie` VALUES ('11', 'Fate', '77.90', '1907-07-19 17:07:27', '2020-20-20 20:20:20', '10', '日语');
INSERT INTO `movie` VALUES ('12', '哪吒之魔童降世', '80.90', '1980-11-29 23:00:23', '2020-20-20 20:20:20', '8', '汉语');
INSERT INTO `movie` VALUES ('13', '无双', '99.90', '1999-11-11 12:11:12', '2020-20-20 20:20:20', '10', '汉语');
INSERT INTO `movie` VALUES ('14', '三傻大闹宝莱坞', '89.90', '2000-01-11 12:00:00', '2020-20-20 20:20:20', '10', '印度语');
INSERT INTO `movie` VALUES ('15', '钢铁侠1', '99.90', '1900-09-09 09:09:09', '2020-20-20 20:20:20', '10', '英语');
INSERT INTO `movie` VALUES ('16', '钢铁侠2', '88.80', '1901-01-01 01:01:01', '2020-20-20 20:20:20', '8', '英语');
INSERT INTO `movie` VALUES ('17', '美国队长1', '99.90', '1909-09-09 19:29:59', '2020-20-20 20:20:20', '10', '英语');
INSERT INTO `movie` VALUES ('18', '美国队长2', '77.90', '1907-07-19 17:07:27', '2020-20-20 20:20:20', '9', '英语');
INSERT INTO `movie` VALUES ('19', '蜘蛛侠1', '80.90', '1980-11-29 23:00:23', '2020-20-20 20:20:20', '8', '英语');
INSERT INTO `movie` VALUES ('2', '阿甘正传', '88.80', '1901-01-01 01:01:01', '2020-20-20 20:20:20', '8', '英语');
INSERT INTO `movie` VALUES ('20', '蜘蛛侠2', '99.90', '1999-11-11 12:11:12', '2020-20-20 20:20:20', '10', '英语');
INSERT INTO `movie` VALUES ('21', '绿巨人', '89.90', '2000-01-11 12:00:00', '2020-20-20 20:20:20', '7', '英语');
INSERT INTO `movie` VALUES ('22', '复仇者联盟1', '99.90', '1900-09-09 09:09:09', '2020-20-20 20:20:20', '10', '英语');
INSERT INTO `movie` VALUES ('23', '复仇者联盟2', '88.80', '1901-01-01 01:01:01', '2020-20-20 20:20:20', '8', '英语');
INSERT INTO `movie` VALUES ('24', '金刚狼1', '99.90', '1909-09-09 19:29:59', '2020-20-20 20:20:20', '10', '英语');
INSERT INTO `movie` VALUES ('25', '金刚狼2', '77.90', '1907-07-19 17:07:27', '2020-20-20 20:20:20', '9', '英语');
INSERT INTO `movie` VALUES ('26', '侏罗纪公园', '80.90', '1980-11-29 23:00:23', '2020-20-20 20:20:20', '8', '英语');
INSERT INTO `movie` VALUES ('27', '猩球崛起', '99.90', '1999-11-11 12:11:12', '2020-20-20 20:20:20', '10', '英语');
INSERT INTO `movie` VALUES ('28', '金刚', '89.90', '2000-01-11 12:00:00', '2020-20-20 20:20:20', '7', '英语');
INSERT INTO `movie` VALUES ('29', '灵笼', '99.90', '1900-09-09 09:09:09', '2020-20-20 20:20:20', '10', '汉语');
INSERT INTO `movie` VALUES ('3', '当幸福来敲门', '99.90', '1909-09-09 19:29:59', '2020-20-20 20:20:20', '10', '英语');
INSERT INTO `movie` VALUES ('30', '秦时明月之百步飞剑', '88.80', '1901-01-01 01:01:01', '2020-20-20 20:20:20', '10', '汉语');
INSERT INTO `movie` VALUES ('31', '秦时明月之夜尽天明', '99.90', '1909-09-09 19:29:59', '2020-20-20 20:20:20', '10', '汉语');
INSERT INTO `movie` VALUES ('32', '秦时明月之诸子百家', '77.90', '1907-07-19 17:07:27', '2020-20-20 20:20:20', '10', '汉语');
INSERT INTO `movie` VALUES ('33', '秦时明月之万里长城', '80.90', '1980-11-29 23:00:23', '2020-20-20 20:20:20', '10', '汉语');
INSERT INTO `movie` VALUES ('34', '秦时明月至君临天下', '99.90', '1999-11-11 12:11:12', '2020-20-20 20:20:20', '10', '汉语');
INSERT INTO `movie` VALUES ('35', '天行九歌', '89.90', '2000-01-11 12:00:00', '2020-20-20 20:20:20', '10', '汉语');
INSERT INTO `movie` VALUES ('4', '肖申克的救赎', '77.90', '1907-07-19 17:07:27', '2020-20-20 20:20:20', '9', '英语');
INSERT INTO `movie` VALUES ('5', '看不见的客人', '80.90', '1980-11-29 23:00:23', '2020-20-20 20:20:20', '8', '印度语');
INSERT INTO `movie` VALUES ('6', '战狼2', '99.90', '1999-11-11 12:11:12', '2020-20-20 20:20:20', '10', '汉语');
INSERT INTO `movie` VALUES ('7', '大圣归来', '89.90', '2000-01-11 12:00:00', '2020-20-20 20:20:20', '7', '汉语');
INSERT INTO `movie` VALUES ('8', '犬夜叉', '99.90', '1900-09-09 09:09:09', '2020-20-20 20:20:20', '9', '日语');
INSERT INTO `movie` VALUES ('9', '刀剑神域', '88.80', '1901-01-01 01:01:01', '2020-20-20 20:20:20', '8', '日语');

2.2、创建Maven项目,并导入依赖

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.wnkj</groupId>
    <artifactId>sso</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>sso Maven Webapp</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>

    <build>
        <plugins>
            <!-- 设置项目的编译版本为本地jdk的版本 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <!-- 配置编译的默认编码类型为utf-8 -->
                <configuration>
                    <target>1.8</target>
                    <source>1.8</source>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>
            <!-- 启动maven的内置tomcat7服务器 -->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
            </plugin>
            <!-- Maven打包组件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <!-- 导入SpringBoot的父工程 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
    </parent>

    <dependencies>
        <!-- 导入web组件启动器,版本随父工程 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!-- 移除嵌入式tomcat插件 -->
            <!-- <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>Spring-boot-start-tomcat</artifactId>
                </exclusion>
            </exclusions> -->
        </dependency>

        <!-- 导入SpringBoot整合mybatis的组件启动器 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>

        <!-- 导入SpringBoot整合Redis的驱动器类型,版本随父工程 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>1.5.22.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.3</version>
        </dependency>

        <!-- 导入SpringBoot中的test组件启动器,版本随父工程 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <!-- 导入mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>

        <!-- 导入servlet-api依赖 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <!-- 导入jsp-api依赖 -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
            <scope>provided</scope>
        </dependency>
        <!-- 对jsp的支持的依赖 -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
        </dependency>

        <!-- 导入jstl标签库 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.68</version>
        </dependency>

        <!-- jackson,用于RESTful风格的请求url
            其中jackson-databind是主要,其他会作为辅助依赖自动加入-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>1.9.13</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-core-asl</artifactId>
            <version>1.9.13</version>
        </dependency>

        <!-- 便捷封装表单数据到JavaBean的依赖 -->
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.4</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
    </dependencies>
</project>

2.3、准备配置文件

2.3.1、准备SpringBoot的核心配置文件

server:
  port: 8574

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false&allowMultiQueries=true
    username: root
    password: dearest
  mvc:
    view:
      prefix: /
      suffix: .jsp

mybatis:
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/*/*.xml
  type-aliases-package: com.wnkj.entity

2.3.2、准备Redis数据库的参数属性文件

# Redis服务器地址
redis.host=127.0.0.1
# Redis服务器连接端口
redis.port=6379
# Redis服务器连接密码(默认为空)
redis.password=dearest
redis.database=0
redis.timeout=30000
# 连接池最大连接数(使用负值表示没有限制)
redis.maxTotal=30
# 连接池中的最大空闲连接
redis.maxIdle=10
redis.minIdle=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
redis.maxWaitMillis=1500
redis.testOnBorrow=true

2.4、准备MyBatis框架的全局配置文件

在resources/mybatis目录下创建mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 控制台打印sql语句 -->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

</configuration>

2.5、创建SpringBoot的引导类

package com.wnkj;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@MapperScan(value = "com.wnkj.dao")
@ServletComponentScan
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        System.out.println("SSO认证中心开启");
    }

}

2.6、创建实体类

get/set方法,toString(),构造函数自己添加。

2.6.1、创建User类

package com.wnkj.entity;

import java.io.Serializable;

public class User implements Serializable {
    private String uid;
    private String username;
    private String password;
    private String name;


2.6.2、创建Movie类

package com.wnkj.entity;

import java.io.Serializable;

public class Movie implements Serializable {
    private String movie_id;
    private String movie_name;
    private Double movie_price;
    private String movie_release_time;
    private String movie_shelf_time;
    private int movie_recommended_level;
    private String movie_language;
}

2.7、创建RedisConfig.java配置类

package com.wnkj.config;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

@Configuration
@PropertySource(value = "classpath:redisConfig.properties")
public class RedisConfig {

    @Value(value = "${redis.host}")
    private String host;
    @Value(value = "${redis.port}")
    private int port;
    @Value(value = "${redis.password}")
    private String password;
    @Value(value = "${redis.database}")
    private int database;
    @Value(value = "${redis.maxTotal}")
    private int maxTotal;
    @Value(value = "${redis.maxIdle}")
    private int maxIdle;
    @Value(value = "${redis.minIdle}")
    private int minIdle;
    @Value(value = "${redis.maxWaitMillis}")
    private int maxWaitMillis;
    @Value(value = "${redis.timeout}")
    private int timeout;
    @Value(value = "${redis.testOnBorrow}")
    private boolean testOnBorrow;


    @Bean(name = "jedisPoolConfig")
    public JedisPoolConfig getJedisPoolConfig() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(maxTotal);
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMinIdle(minIdle);
        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
        jedisPoolConfig.setTestOnBorrow(testOnBorrow);
        return jedisPoolConfig;
    }

    @Bean(name = "jedisPool")
    public JedisPool getJedisPool(@Qualifier(value = "jedisPoolConfig") JedisPoolConfig jedisPoolConfig) {
        return new JedisPool(jedisPoolConfig, host, port, timeout, password, database);
    }

}

2.8、创建MyBatis框架的持久层映射文件

在resources/mybatis/mapper目录下创建映射文件

2.8.1、创建UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wnkj.dao.UserMapper">

    <sql id="tableName">
        user
    </sql>

    <sql id="Field">
        uid,
        username,
        password,
        name
    </sql>

    <sql id="FieldValue">
        #{uid},
        #{username},
        #{password},
        #{name}
    </sql>

    <select id="findByUsername" parameterType="String" resultType="User">
        SELECT
            <include refid="Field" />
        FROM
            <include refid="tableName" />
        WHERE
            username = #{username}
    </select>

</mapper>

2.8.2、创建MovieMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wnkj.dao.MovieMapper">

    <sql id="tableName">
        movie
    </sql>

    <sql id="Field">
        movie_id,
        movie_name,
        movie_price,
        movie_release_time,
        movie_shelf_time,
        movie_recommended_level,
        movie_language
    </sql>

    <sql id="FieldValue">
        #{movie_id},
        #{movie_name},
        #{movie_price},
        #{movie_release_time},
        #{movie_shelf_time},
        #{movie_recommended_level},
        #{movie_language}
    </sql>

    <select id="listAll" resultType="Movie">
        SELECT
            <include refid="Field" />
        FROM
            <include refid="tableName" />
    </select>

</mapper>

2.9、创建持久层文件

2.9.1、创建用户模块持久层接口UserMapper.java

package com.wnkj.dao;

import com.wnkj.entity.User;

public interface UserMapper {

    User findByUsername(String username);
}

2.9.2、创建电影模块持久层MovieMapper.java

package com.wnkj.dao;

import com.wnkj.entity.Movie;

import java.util.List;

public interface MovieMapper {

    List<Movie> listAll();
}

2.10、创建业务层文件

2.10.1、创建用户模块业务层接口:UserService.java

package com.wnkj.service;

import com.wnkj.entity.User;

public interface UserService {

    User findByUsername(String username);
}

2.10.2、创建用户模块业务层实现类:UserServiceImpl.java

package com.wnkj.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wnkj.dao.UserMapper;
import com.wnkj.entity.User;
import com.wnkj.service.UserService;
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

@Service(value = "userService")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private JedisPool jedisPool;

    @Override
    public User findByUsername(String username) {
        Jedis jedis = null;
        User user = null;
        try {
            jedis = jedisPool.getResource();
            String userJsonString = jedis.get(username);

            // 如果查询缓存,等于空,则查询成User对象,并且不论查询结果是否为空,都保存到缓存
            if (userJsonString == null || "".equals(userJsonString) || userJsonString.trim().isEmpty()) {
                user = userMapper.findByUsername(username);
                jedis.set(username, JSON.toJSONString(user));
            } else if ("null".equals(userJsonString)) { // 当查询出空对象时会保存到数据库,由于保存的值会被序列化成字符串,因此是字符串的null值
                return user;
            } else { // 既不等于空也不等于“null”,说明是存在真实数据,解析为User对象并返回
                user = new User();
                JSONObject userJsonObject = JSON.parseObject(userJsonString);
                BeanUtils.populate(user, userJsonObject);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (jedis != null) {
                    jedis.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return user;
    }
}

2.10.3、创建电影模块业务层接口:MovieService.java

package com.wnkj.service;

import com.wnkj.entity.Movie;

import java.util.List;

public interface MovieService {

    List<Movie> listAll();
}

2.10.4、创建电影模块业务层实现类:MovieServiceImpl.java

package com.wnkj.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.wnkj.dao.MovieMapper;
import com.wnkj.entity.Movie;
import com.wnkj.service.MovieService;
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.ArrayList;
import java.util.List;

@Service(value = "movieService")
public class MovieServiceImpl implements MovieService {

    @Autowired
    private MovieMapper movieMapper;
    @Autowired
    private JedisPool jedisPool;

    @Override
    public List<Movie> listAll() {
        List<Movie> movieList = null;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String key = "MOVIE:listAll";
            String movieListJsonString = jedis.get(key);

            if (movieListJsonString == null || "".equals(movieListJsonString) || movieListJsonString.trim().isEmpty()) {
                movieList = movieMapper.listAll();
                System.out.println(JSON.toJSONString(movieList));
                jedis.set(key, JSON.toJSONString(movieList));
            } else if ("null".equals(movieListJsonString)) {
                return null;
            } else {
                movieList = new ArrayList<Movie>();
                JSONArray movieListJsonArray = JSON.parseArray(movieListJsonString);
                for (int i = 0; i < movieListJsonArray.size(); i++) {
                    JSONObject movieJsonObject = (JSONObject) movieListJsonArray.get(i);
                    Movie movie = new Movie();
                    BeanUtils.populate(movie, movieJsonObject);
                    movieList.add(movie);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (jedis != null) {
                    jedis.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return movieList;
    }
}

2.11、创建控制器文件

2.11.1、创建用户模块控制器:UserController.java

package com.wnkj.web.controller;

import com.alibaba.fastjson.JSON;
import com.wnkj.entity.User;
import com.wnkj.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

@RestController
@RequestMapping(value = "/user")
public class UserController {

    @Autowired
    private UserService userService;
    @Autowired
    private JedisPool jedisPool;

    @PostMapping(value = "/login")
    public ModelAndView login(User user, HttpServletRequest request, HttpServletResponse response) throws Exception {

        ModelAndView mv = new ModelAndView();

        // 校验用户名非空
        String username = user.getUsername();
        if (username == null || "".equals(username) || username.trim().isEmpty()) {
            mv.setViewName("login");
            mv.addObject("user", user);
            mv.addObject("username_errormsg", "用户名不能为空");
            return mv;
        }

        // 校验密码非空
        String password = user.getPassword();
        if (password == null || "".equals(password) || password.trim().isEmpty()) {
            mv.setViewName("login");
            mv.addObject("user", user);
            mv.addObject("password_errormsg", "密码不能为空");
            return mv;
        }

        // 根据用户名查询数据库
        User checkUser = userService.findByUsername(username);

        // 校验查询结果是否为空
        if (checkUser == null) {
            mv.setViewName("login");
            mv.addObject("user", user);
            mv.addObject("user_errormsg", "用户不存在");
            return mv;
        }

        // 校验密码是否相等
        String checkPassword = checkUser.getPassword();
        if (! password.equals(checkPassword)) {
            mv.setViewName("login");
            mv.addObject("user", user);
            mv.addObject("password_errormsg", "密码错误");
            return mv;
        }

        // 把用户信息保存到session域中
        request.getSession().setAttribute("user", checkUser);

        // 生成token
        String token = UUID.randomUUID().toString().replace("-", "").toUpperCase();

        // 创建Cookie并将token保存其中
        Cookie cookie = new Cookie("token", token);
        // 设置Cookie路径
        cookie.setPath("/");
        // 保存Cookie
        response.addCookie(cookie);

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            // 将token保存到redis缓存中
            jedis.set(token, JSON.toJSONString(checkUser));
            // 设置过期时间
            jedis.expire(token, 5 * 60);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (jedis != null) {
                    jedis.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 获取重定向链接
        String path = request.getScheme() + "://" +  request.getServerName() + ":"
                + request.getServerPort() + request.getContextPath() + "/";
        // 重定向
        response.sendRedirect(path.concat("movie/listAll"));

        return null;
    }
}

2.11.2、创建电影模块控制器:MovieController.java

package com.wnkj.web.controller;

import com.wnkj.entity.Movie;
import com.wnkj.service.MovieService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import redis.clients.jedis.JedisPool;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

@Controller
@RequestMapping(value = "/movie")
public class MovieController {

    @Autowired
    private MovieService movieService;

    @GetMapping(value = "/listAll")
    public ModelAndView listAll (HttpServletRequest request) {

        List<Movie> movieList = movieService.listAll();

        ModelAndView mv = new ModelAndView();
        mv.addObject("movieList", movieList);
        mv.setViewName("/WEB-INF/jsp/movie/listAll");
        return mv;
    }
}

2.12、创建过滤器:LoginFilter.java

package com.wnkj.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wnkj.entity.User;
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * 过滤器,过滤未登录用户
 */
@WebFilter(urlPatterns = "/*", filterName = "loginFilter")
public class LoginFilter implements Filter {

    @Autowired
    private JedisPool jedisPool;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("LoginFilter启动");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 强转类型
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;

        // 得到请求的servlet路径
        String servletPath = httpServletRequest.getServletPath();
        System.out.println("servletPath = " + servletPath);
        // 定义path,用于重定向
        String path = httpServletRequest.getScheme() + "://" + httpServletRequest.getServerName() + ":" + httpServletRequest.getServerPort()
                + httpServletRequest.getContextPath();

        // 如果包含index或login
        if (servletPath.contains("index") || servletPath.contains("login")) {
            // 放行
            chain.doFilter(httpServletRequest, httpServletResponse);
        } else {
            // 否则获取Cookie
            Cookie[] cookies = httpServletRequest.getCookies();
            if (cookies != null) {
                String token = null;
                // 缓存遍历
                for (Cookie c : cookies) {
                    // 得到每个cookie的名称
                    String name = c.getName();
                    // 如果cookie名称等于token
                    if ("token".equals(name)) {
                        // 获取到cookie的值
                        token = c.getValue();
                    }
                }

                // 判断token是否为空
                if (token != null && ! "".equals(token) && ! token.trim().isEmpty()) {
                    Jedis jedis = null;
                    String userJsonString = null;
                    try {
                        jedis = jedisPool.getResource();
                        // 查询Redis缓存
                        userJsonString = jedis.get(token);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    } finally {
                        try {
                            if (jedis != null) {
                                jedis.close();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

                    // 判断Redis缓存中对应token的用户信息是否为空
                    if (userJsonString != null && ! "".equals(userJsonString) && ! userJsonString.trim().isEmpty()) {
                        // 将Json字符串的用户信息转化为JsonObject对象(可看作是Map集合)
                        JSONObject userJsonObject = JSON.parseObject(userJsonString);
                        User user = new User();
                        try {
                            // 利用apache提供的BeanUtils组件封装Map中的属性到User实例中
                            BeanUtils.populate(user, userJsonObject);
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                        // 得到Session
                        HttpSession session = httpServletRequest.getSession();
                        // 保存用户信息到session域中
                        session.setAttribute("user", user);
                        // 放行
                        chain.doFilter(httpServletRequest, httpServletResponse);
                    } else {  // 如果Redis缓存中查询得到的用户信息为空,则要求重新登录获取用户信息
                        httpServletResponse.sendRedirect(path.concat("/login.jsp"));
                    }
                } else { // 如果Redis缓存中没有该token,说明登录状态失效
                    httpServletResponse.sendRedirect(path.concat("/login.jsp"));
                }
            } else { // 不包含Cookie,则跳转到login.jsp要求完成登录
                httpServletResponse.sendRedirect(path.concat("/login.jsp"));
            }
        }
    }

    @Override
    public void destroy() {
        System.out.println("LoginFilter停止");
    }
}

2.13、准备前端页面

2.13.1、修改index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<jsp:forward page="login.jsp" />

2.13.2、准备login.jsp

在webapps目录下创建login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<!DOCTYPE html>
<html>
<head>
    <base href="<%=basePath%>">
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="<%=basePath%>user/login" method="post">
        username:<input type="text" name="username" id="username" value="${requestScope.user.username}" />
        <label>${requestScope.username_errormsg}</label><br/>
        password:<input type="password" name="password" id="password" value="${requestScope.user.password}" />
        <label>${requestScope.password_errormsg}</label><br/>
        <input type="submit" value="login" />
        <label>${requestScope.user_errormsg}</label>
    </form>
</body>
</html>

2.13.3、创建listAll.jsp

在webapps/WEB-INF/jsp/movie/listAll目录下创建listAll.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<!DOCTYPE html>
<html>
<head>
    <base href="<%=basePath%>">
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        table, td {
            border:1px solid purple;
        }
        td {
            padding: 5px 10px;
        }
        table {
            text-align: center;
            border-collapse: collapse;
        }
    </style>
</head>
<body>
    <h1>欢迎您,${sessionScope.user.name}</h1>
    <table>
        <tr>
            <td>电影编号</td>
            <td>电影名称</td>
            <td>价格</td>
            <td>电影上映时间</td>
            <td>电影下架时间</td>
            <td>推荐等级</td>
            <td>电影语种</td>
        </tr>
        <c:forEach var="movie" items="${requestScope.movieList}">
            <tr>
                <td>${pageScope.movie.movie_id}</td>
                <td>${pageScope.movie.movie_name}</td>
                <td><fmt:formatNumber pattern="0.00" value="${pageScope.movie.movie_price}" /></td>
                <td>${pageScope.movie.movie_release_time}</td>
                <td>${pageScope.movie.movie_shelf_time}</td>
                <td>${pageScope.movie.movie_recommended_level}</td>
                <td>${pageScope.movie.movie_language}</td>
            </tr>
        </c:forEach>
    </table>
</body>
</html>

3、结果测试

3.1、Redis客户端查看Redis缓存

在这里插入图片描述

3.2、启动项目,测试

在这里插入图片描述
浏览器输入地址:http://localhost:8574/movie/listAll
查看控制台可知,servletPath不包含index/login,所以跳转到login.jsp要求完成登录
在这里插入图片描述
会跳转到如下页面:
在这里插入图片描述
输入用户名和密码登录之后,就可以查看到电影信息了
在这里插入图片描述

3.3、查看Redis缓存

有三个缓存:
在这里插入图片描述

随机字符串其实是token的值,用token做key,用户信息做value保存到缓存中:

在这里插入图片描述

lmf是我登录所使用的帐号,即当前登录的用户:

在这里插入图片描述

MOVIE:listAll是查询出来的电影数据:

在这里插入图片描述

未登录前,访问http://localhost:8574/movie/listAll是会被过滤掉请求的,登录后就可以直接访问这个链接了。
注意:由于代码中保存到redis缓存的token有效期为5分钟,5分钟后token会被删除,届时便无法直接查询电影数据,需要重新登录。

如下:
在这里插入图片描述
又需要登录才可以查询了:
在这里插入图片描述

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是实现步骤: 1. 添加 Redis 依赖 在 `pom.xml` 文件中添加 Redis 依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` 2. 配置 Redis 在 `application.properties` 文件中添加 Redis 相关配置: ```properties # Redis配置 spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.password= spring.redis.database=0 spring.redis.timeout=60000 ``` 3. 编写验证码生成和存储逻辑 编写一个 `VerificationCodeUtil` 工具类,生成验证码并将验证码存储到 Redis 中: ```java @Component public class VerificationCodeUtil { @Autowired private RedisTemplate<String, String> redisTemplate; /** * 生成验证码 * @param key 键 * @param expire 过期时间 * @return 验证码 */ public String generateCode(String key, long expire) { //生成四位数字的验证码 String code = String.format("%04d", new Random().nextInt(9999)); //将验证码存储到 RedisredisTemplate.opsForValue().set(key, code, expire, TimeUnit.SECONDS); return code; } } ``` 4. 编写验证码登录逻辑 编写一个 `LoginController` 控制器,实现验证码登录功能: ```java @RestController public class LoginController { @Autowired private VerificationCodeUtil verificationCodeUtil; @Autowired private RedisTemplate<String, String> redisTemplate; @PostMapping("/login") public String login(String phone, String code) { //从 Redis 中获取验证码 String cacheCode = redisTemplate.opsForValue().get(phone); if (cacheCode == null) { return "验证码已过期,请重新获取"; } if (!cacheCode.equals(code)) { return "验证码错误"; } //验证码验证通过,执行登录逻辑 return "登录成功"; } @GetMapping("/getCode") public String getCode(String phone) { //生成验证码并存储到 Redis 中,有效期为60秒 String code = verificationCodeUtil.generateCode(phone, 60); return code; } } ``` 至此,我们已经实现了使用 Redis 存储验证码并实现验证码登录的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值