目录:

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. bzoj1031-字符加密

    环的问题,经典方法倍长串,求出后缀数组,扫一次sa,如果sa[i]小于等于n,那么就输出这个字符串结尾的位置(即s[sa[i]+n-1]). 代码 #include<cstdio> #in ...

  2. jmeter之JDBC的使用

     一.配置JDBC Request 1.添加需要的驱动jar包 使用不同的数据库,我们需要引入不同的jar包. 方式1:直接将jar包复制到jmeter的lib目录 mysql数据库:下载mysql- ...

  3. 【刷题】BZOJ 4566 [Haoi2016]找相同字符

    Description 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. Input 两行,两个字符串s1,s2,长度分别为 ...

  4. 利用Runloop优化流畅度

    我们可以对runloop添加观察者,当观察到状态为kCFRunLoopExit,kCFRunLoopBeforeWaiting的时候,做一些耗时的处理,废话不说,直接上代码 - (void)viewD ...

  5. POJ.3468 A Simple Problem with Integers(线段树 区间更新 区间查询)

    POJ.3468 A Simple Problem with Integers(线段树 区间更新 区间查询) 题意分析 注意一下懒惰标记,数据部分和更新时的数字都要是long long ,别的没什么大 ...

  6. apue.3e 的安装 (基于ubuntu12.0.4)

    本菜刚刚学习UNIX下高级编程,无奈搭建本书编程环境时遇到不少问题.幸好网上有各种大神的解决办法让我最终解决了问题.在这里感谢为LINUX开源操作系统奋斗的大神. 不过话说回来,网上大都是针对UNIX ...

  7. LibreOJ #539. 「LibreOJ NOIP Round #1」旅游路线(倍增+二分)

    哎一开始看错题了啊T T...最近状态一直不对...最近很多傻逼题都不会写了T T 考虑距离较大肯定不能塞进状态...钱数<=n^2能够承受, 油量再塞就不行了...显然可以预处理出点i到j走c ...

  8. bnuoj53075 外挂使用拒绝

    题目链接:http://www.bnuoj.com/v3/problem_show.php?pid=53075 第一次给校赛出题,来为自己的题目写一发题解吧. 其实我原本的题意非常简洁: 结果被另一位 ...

  9. [Z3001] connection to database 'zabbix' failed: [1045] Access denied for user 'zabbix'@'localhost' (using password: YES)

    在配置了zabbix服务端后,发现:“zabbix server is running”的Value值是“no”, 用:netstat -atnlp|grep 10051 发现没有出现zabbix_s ...

  10. 清除localstorage

    h5本地存储localStorage,sessionStorage. localStorage是没有失效时间的,sessionStorage的声明周期是浏览器的生命周期. 当浏览器关闭时,sessio ...