当前位置: 代码网 > it编程>编程语言>Java > SpringBoot使用redis实现session共享功能

SpringBoot使用redis实现session共享功能

2024年05月28日 Java 我要评论
1.安装redis,并配置密码这里就不针对于redis的安装约配置进行说明了,直接在项目中使用。redis在windows环境下安装:window下redis的安装和部署详细图文教程_redis_代码

1.安装redis,并配置密码

这里就不针对于redis的安装约配置进行说明了,直接在项目中使用。

redis在windows环境下安装:window下redis的安装和部署详细图文教程_redis_代码网 (jb51.net)

2.pom.xml文件中引入需要的maven

     <dependency>
		<groupid>redis.clients</groupid>
		<artifactid>jedis</artifactid>
		<version>2.9.0</version>
	</dependency>

3.在项目的配置文件中加入redis的配置

redis:
  database: 0
  host: localhost
  password: 123456
  pool:
    max-active: 8
    max-idle: 8
    max-wait: -1
    min-idle: 0
  port: 6379
  timeout: 3000

4.添加redis的配置文件放在项目的config文件夹下

import org.springframework.beans.factory.annotation.value;
import org.springframework.cache.annotation.cachingconfigurersupport;
import org.springframework.cache.annotation.enablecaching;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.data.redis.connection.redisconnectionfactory;
import org.springframework.data.redis.connection.jedis.jedisconnectionfactory;
import redis.clients.jedis.jedispool;
import redis.clients.jedis.jedispoolconfig;
 
/**
 * @author kjz
 */
@configuration
@enablecaching
public class redisconfig extends cachingconfigurersupport {
 
	@value("${redis.host}")
	private string host;
 
	@value("${redis.port}")
	private int port;
 
	@value("${redis.timeout}")
	private int timeout;
 
	@value("${redis.pool.max-idle}")
	private int maxidle;
 
	@value("${redis.pool.max-wait}")
	private long maxwaitmillis;
 
	@value("${redis.password}")
	private string password;
 
	@bean
	public jedispool redispoolfactory() {
		jedispoolconfig jedispoolconfig = new jedispoolconfig();
		jedispoolconfig.setmaxidle(maxidle);
		jedispoolconfig.setmaxwaitmillis(maxwaitmillis);
 
		jedispool jedispool = new jedispool(jedispoolconfig, host, port, timeout);
		return jedispool;
	}
 
	@bean
	public redisconnectionfactory redisconnectionfactory() {
		jedispoolconfig jedispoolconfig = new jedispoolconfig();
		jedispoolconfig.setmaxidle(maxidle);
		jedispoolconfig.setmaxwaitmillis(maxwaitmillis);
		jedisconnectionfactory jedisconnectionfactory = new jedisconnectionfactory(jedispoolconfig);
		jedisconnectionfactory.sethostname(host);
		jedisconnectionfactory.setport(port);
		jedisconnectionfactory.settimeout(timeout);
		jedisconnectionfactory.setpassword(password);
		return jedisconnectionfactory;
	}
 
}

5.具体实现思路(手动实现)

实现思路

创建一个过滤器,拦截除了登录之外的所有请求,判断请求中是否存在cookie,如果存在cookie则判断redis中是否存在以cookie为key的键值对数据,如果有则取出对应的value同时对这个key的过期时间进行续期,如果没有则返回一个响应,说明登录已经过期了,将value就是session进行jason反序列化得到session对象,然后把session绑定到当前的请求中,如果不存在cookie,则直接返回一个响应,说明还未登录。如果是登录请求的话,直接到controller中进行登录校验,让深沉的session通过json序列化放到redis中,并且以uuid为key,同时,返回给前端一个cookie字段,cookie字段的值就是uuid,请求完成之后,在过滤器中将会话数据session更新到redis中。

下面是思路流程图

代码实现

创建过滤器 sessionfilter

import com.fasterxml.jackson.databind.objectmapper;
import redis.clients.jedis.jedis;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.ioexception;
import java.util.hashmap;
import java.util.map;
import java.util.uuid;
 
public class sessionfilter implements filter {
 
    private jedispool jedispool;
    private objectmapper objectmapper;
    private static final string login_path = "/login";
    private static final int session_expiration_time = 30 * 60; // 30 minutes in seconds
 
    public sessionfilter(jedispool jedispool) {
        this.jedispool = jedispool;
        this.objectmapper = new objectmapper();
    }
 
