目录:

1.项目集成redis客户端jedis

引入Jedis pom

2.redis连接池构建及调试

1)JedisPoolConfig源码解析

2)JedisPool源码解析

3)JedisPool回收资源

4)封装redisPool

 public class RedisPool {
//声明成static的原因:保证jedis连接池在tomcat启动时就加载出来
//jedis连接池
private static JedisPool pool;
//与redis连接池连接的最大连接数
private static Integer maxTotal = Integer.parseInt(PropertiesUtil.getProperty("redis.max.total", "20"));
//在这个连接池中最多有多少个状态为idle的jedis实例,jedis连接池里就是jedis的实例,idle就是空闲的jedis实例
//在jedis连接池中最大的idle状态(空闲的)的jedis实例的个数
private static Integer maxIdle = Integer.parseInt(PropertiesUtil.getProperty("redis.max.idle", "10"));
//在jedis连接池中最小的idle状态(空闲的)的jedis实例的个数
private static Integer minIdle = Integer.parseInt(PropertiesUtil.getProperty("redis.min.idle", "2")); //在borrow一个jedis实例的时候,是否要进行验证操作,如果赋值为true,则得到的jedis实例肯定是可用的
private static Boolean testOnBorrow = Boolean.parseBoolean(PropertiesUtil.getProperty("redis.test.borrow", "true"));
//在return一个jedis实例的时候,是否要进行验证操作,如果赋值为true,则返回jedis连接池的jedis实例肯定是可用的
private static Boolean testOnReturn = Boolean.parseBoolean(PropertiesUtil.getProperty("redis.test.return", "true")); private static String redisIp = PropertiesUtil.getProperty("redis.ip");
private static Integer redisPort = Integer.parseInt(PropertiesUtil.getProperty("redis.port")); //初始化连接池,只会调用一次
private static void initPool() {
JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(maxTotal);
config.setMaxIdle(maxIdle);
config.setMinIdle(minIdle); config.setTestOnBorrow(testOnBorrow);
config.setTestOnReturn(testOnReturn); //连接池耗尽的时候,是否阻塞,false会抛出异常,true阻塞直到超时,会抛出超时异常,默认为true
config.setBlockWhenExhausted(true); //这里超时时间是2s
pool = new JedisPool(config, redisIp, redisPort, 1000*2); } static {
initPool();
} //从连接池中拿取一个实例
public static Jedis getJedis() {
return pool.getResource();
} //将正常实例放回jedis连接池
public static void returnResource(Jedis jedis) {
pool.returnResource(jedis);
} //将破损实例放回jedis连接池
public static void returnBrokenResource(Jedis jedis) {
pool.returnResource(jedis);
} //测试是否与redis-server正常连接上
// public static void main(String[] args) {
// Jedis jedis = pool.getResource();
// jedis.set("kkk", "ddd");
// returnResource(jedis);
// pool.destroy();
// System.out.println("end");
// } }

3.jedis API封装及调试

封装RedisPoolUtil

 @Slf4j
public class RedisPoolUtil { //重新设置有效期
//参数只有key和有效期,因为只需要根据key设置有效期即可
public static Long expire(String key, int exTime) {
Jedis jedis = null;
Long result = null;
try {
jedis = RedisPool.getJedis();
//设置有效期
result = jedis.expire(key, exTime);
} catch (Exception e) {
log.error("setex key:{} error", key, e);
RedisPool.returnBrokenResource(jedis);
return result;
}
RedisPool.returnResource(jedis);
return result;
} //exTime单位是s,设置session有效时间
//当用户初次登录的时候,需要设置有限期,存在redis session中
//后续如果用户再次请求登录,则只需要调用expire,重新设置有效期即可
public static String setEx(String key, String value, int exTime) {
Jedis jedis = null;
String result = null;
try {
jedis = RedisPool.getJedis();
result = jedis.setex(key, exTime, value);
} catch (Exception e) {
log.error("setex key:{} value:{} error", key, value, e);
RedisPool.returnBrokenResource(jedis);
return result;
}
RedisPool.returnResource(jedis);
return result;
} public static String set(String key, String value) {
Jedis jedis = null;
//jedis返回的结果
String result = null;
try {
jedis = RedisPool.getJedis();
//设置key-value
result = jedis.set(key, value);
} catch (Exception e) {
log.error("set key:{} value:{} error", key, value, e);
RedisPool.returnBrokenResource(jedis);
return result;
}
RedisPool.returnResource(jedis);
return result;
} public static String get(String key) {
Jedis jedis = null;
String result = null;
try {
jedis = RedisPool.getJedis();
//根据key获取value值
result = jedis.get(key);
} catch (Exception e) {
log.error("set key:{} error", key, e);
RedisPool.returnBrokenResource(jedis);
return result;
}
RedisPool.returnResource(jedis);
return result;
} public static Long del(String key) {
Jedis jedis = null;
Long result = null;
try {
jedis = RedisPool.getJedis();
//根据key删除key-value
result = jedis.del(key);
} catch (Exception e) {
log.error("set key:{} error", key, e);
RedisPool.returnBrokenResource(jedis);
return result;
}
RedisPool.returnResource(jedis);
return result;
} //单步调试上面的各个方法
// public static void main(String[] args) {
// Jedis jedis = RedisPool.getJedis();
// RedisPoolUtil.set("keyTest", "value");
// String value = RedisPoolUtil.get("keyTest");
// RedisPoolUtil.setEx("keyex", "valueex", 60*10);
// RedisPoolUtil.expire("keyTest", 60*20);
// RedisPoolUtil.del("keyTest");
// System.out.println("end");
// }
}

4.json封装jsonUtil及调试

1)封装及调试

