likes
comments
collection
share

SpringBoot2.x系列教程45--SpringBoot整合JPA、Redis实现分布式的Session共享

作者站长头像
站长
· 阅读数 17

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第19天,点击查看活动详情

前言

在上一篇文章中,壹哥 已经带大家实现了分布式环境下的Session共享功能,但之前的案例中,其实还是存在着一些问题,主要就是性能不够高效。现在开发时,分布式的Session共享功能都是基于Redis来进行实现的,简单又高效,那这个该如何实现呢?本篇教程中,壹哥 就带大家基于Redis来实现Session共享功能。

一. 基于Redis实现分布式的Session共享

1. 创建web项目

我们按照之前的经验,创建一个web程序,并将之改造成Spring Boot项目,具体过程略。 SpringBoot2.x系列教程45--SpringBoot整合JPA、Redis实现分布式的Session共享

2. 添加依赖包

在pom.xml文件中添加如下核心依赖包。

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!-- 改造:添加sql相关的依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <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>
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.39</version>
        </dependency>

3. 创建application.yml配置文件

创建application.yml文件,在其中添加如下配置,尤其是要把redis的配置设置好。

server:
  port: 8080
  servlet:
    #设置session过期时间
    session:
      timeout: "PT10M"
spring:
  application:
    name: spring-session-jpa
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: syc
    url: jdbc:mysql://localhost:3306/spring-security?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false&serverTimezone=UTC
  redis:
    host: localhost
    port: 6379
    database: 0
    jedis:
      pool:
        max-idle: 8
        min-idle: 0
        max-active: 8
  session:
    #设置session存储类型
    store-type: redis
  jpa:
    database: mysql
    show-sql: true #开发阶段,打印要执行的sql语句.
    hibernate:
      ddl-auto: update

4. 创建User实体类

创建一个实体类,用于封装用户信息。

package com.yyg.boot.entity;

import ch.qos.logback.classic.db.names.TableName;
import lombok.Data;
import lombok.ToString;
import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;

/**
 * @Author 一一哥Sun
 * @Date Created in 2020/4/29
 * @Description Description
 */
@Entity
@Table(name="user")
@Data
@ToString
public class User implements Serializable {

    @Id
    @GeneratedValue(generator = "idGenerator",strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "username")
    @NotBlank(message = "username can not be empty")
    private String username;

    @Column(name = "password")
    @NotBlank(message = "password can not be empty")
    private String password;

}

5. 创建UserRepository仓库类

我们这里还是利用JPA进行数据库的操作,所以需要创建一个UserRepository类。

package com.yyg.boot.repository;

import com.yyg.boot.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * @Author 一一哥Sun
 * @Date Created in 2020/4/29
 * @Description Description
 */
public interface UserRepository extends JpaRepository<User,Long> {

    User findByUsername(String username);

    User findByUsernameAndPassword(String username,String password);

}

6. 创建Service实现类

接下来我们编写Service代码,调用dao层数据库操作。

6.1 创建UserService接口

package com.yyg.boot.service;

import com.yyg.boot.entity.User;

/**
 * @Author 一一哥Sun
 * @Date Created in 2020/4/29
 * @Description Description
 */
public interface UserService {
    /**
     * save user
     */
    User saveUser(User user);

    /**
     * find user by account
     */
    User findByUsername(String username);

    /**
     * user login
     */
    User login(String username, String password);

}

6.2 创建UserServiceImpl实现类

package com.yyg.boot.service.impl;

import com.yyg.boot.entity.User;
import com.yyg.boot.repository.UserRepository;
import com.yyg.boot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @Author 一一哥Sun
 * @Date Created in 2020/4/29
 * @Description Description
 */
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public User saveUser(User user) {
        return userRepository.save(user);
    }

    @Override
    public User findByUsername(String username) {
        return userRepository.findByUsername(username);
    }

    @Override
    public User login(String username, String password) {
        return userRepository.findByUsernameAndPassword(username, password);
    }

}

7. 创建UserController接口

package com.yyg.boot.web;

import com.yyg.boot.entity.User;
import com.yyg.boot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpSession;

