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
是一个方便的注解,它做了以下几件事情:
- 启用 spring session 的支持,使得 httpsession 能够被 spring session 管理。
- 配置 redis 作为会话数据的存储后端。
- 注册一个
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共享的资料请关注代码网其它相关文章!
发表评论