2)多泛型(一个对象里面有多个泛型,例如map里面的key-value,value里面是一个set或list,里面又是一个泛型)序列化和反序列化

 public class JsonUtil {
private static ObjectMapper objectMapper = new ObjectMapper();
static {
//序列化
//对象的所有字段全部列入
objectMapper.setSerializationInclusion(Inclusion.ALWAYS);
//取消默认转换timestamps形式
objectMapper.configure(SerializationConfig.Feature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);
//忽略空Bean转json的错误
objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
//所有的日期格式都统一为以下的样式,即yyyy-MM-dd HH:mm:ss
objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT)); //反序列化
//忽略在json字符串中存在,但在java对象中不存在对应属性的情况,防止错误
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
} //对象转json字符串
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字符串
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;
}
} //json字符串转对象
//只能针对一个对象的情况,如果是list,就不适用
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;
}
} //可以针对list的情况,只要第二个参数传入new TypeReference<List<User>>就可以
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的情况,只要传入List.class和User.class就可以了
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;
}
} /* public static void main(String[] args) {
User u1 = new User();
u1.setId(1);
u1.setEmail("kkk@163.com");
User u2 = new User();
u2.setId(2);
u2.setEmail("iii@163.com"); String user1Json = JsonUtil.obj2String(u1);
String user1JsonPretty = JsonUtil.obj2StringPretty(u1); log.info("user1Json:{}", user1Json);
log.info("user1JsonPretty:{}", user1JsonPretty); User user = JsonUtil.string2Obj(user1Json, User.class); List<User> userList = Lists.newArrayList();
userList.add(u1);
userList.add(u2);
String userListStr = JsonUtil.obj2StringPretty(userList); log.info("------------------");
log.info(userListStr); // List<User> userListObj = JsonUtil.string2Obj(userListStr, List.class);
// List<User> userListObj = JsonUtil.string2Obj(userListStr, new TypeReference<List<User>>() {
// });
List<User> userListObj = JsonUtil.string2Obj(userListStr, List.class, User.class);
System.out.println("end");
}*/
}

