Redis快速实现分布式session的方法详解

目录
  • 前言
    • spring security
    • apache shiro
  • session作用
    • spring-session
      • 支持功能
    • 分布式seesion实战
      • 步骤1:依赖包
      • 步骤2:配置文件
      • 步骤3:实现逻辑
      • 步骤4:编写session拦截器
      • 步骤5:把拦截器注入到拦截器链中
      • 步骤6:测试

    前言

    我们在开发一个项目时通常需要登录认证,常用的登录认证技术实现框架有spring security和shiro

    spring security

    spring security是一个功能强大且高度可定制的身份验证和访问控制框架。它是保护基于spring的应用程序的事实上的标准。

    spring security是一个专注于为java应用程序提供身份验证和授权的框架。与所有spring项目一样,spring security的真正强大之处在于它可以很容易地扩展以满足定制需求,并且spring security和spring更加适配贴合,我们工作中常常使用到spring security。

    apache shiro

    apache shiro 是 java 的一个安全框架。目前,使用 apache shiro 的人越来越多,因为它相当简单,对比spring security,可能没有 spring security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 shiro 就足够了。

    不足:

    这些都是认证技术框架,在单体应用中都是常用的技术框架,但是在分布式中,应用可能要部署多份,这时通过nginx分发请求,但是每个单体应用都要可能重复验证,因为他们的seesion数据是放在他们自己服务中的。

    session作用

    session是客户端与服务器通讯会话跟踪技术,服务器与客户端保持整个通讯的会话基本信息。

    客户端在第一次访问服务端的时候,服务端会响应一个sessionid并且将它存入到本地cookie中,在之后的访问会将cookie中的sessionid放入到请求头中去访问服务器。

    spring-session

    spring session是spring的项目之一,spring session把servlet容器实现的httpsession替换为spring-session,专注于解决session管理问题。

    spring session提供了集群session(clustered sessions)功能,默认采用外置的redis来存储session数据,以此来解决session共享的问题。

    spring-session提供对用户session管理的一系列api和实现。提供了很多可扩展、透明的封装方式用于管理httpsession/websocket的处理。

    支持功能

    1. 轻易把session存储到第三方存储容器,框架提供了redis、jvm的map、mongo、gemfire、hazelcast、jdbc等多种存储session的容器的方式。这样可以独立于应用服务器的方式提供高质量的集群。
    2. 同一个浏览器同一个网站,支持多个session问题。 从而能够很容易地构建更加丰富的终端用户体验。
    3. restful api,不依赖于cookie。可通过header来传递jessionid 。控制session id如何在客户端和服务器之间进行交换,这样的话就能很容易地编写restful api,因为它可以从http 头信息中获取session id,而不必再依赖于cookie
    4. websocket和spring-session结合,同步生命周期管理。当用户使用websocket发送请求的时候

    分布式seesion实战

    步骤1:依赖包

    因为是web应用。我们加入springboot的常用依赖包web,加入springsession、redis的依赖包,移支持把session存储在redis

    <dependency>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-web</artifactid>
    </dependency>
    
    <dependency>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-test</artifactid>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupid>org.projectlombok</groupid>
        <artifactid>lombok</artifactid>
        <version>1.18.8</version>
    </dependency>
    
    <dependency>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-redis</artifactid>
        <version>1.4.7.release</version>
    </dependency>
    
    <dependency>
        <groupid>org.springframework.session</groupid>
        <artifactid>spring-session-data-redis</artifactid>
    </dependency>

    这里因为是把seesion存储在redis,这样每个服务登录都是去查看redis中数据进行验证的,所有是分布式的。 这里要引入spring-session-data-redis和spring-boot-starter-redis

    步骤2:配置文件

    spring.application.name=spring-boot-redis
    server.port=9090
    # 设置session的存储方式,采用redis存储
    spring.session.store-type=redis
    # session有效时长为15分钟
    server.servlet.session.timeout=pt15m
    
    ## redis 配置
    ## redis数据库索引
    spring.redis.database=1
    ## redis服务器地址
    spring.redis.host=127.0.0.1
    ## redis服务器连接端口
    spring.redis.port=6379
    ## redis服务器连接密码(默认为空)
    spring.redis.password=

    步骤3:实现逻辑

    初始化用户数据

    @slf4j
    @restcontroller
    @requestmapping(value = "/user")
    public class usercontroller {
    
        map<string, user> usermap = new hashmap<>();
    
        public usercontroller() {
            //初始化1个用户,用于模拟登录
            user u1=new user(1,"user1","user1");
            usermap.put("user1",u1);
        }
    }

    这里就不用使用数据库了,初始化两条数据代替数据库,用于模拟登录

    登录

     @getmapping(value = "/login")
        public string login(string username, string password, httpsession session) {
            //模拟数据库的查找
            user user = this.usermap.get(username);
            if (user != null) {
                if (!password.equals(user.getpassword())) {
                    return "用户名或密码错误!!!";
                } else {
                    session.setattribute(session.getid(), user);
                    log.info("登录成功{}",user);
                }
            } else {
                return "用户名或密码错误!!!";
            }
            return "登录成功!!!";
        }

    登录接口,根据用户名和密码登录,这里进行验证,如果验证登录成功,使用 session.setattribute(session.getid(), user);把相关信息放到session中。

    查找用户

        /**
         * 通过用户名查找用户
         */
        @getmapping(value = "/find/{username}")
        public user find(@pathvariable string username) {
            user user=this.usermap.get(username);
            log.info("通过用户名={},查找出用户{}",username,user);
            return user;
        }

    模拟通过用户名查找用户

    获取session

      /**
         *拿当前用户的session
         */
        @getmapping(value = "/session")
        public string session(httpsession session) {
            log.info("当前用户的session={}",session.getid());
            return session.getid();
        }

    退出登录

      /**
         * 退出登录
         */
        @getmapping(value = "/logout")
        public string logout(httpsession session) {
            log.info("退出登录session={}",session.getid());
            session.removeattribute(session.getid());
            return "成功退出!!";
        }

    这里退出时,要把session中的用户信息删除。

    步骤4:编写session拦截器

    session拦截器的作用:验证当前用户发来的请求是否有携带sessionid,如果没有携带,提示用户重新登录。

     @configuration
        public class securityinterceptor implements handlerinterceptor {
            @override
            public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws ioexception {
                httpsession session = request.getsession();
                //验证当前session是否存在,存在返回true true代表能正常处理业务逻辑
                if (session.getattribute(session.getid()) != null){
                    log.info("session拦截器,session={},验证通过",session.getid());
                    return true;
                }
                //session不存在,返回false,并提示请重新登录。
                response.setcharacterencoding("utf-8");
                response.setcontenttype("application/json; charset=utf-8");
                response.getwriter().write("请登录!!!!!");
                log.info("session拦截器,session={},验证失败",session.getid());
                return false;
            }
        }

    步骤5:把拦截器注入到拦截器链中

    @slf4j
    @configuration
    public class sessioncofig implements webmvcconfigurer {
    
        @override
        public void addinterceptors(interceptorregistry registry) {
            registry.addinterceptor(new securityinterceptor())
                    //排除拦截的2个路径
                    .excludepathpatterns("/user/login")
                    .excludepathpatterns("/user/logout")
                    //拦截所有url路径
                    .addpathpatterns("/**");
        }
    }

    步骤6:测试

    登录user1用户:http://127.0.0.1:9090/user/login?username=user1&password=user1

    查询user1用户session:http://127.0.0.1:9090/user/session

    退出登录: http://127.0.0.1:9090/user/logout

    登录后查看redis中数据:

    seesion数据已经保存到redis了,到这里我们就整合了使用spring-seesion实现分布式seesion功能。

    以上就是redis快速实现分布式session的方法详解的详细内容,更多关于redis 分布式session的资料请关注www.887551.com其它相关文章!

    (0)
    上一篇 2022年3月21日
    下一篇 2022年3月21日

    相关推荐