/**
 * @Author 一一哥Sun
 * @Date Created in 2020/4/29
 * @Description Description
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * save user
     */
    @PostMapping(value = "/save")
    public User save(@RequestBody User user) {
        return userService.saveUser(user);
    }

    /**
     * find user by username
     */
    @GetMapping(value = "/find/{username}")
    public User findUser(@PathVariable String username) {
        return userService.findByUsername(username);
    }

    /**
     * user login
     */
    @GetMapping(value = "/login")
    public User login(String username, String password, HttpSession session) {
        User user = userService.login(username, password);
        if(user!=null){
            session.setAttribute(session.getId(),user);
        }
        return user;
    }

    @GetMapping(value = "/logout")
    public String logout(HttpSession session){
        session.removeAttribute(session.getId());
        return "user logout success";
    }

}

8. 创建拦截器

这里我们通过创建一个拦截器,来判断session是否有效。

package com.yyg.boot.config;

import com.alibaba.fastjson.JSON;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @Author 一一哥Sun
 * @Date Created in 2020/4/28
 * @Description 开启Redis Http Session
 */
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
public class RedisHttpSessionConfiguration implements WebMvcConfigurer {

    /**
     * 添加过滤接口
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SecurityInterceptor())
                //排除拦截
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/user/logout")
                //拦截路径
                .addPathPatterns("/**");
    }

    /**
     * 判断session是否为空,从而进行拦截放行
     */
    @Configuration
    public class SecurityInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
            HttpSession session = request.getSession();
            if (session.getAttribute(session.getId()) != null) {
                return true;
            }
            response.getWriter().write(JSON.toJSONString("please login first"));
            return false;
        }
    }

}

9. 创建入口类

最后创建一个项目入口类,进行项目启动。

package com.yyg.boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @Author 一一哥Sun
 * @Date Created in 2020/4/28
 * @Description Description
 */
@SpringBootApplication
public class SpringSessionJpaApplication {

    public static void main(String[] args){
        SpringApplication.run(SpringSessionJpaApplication.class,args);
    }

}

10. 完整项目结构

完整的项目结构如下图所示,大家可以参考创建出来。

SpringBoot2.x系列教程45--SpringBoot整合JPA、Redis实现分布式的Session共享

二. 启动项目,进行测试

1. 测试未登录时的反应

我们在未登录的情况下,直接根据用户名查询某个用户信息,会看到浏览器提示我们先要进行登录。 SpringBoot2.x系列教程45--SpringBoot整合JPA、Redis实现分布式的Session共享

2. 测试save接口

然后测试save接口,会发现也是提示先进行登录才可以。 SpringBoot2.x系列教程45--SpringBoot整合JPA、Redis实现分布式的Session共享

3. 进行登录测试

此时我们进行登录,登录成功 SpringBoot2.x系列教程45--SpringBoot整合JPA、Redis实现分布式的Session共享

4. 测试save与find接口

然后我们再进行save或find接口的测试,会发现此时这些接口都已经可以正常访问了。 SpringBoot2.x系列教程45--SpringBoot整合JPA、Redis实现分布式的Session共享

数据库中已经多了一个新的数据。 SpringBoot2.x系列教程45--SpringBoot整合JPA、Redis实现分布式的Session共享

5. 再次退出登录

然后我们再退出登录 SpringBoot2.x系列教程45--SpringBoot整合JPA、Redis实现分布式的Session共享

接着重新测试查询接口,会发现已经无法查询用户信息了,提示需要进行登录才可以。 SpringBoot2.x系列教程45--SpringBoot整合JPA、Redis实现分布式的Session共享

再重新登录后,我们可以再启动8081这个新的进程,然后查询tom这个用户信息,会发现也查询到了tom的用户信息,说明我们已经实现了Session的共享。 SpringBoot2.x系列教程45--SpringBoot整合JPA、Redis实现分布式的Session共享

而且redis中也有了保存的Session信息。 SpringBoot2.x系列教程45--SpringBoot整合JPA、Redis实现分布式的Session共享

结语

至此,我们就基于Redis,实现了分布式环境下的Session共享功能。不知今天的代码,你有么有搞出来呢?评论区留言说说你的收获吧。