5.json ObjectMapper源码解析

1)Inclusion.ALWAYS

2)Inclusion.NON_NULL

3)Inclusion.NON_DEFAULT

4)Inclusion.NON_EMPTY

5)SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPSInclusion.NON_NULL

6)SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS

7)DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES

8)ObjectMapper DateFormat

6.cookie封装及使用

1)写cookie

2)读cookie

3)删cookie

4)domain

5)path

6)maxAge

7)httponly

 public class CookieUtil {
//将这个cookie放在一级域名下,则二级域名www.mall.com、user.mall.com等都可以访问到这个cookie,而同级域名是访问不到的
private final static String COOKIE_DOMAIN = ".mall.com";
//这个名字会由服务端种到客户端的浏览器上,
private final static String COOKIE_NAME = "mall_login_token"; //获取cookie
public static String readLoginToken(HttpServletRequest request) {
Cookie[] cks = request.getCookies();
if(cks != null) {
for(Cookie ck : cks) {
log.info("cookieName:{}, cookieValue:{}", ck.getName(), ck.getValue());
if(StringUtils.equals(ck.getName(), COOKIE_NAME)) {
log.info("return cookie_name:{}, cookie_value:{}", ck.getName(), ck.getValue());
return ck.getValue();
}
}
}
return null;
} //X:domain=".mall.com",a,b,c,d,e都能拿到这个cookie
//a,b不能看到彼此的cookie
//a:A.mall.com cookie:domain = A.mall.com;path = "/"
//b:B.mall.com cookie:domain = B.mall.com;path = "/"
//c,d能共享a的cookie,因为domain相同;c,d也能共享e的cookie,因为domain和path
//c,d不能看到彼此的cookie,也不能看到b的cookie
//c:A.mall.com/test/cc cookie:domain = A.mall.com;path = "/test/cc"
//d:A.mall.com/test/dd cookie:domain = A.mall.com;path = "/test/dd"
//e:A.mall.com/test cookie:domain = A.mall.com;path = "/test"
//登录时,写入cookie,这个token就是sessionId
public static void writeLoginToken(HttpServletResponse response, String token) {
Cookie ck = new Cookie(COOKIE_NAME, token);
ck.setDomain(COOKIE_DOMAIN);
//"/"代表设置在根目录,
ck.setPath("/");
//禁止通过脚本访问cookie,可以防止脚本攻击泄露信息
ck.setHttpOnly(true);
//如果是-1,代表永久,单位是s;如果不设置这个变量,则cookie不会写入硬盘,而只是写在内存,值在当前页面有效
ck.setMaxAge(60 * 60 * 24 * 365);
log.info("write cookie_name:{}, cookie_value:{}", ck.getName(), ck.getValue());
response.addCookie(ck);
} //注销时删除cookie
public static void delLoginToken(HttpServletRequest request, HttpServletResponse response) {
Cookie[] cks = request.getCookies();
if(cks != null) {
for(Cookie ck : cks) {
if(StringUtils.equals(ck.getName(), COOKIE_NAME)) {
ck.setDomain(COOKIE_DOMAIN);
ck.setPath("/");
//设置成0,代表删除此cookie
ck.setMaxAge(0);
log.info("del cookie_name:{}, cookie_value:{}", ck.getName(), ck.getValue());
response.addCookie(ck);
return;
}
}
}
}
}

7.SessionExpireFilter重置session有效期

由于用户在第一次登录的时候,会设置session有效期,为了session不过期,在用户每一次请求页面数据的时候,就重置一下session有效期。使用过滤器实现