    @override
    public void dofilter(servletrequest request, servletresponse response, filterchain chain)
            throws ioexception, servletexception {
 
        httpservletrequest httprequest = (httpservletrequest) request;
        httpservletresponse httpresponse = (httpservletresponse) response;
 
        string requesturi = httprequest.getrequesturi();
 
        if (requesturi.equals(login_path)) {
            // 直接转发到登录控制器
            chain.dofilter(request, response);
        } else {
            // 检查 cookie
            cookie[] cookies = httprequest.getcookies();
            string sessionid = null;
            if (cookies != null) {
                for (cookie cookie : cookies) {
                    if ("sessionid".equals(cookie.getname())) {
                        sessionid = cookie.getvalue();
                        break;
                    }
                }
            }
 
            if (sessionid != null) {
                try (jedis jedis = jedispool.getresource()) {
                    string sessiondatajson = jedis.get(sessionid);
                    if (sessiondatajson != null) {
                        // 续期
                        jedis.expire(sessionid, session_expiration_time);
 
                        // 反序列化 session
                        map<string, object> sessionattributes = objectmapper.readvalue(sessiondatajson, map.class);
                        httpsessionwrapper wrappedsession = new httpsessionwrapper(sessionattributes);
                        request.setattribute("httpsession", wrappedsession);
 
                        // 继续执行过滤器链
                        chain.dofilter(request, response);
 
                        // 更新 session 到 redis
                        if (wrappedsession.isdirty()) {
                            jedis.set(sessionid, objectmapper.writevalueasstring(wrappedsession.getsessiondata()));
                        }
                    } else {
                        // 登录过期
                        httpresponse.setcontenttype("application/json");
                        httpresponse.getwriter().write("{\"error\": \"session expired\"}");
                    }
                } catch (exception e) {
                    // 处理异常
                    e.printstacktrace();
                    httpresponse.senderror(httpservletresponse.sc_internal_server_error);
                }
            } else {
                // 未登录
                httpresponse.setcontenttype("application/json");
                httpresponse.getwriter().write("{\"error\": \"not logged in\"}");
            }
        }
    }
 
    // ... 其他 filter 方法 ...
}

注册过滤器

在 spring 配置中注册过滤器:

@bean
public filterregistrationbean<sessionfilter> sessionfilterregistration(sessionfilter sessionfilter) {
    filterregistrationbean<sessionfilter> registrationbean = new filterregistrationbean<>();
    registrationbean.setfilter(sessionfilter);
    registrationbean.addurlpatterns("/*");
    return registrationbean;
}

创建 httpsessionwrapper 类

将session和sessionid封装到这个类里面

import javax.servlet.http.httpsession;
import java.util.enumeration;
import java.util.hashmap;
import java.util.map;
 
public class httpsessionwrapper implements httpsession {
 
    private final map<string, object> attributes;
    private boolean dirty;
 
    public httpsessionwrapper(map<string, object> attributes) {
        this.attributes = attributes;
        this.dirty = false;
    }
 
    // ... 实现 httpsession 接口的方法 ...
 
    public void setattribute(string name, object value) {
        attributes.put(name, value);
        dirty = true;
    }
 
    public map<string, object> getsessiondata() {
        return attributes;
    }
 
    public boolean isdirty() {
        return dirty;
    }
 
    // ... 其他方法 ...
}

登录控制器

@restcontroller
public class logincontroller {
 
    @postmapping("/login")
    public httpservletresponse login(httpservletrequest request, httpservletresponse response) {
        // ... 登录逻辑 ...
 
        // 创建新的会话
        string sessionid = uuid.randomuuid().tostring();
        map<string, object> sessionattributes = new hashmap<>();
        // 填充会话属性
        sessionattributes.put("user", user);
 
        // 使用 jsonutil 序列化会话并存储到 redis
        string sessiondatajson = jsonutil.obj2string(sessionattributes);
        try (jedis jedis = jedispool.getresource()) {
            jedis.setex(sessionid, session_expiration_time, sessiondatajson);
        } catch (exception e) {
            // 处理异常
            e.printstacktrace();
            return "error";
        }
 
        // 创建 cookie 并设置给客户端
        cookie sessioncookie = new cookie("sessionid", sessionid);
        sessioncookie.setpath("/");
        sessioncookie.sethttponly(true); // 确保 cookie 不会被 javascript 访问
        sessioncookie.setsecure(true); // 确保 cookie 在 https 连接中传输
        response.addcookie(sessioncookie);
 
        return httpservletresponse ;
    }
}

下面是序列化工具类

import com.fasterxml.jackson.annotation.jsoninclude;
import com.fasterxml.jackson.core.type.typereference;
import com.fasterxml.jackson.databind.javatype;
import com.fasterxml.jackson.databind.objectmapper;
import lombok.extern.slf4j.slf4j;
import org.apache.commons.lang.stringutils;
 
 
/**
 *@author kjz
 */
