【SpringBoot框架篇】19.集成spring-session利用redis来实现session共享

文章目录

  • 1.简介
  • 2.引入依赖
  • 3.application.yml配置
  • 4.编写session拦截器
  • 5.启用RedisHttpSession和注册拦截器
  • 6.测试
  • 7.项目配套代码

1.简介

单机部署web应用的时候session是唯一的,但是如果水平扩展后,通过nginx负载访问,就会出现session不一致的情况,
例如在A节点登录的用户,后续的操作请求访问到B节点的接口,但是B节点session中没有用户身份信息,就会导致重新跳转到登录页的情况。
【SpringBoot框架篇】19.集成spring-session利用redis来实现session共享_第1张图片

解决session几种方式,还有其它的就不全列举出来了

  • session复制:例如Tomcat的广播机制
  • session持久化:把session信息放入sql数据库或者nosql数据库中,例如放入redis中
  • 反向代理hash一致性:四层hash和七层hash都可以做,保证一个用户的请求落在一台web-server上,如nginx配置如下即可解决session不一致的问题.
#七层 hash 
upstream tomcatServer{
ip_hash;
server 192.168.31.139:8080;
server 192.168.31.138:8080; 
} 

ip_hash保证每个访客固定访问一个后端服务器,nginx配置这块可以参考我的博客【Nginx从入门到应用】

此文使用SpringBoot+Spring-session+Redis实现Session的共享

2.引入依赖

        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
        
            org.springframework.boot
            spring-boot-starter-data-redis
            2.3.0.RELEASE
        

        
        
            org.springframework.session
            spring-session-data-redis
        

3.application.yml配置

spring:
  session:
  	#session存储方式
    store-type: redis
  redis:
    host: 127.0.0.1
    port: 6379
    #redis的超时时间
    timeout: 3000
    #设置会话操作后立即更新到redis中,默认是等服务器请求结束后再将变化的值同步到redis中
    flush-mode: immediate
    pool:
      # 连接池中的最大空闲连接
      min-idle: 0
      # 连接池中的最大空闲连接
      max-idle: 8
      # 连接池最大连接数(使用负值表示没有限制)
      max-active: 8
      # 连接池中的最小空闲连接
      max-wait: -1

4.编写session拦截器

  • 1.查看session中的user是否为null
  • 2.根据用户名去redis查询对应的sessionId信息
  • 3.如果查询的为null或者取出来的sessionId和当前会话的sessionId不一致,则拦截,否则放行
@Component
public class RedisSessionInterceptor implements HandlerInterceptor {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        if (session.getAttribute("user") != null) {
            try {
                User user = (User) session.getAttribute("user");
                //验证当前请求的session是否是已登录的session
                String loginSessionId = redisTemplate.opsForValue().get("loginUser:" + user.getUsername());
                if (loginSessionId != null && loginSessionId.equals(session.getId())) {
                    return true;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        response401(response);
        return false;
    }

    private void response401(HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        try {
            response.getWriter().print("用户未登录或登陆超时!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

5.启用RedisHttpSession和注册拦截器

对除了登录接口的所有以**/api**开头的接口进行拦截.

@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 20)//session过期时间(秒)
@Configuration
public class RedisSessionConfig implements WebMvcConfigurer {
    @Autowired
    RedisSessionInterceptor redisSessionInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry)
    {
        //所有已api开头的访问都要进入RedisSessionInterceptor拦截器进行登录验证;
        registry.addInterceptor(redisSessionInterceptor).addPathPatterns("/api/**").excludePathPatterns("/api/login/**");
    }
}

6.测试

测试用的Model类

public class User implements Serializable {
    private String username;
    private String loginSessionId;
    //省略 get,set 方法 ...
}

存放到session的model必须实现Serializable接口,不然会出现org.springframework.data.redis.serializer.SerializationException序列化异常,如下
在这里插入图片描述

测试用的接口

@RestController
@RequestMapping("/api")
public class UserController {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @GetMapping("/login/{username}")
    public User login(HttpSession session, @PathVariable String username){
        User user=new User();
        user.setUsername(username);
        user.setLoginSessionId(session.getId());
        session.removeAttribute("user");
        session.setAttribute("user",user);
        redisTemplate.opsForValue().set("loginUser:"+username, session.getId());
        return user;
    }

    @GetMapping("/index")
    public User index(HttpSession session){
        User user=(User) session.getAttribute("user");
        return user;
    }
}

测试流程
第一步.首先启动应用2次,端口例如8020,8021

idea中同一个项目启动2次操作如下
在这里插入图片描述
勾选Allow Paraller run,让idea支持同一个项目启动多次
【SpringBoot框架篇】19.集成spring-session利用redis来实现session共享_第2张图片
启动完第1个端口的时候,把配置文件的端口修改成8021,然后再次启动即可。

第二步.打开浏览器访问: http://localhost:8020/api/login/test

在这里插入图片描述

第三步.再打开一个窗口访问: http://localhost:8021/api/index
在这里插入图片描述

可以看到上面2个图片返回的sessionId是一致的,表示着操作已经成功了,

第四步.测试session拦截器是否有效
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 20)

由于我上面配置的session超时时间是20秒,所以等20秒后再访问/api/index接口就可以看到请求已经被成功拦截了。
在这里插入图片描述

到此该博客的任务已经完成,是不是配置起来很简单呢,感兴趣的小伙伴可以自己试试。

7.项目配套代码

github地址

创作不易,要是觉得我写的对你有点帮助的话,麻烦在github上帮我点下 Star

【SpringBoot框架篇】其它文章如下,后续会继续更新。

  • 1.搭建第一个springboot项目
  • 2.Thymeleaf模板引擎实战
  • 3.优化代码,让代码更简洁高效
  • 4.集成jta-atomikos实现分布式事务
  • 5.分布式锁的实现方式
  • 6.docker部署,并挂载配置文件到宿主机上面
  • 7.项目发布到生产环境
  • 8.搭建自己的spring-boot-starter
  • 9.dubbo入门实战
  • 10.API接口限流实战
  • 11.Spring Data Jpa实战
  • 12.使用druid的monitor工具查看sql执行性能
  • 13.使用springboot admin对springboot应用进行监控
  • 14.mybatis-plus实战
  • 15.使用shiro对web应用进行权限认证
  • 16.security整合jwt实现对前后端分离的项目进行权限认证
  • 17.使用swagger2生成RESTful风格的接口文档
  • 18.使用Netty加websocket实现在线聊天功能
  • 19.使用spring-session加redis来实现session共享
  • 20.自定义@Configuration配置类启用开关
  • 21.对springboot框架编译后的jar文件瘦身
  • 22.集成RocketMQ实现消息发布和订阅
  • 23.集成smart-doc插件零侵入自动生成RESTful格式API文档
  • 24.集成FastDFS实现文件的分布式存储

你可能感兴趣的:(springBoot,java,redis)