Filter类:

 public class SessionExpireFilter implements Filter {

     @Override
public void init(FilterConfig filterConfig) throws ServletException { } @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;
String loginToken = CookieUtil.readLoginToken(httpServletRequest);
if(StringUtils.isNotEmpty(loginToken)) {
//判断loginToken是否为空或者""
//如果不为空的话,符合条件,继续拿user信息
String userJsonStr = RedisShardedPoolUtil.get(loginToken);
User user = JsonUtil.string2Obj(userJsonStr, User.class);
if(user != null) {
//如果user不为空,则重置session的时间,即调用expire命令
RedisShardedPoolUtil.expire(loginToken, Const.RedisCacheExtime.REDIS_SESSION_EXTIME);
}
}
filterChain.doFilter(servletRequest, servletResponse);
} @Override
public void destroy() { }
}

web.xml配置过滤器:

     <filter>
<filter-name>sessionExpireFilter</filter-name>
<filter-class>com.mall.controller.common.SessionExpireFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sessionExpireFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>

8.用户session相关模块重构

9.Guava cache迁移redis缓存

 public class JsonUtil {
private static ObjectMapper objectMapper = new ObjectMapper();
static {
//序列化
//对象的所有字段全部列入
objectMapper.setSerializationInclusion(Inclusion.ALWAYS);
//取消默认转换timestamps形式
objectMapper.configure(SerializationConfig.Feature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);
//忽略空Bean转json的错误
objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
//所有的日期格式都统一为以下的样式,即yyyy-MM-dd HH:mm:ss
objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT)); //反序列化
//忽略在json字符串中存在,但在java对象中不存在对应属性的情况,防止错误
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
} //对象转json字符串
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字符串
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;
}
} //json字符串转对象
//只能针对一个对象的情况,如果是list,就不适用
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;
}
} //可以针对list的情况,只要第二个参数传入new TypeReference<List<User>>就可以
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的情况,只要传入List.class和User.class就可以了
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;
}
} /* public static void main(String[] args) {
User u1 = new User();
u1.setId(1);
u1.setEmail("kkk@163.com");
User u2 = new User();
u2.setId(2);
u2.setEmail("iii@163.com"); String user1Json = JsonUtil.obj2String(u1);
String user1JsonPretty = JsonUtil.obj2StringPretty(u1); log.info("user1Json:{}", user1Json);
log.info("user1JsonPretty:{}", user1JsonPretty); User user = JsonUtil.string2Obj(user1Json, User.class); List<User> userList = Lists.newArrayList();
userList.add(u1);
userList.add(u2);
String userListStr = JsonUtil.obj2StringPretty(userList); log.info("------------------");
log.info(userListStr); // List<User> userListObj = JsonUtil.string2Obj(userListStr, List.class);
// List<User> userListObj = JsonUtil.string2Obj(userListStr, new TypeReference<List<User>>() {
// });
List<User> userListObj = JsonUtil.string2Obj(userListStr, List.class, User.class);
System.out.println("end");
}*/
}