@slf4j
public class jsonutil {
 
    private static objectmapper objectmapper = new objectmapper();
 
    static{
        //对象的所有字段全部列入
        objectmapper.setserializationinclusion(jsoninclude.include.always);
    }
 
    public static <t> string obj2string(t obj){
        if(obj == null){
            return null;
        }
        try {
            return obj instanceof string ? (string)obj :  objectmapper.writevalueasstring(obj);
        } catch (exception e) {
            log.warn("parse object to string error",e);
            return null;
        }
    }
 
    /**
     * 格式化json串,看起来比较好看,但是有换行符等符号,会比没有格式化的大
     * @param obj
     * @param <t>
     * @return
     */
    public static <t> string obj2stringpretty(t obj){
        if(obj == null){
            return null;
        }
        try {
            return obj instanceof string ? (string)obj :  objectmapper.writerwithdefaultprettyprinter().writevalueasstring(obj);
        } catch (exception e) {
            log.warn("parse object to string error",e);
            return null;
        }
    }
 
 
    public static <t> t string2obj(string str,class<t> clazz){
        if(stringutils.isempty(str) || clazz == null){
            return null;
        }
 
        try {
            return clazz.equals(string.class)? (t)str : objectmapper.readvalue(str,clazz);
        } catch (exception e) {
            log.warn("parse string to object error",e);
            return null;
        }
    }
 
 
    public static <t> t string2obj(string str, typereference<t> typereference){
        if(stringutils.isempty(str) || typereference == null){
            return null;
        }
        try {
            return (t)(typereference.gettype().equals(string.class)? str : objectmapper.readvalue(str,typereference));
        } catch (exception e) {
            log.warn("parse string to object error",e);
            return null;
        }
    }
 
    /**
     * 转换集合
     * list<user></>
     * @param str
     * @param collectionclass
     * @param elementclasses
     * @param <t>
     * @return
     */
    public static <t> t string2obj(string str,class<?> collectionclass,class<?>... elementclasses){
        javatype javatype = objectmapper.gettypefactory().constructparametrictype(collectionclass,elementclasses);
        try {
            return objectmapper.readvalue(str,javatype);
        } catch (exception e) {
            log.warn("parse string to object error",e);
            return null;
        }
    }
}

6.利用spring session data redis框架实现

引入spring session data redis 的依赖

 <dependency>
        <groupid>org.springframework.session</groupid>
        <artifactid>spring-session-data-redis</artifactid>
        <version>2.7.0</version>
    </dependency>

创建spring session的配置类

创建一个配置类 sessionconfig,使用 @enableredishttpsession 注解来启用 spring session 的 redis 支持:

import org.springframework.session.data.redis.config.configureredisaction;
import org.springframework.session.data.redis.config.annotation.web.http.redishttpsessionconfiguration;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
 
@configuration
@enableredishttpsession
public class sessionconfig {
 
    // 配置会话过期时间(例如,设置为 1800 秒,即 30 分钟)
    @bean
    public redishttpsessionconfiguration redishttpsessionconfiguration() {
        redishttpsessionconfiguration configuration = new redishttpsessionconfiguration();
        configuration.setmaxinactiveintervalinseconds(1800);
        return configuration;
    }
 
    // 如果你使用的是 spring boot 2.3 或更高版本,你可能需要定义这个 bean 来避免警告
    @bean
    public static configureredisaction configureredisaction() {
        return configureredisaction.no_op;
    }
}

@enableredishttpsession 是一个方便的注解,它做了以下几件事情:

  1. 启用 spring session 的支持,使得 httpsession 能够被 spring session 管理。
  2. 配置 redis 作为会话数据的存储后端。
  3. 注册一个 sessionrepositoryfilter 的 bean,这个 filter 负责拦截请求,并将标准的 httpsession 替换为 spring session 的实现。

session的创建存储和获取

做完上面的配置之后,你可以像使用常规 httpsession 一样使用 spring session。每次修改会话时,spring session 都会自动将这些更改同步到 redis。

import org.springframework.web.bind.annotation.getmapping;
import org.springframework.web.bind.annotation.restcontroller;
 
import javax.servlet.http.httpsession;
 
@restcontroller
public class sessioncontroller {
 
    @getmapping("/setsession")
    public string setsession(httpsession session) {
        session.setattribute("message", "hello, redis session!");
        return "session set in redis";
    }
 
    @getmapping("/getsession")
    public string getsession(httpsession session) {
        return "session message: " + session.getattribute("message");
    }
}

以上就是springboot使用redis实现session共享的详细内容,更多关于springboot redis session共享的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com