redis+cookie+json+filter实现单点登录的更多相关文章

  1. .NET Core2.0+MVC 用Redis/Memory+cookie实现的sso单点登录

    之前发布过使用session+cookie实现的单点登录,博主个人用的很不舒服,为什么呢,博主自己测试的时候,通过修改host的方法,在本机发布了三个站点,但是,经过测试,发现,三个站点使用的sess ...

  2. Asp.Net Core基于Cookie实现同域单点登录(SSO)

    在同一个域名下有很多子系统 如:a.giant.com  b.giant.com   c.giant.com等 但是这些系统都是giant.com这个子域. 这样的情况就可以在不引用其它框架的情况下, ...

  3. 基于SpringBoot+Redis的Session共享与单点登录

    title: 基于SpringBoot+Redis的Session共享与单点登录 date: 2019-07-23 02:55:52 categories: 架构 author: mrzhou tag ...

  4. NET Core 2.0使用Cookie认证实现SSO单点登录

    NET Core 2.0使用Cookie认证实现SSO单点登录 之前写了一个使用ASP.NET MVC实现SSO登录的Demo,https://github.com/bidianqing/SSO.Sa ...

  5. 前端分享之cookie的使用及单点登录

    cookie是什么 cookie的英文意思是饼干.在计算机术语中指服务端存放在客户端的一段数据.这段数据在客户端每次进行http请求时会自动加在http请求报文中的header上:服务端在响应时,可以 ...

  6. 基于Cookie跨域的单点登录问题

    由于项目中,需要用的单点登录,主要的思路是:系统1:用户名密码-->写入Cookie-->其他系统读取Cookie. 1.在同一个服务器下的Cookie共享 @Component(&quo ...

  7. .NET Core2.0+MVC 用session,cookie实现的sso单点登录

    博主刚接触.NET Core2.0,想做一个单点登录的demo,所以参考了一些资料,这里给上链接: 1.http://www.cnblogs.com/baibaomen/p/sso-sequence- ...

  8. CORS跨域、Cookie传递SessionID实现单点登录后的权限认证的移动端兼容性测试报告

    简述 本文仅记录如标题所述场景的测试所得,由于场景有些特殊,且并不需兼容所有浏览器,所以本文的内容对读者也许并无作用,仅为记录. 场景.与实现 需在移动端单点登录 需在移动端跨域访问我们的服务 基于历 ...

  9. 使用Cookie实现跨域单点登录的原理

    对于构建分布式系统来说业务功能的物理部署会随着新业务模块的增加而增加或改变物理部署的位置.而每个用户都有统一的帐号作为我们登录系统时的一个认证.当新业务或子系统部署在不同的物理机上,我们去访问不同的业 ...

随机推荐

  1. Java内存区域介绍

    Java虚拟机把内存划分成几个区域,每个区域都有各自的职责.下面将逐一分析每个区域. 有助于我们了解,每个方法,变量,对象等都去哪儿了! 程序计数器: 它占用一块很小的内存空间,可以看作是当前线程所执 ...

  2. Debug快捷键

    Debug快捷键 1. F5单步调试进入函数内部2. F6单步调试不进入函数内部3. F7由函数内部返回到调用处4. F8一直执行到下一个断点5. F11 重新运行debug

  3. Javascript面向对象三大特性(封装性、继承性、多态性)详解及创建对象的各种方法

    Javascript基于对象的三大特征和C++,Java面向对象的三大特征一样,都是封装(encapsulation).继承(inheritance )和多态(polymorphism ).只不过实现 ...

  4. Git 自动补全

    如果你用的是 Bash shell,可以试试看 Git 提供的自动补全脚本. http://git-scm.com/download 下载 Git 的源代码,进入contrib/completion  ...

  5. 【线段树】【P2572】【SCOI2010】序列操作

    Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 ...

  6. oracle、mysql、db2三大数据库分页方法的整理

    最近项目中经常会涉及到代码中支持三种数据库的分页的功能,自己整理了关于三种数据库的分页的写法,分享给大家,以供大家使用.希望能帮到更多的码友! 先来看一个代码片段: String page = ala ...

  7. lnmp集成环境Access Denied的问题

    在你的php.ini配置文件中,设置cgi.fix_pathinfo=1

  8. [Java多线程]-ThreadLocal源码及原理的深入分析

    ThreadLocal<T>类:以空间换时间提供一种多线程更快捷访问变量的方式.这种方式不存在竞争,所以也不存在并发的安全性问题. //-------------------------- ...

  9. windows中apache+tomcat整合,使php和java项目能够独立运行

    一.下载和安装 1.安装php  网上有安装教程,不再赘述 2.安装apache 比如安装目录为e:\apache;  项目根目录为e:\www;   网上有安装教程,不再赘述 3.安装jdk  不再 ...

  10. Vue.js随笔二(新建路由+component+添加样式+变量的显示)

    创建一个页面: 1.首先让我们看一下整个vue.js的目录,如下图所示: 2.现在让我们创建一个页面吧: 2-1首先你需要新建路由(就和建立一个如何找到项目文件的目录一个意思):进入src/route ...