redis+cookie+json+filter实现单点登录

目录:

1.项目集成redis客户端jedis

引入Jedis pom

2.redis连接池构建及调试

1)JedisPoolConfig源码解析

2)JedisPool源码解析

3)JedisPool回收资源

4)封装redisPool

 1 public class RedisPool {
 2     //声明成static的原因:保证jedis连接池在tomcat启动时就加载出来
 3     //jedis连接池
 4     private static JedisPool pool;
 5     //与redis连接池连接的最大连接数
 6     private static Integer maxTotal = Integer.parseInt(PropertiesUtil.getProperty("redis.max.total", "20"));
 7     //在这个连接池中最多有多少个状态为idle的jedis实例,jedis连接池里就是jedis的实例,idle就是空闲的jedis实例
 8     //在jedis连接池中最大的idle状态(空闲的)的jedis实例的个数
 9     private static Integer maxIdle = Integer.parseInt(PropertiesUtil.getProperty("redis.max.idle", "10"));
10     //在jedis连接池中最小的idle状态(空闲的)的jedis实例的个数
11     private static Integer minIdle = Integer.parseInt(PropertiesUtil.getProperty("redis.min.idle", "2"));
12 
13     //在borrow一个jedis实例的时候,是否要进行验证操作,如果赋值为true,则得到的jedis实例肯定是可用的
14     private static Boolean testOnBorrow = Boolean.parseBoolean(PropertiesUtil.getProperty("redis.test.borrow", "true"));
15     //在return一个jedis实例的时候,是否要进行验证操作,如果赋值为true,则返回jedis连接池的jedis实例肯定是可用的
16     private static Boolean testOnReturn = Boolean.parseBoolean(PropertiesUtil.getProperty("redis.test.return", "true"));
17 
18     private static String redisIp = PropertiesUtil.getProperty("redis.ip");
19     private static Integer redisPort = Integer.parseInt(PropertiesUtil.getProperty("redis.port"));
20 
21     //初始化连接池,只会调用一次
22     private static void initPool() {
23         JedisPoolConfig config = new JedisPoolConfig();
24 
25         config.setMaxTotal(maxTotal);
26         config.setMaxIdle(maxIdle);
27         config.setMinIdle(minIdle);
28 
29         config.setTestOnBorrow(testOnBorrow);
30         config.setTestOnReturn(testOnReturn);
31 
32         //连接池耗尽的时候,是否阻塞,false会抛出异常,true阻塞直到超时,会抛出超时异常,默认为true
33         config.setBlockWhenExhausted(true);
34 
35         //这里超时时间是2s
36         pool = new JedisPool(config, redisIp, redisPort, 1000*2);
37 
38     }
39 
40     static {
41         initPool();
42     }
43 
44     //从连接池中拿取一个实例
45     public static Jedis getJedis() {
46         return pool.getResource();
47     }
48 
49     //将正常实例放回jedis连接池
50     public static void returnResource(Jedis jedis) {
51         pool.returnResource(jedis);
52     }
53 
54     //将破损实例放回jedis连接池
55     public static void returnBrokenResource(Jedis jedis) {
56         pool.returnResource(jedis);
57     }
58 
59     //测试是否与redis-server正常连接上
60 //    public static void main(String[] args) {
61 //        Jedis jedis = pool.getResource();
62 //        jedis.set("kkk", "ddd");
63 //        returnResource(jedis);
64 //        pool.destroy();
65 //        System.out.println("end");
66 //    }
67 
68 }
View Code

3.jedis API封装及调试

封装RedisPoolUtil

 1 @Slf4j
 2 public class RedisPoolUtil {
 3 
 4     //重新设置有效期
 5     //参数只有key和有效期,因为只需要根据key设置有效期即可
 6     public static Long expire(String key, int exTime) {
 7         Jedis jedis = null;
 8         Long result = null;
 9         try {
10             jedis = RedisPool.getJedis();
11             //设置有效期
12             result = jedis.expire(key, exTime);
13         } catch (Exception e) {
14             log.error("setex key:{} error", key, e);
15             RedisPool.returnBrokenResource(jedis);
16             return result;
17         }
18         RedisPool.returnResource(jedis);
19         return result;
20     }
21 
22     //exTime单位是s,设置session有效时间
23     //当用户初次登录的时候,需要设置有限期,存在redis session中
24     //后续如果用户再次请求登录,则只需要调用expire,重新设置有效期即可
25     public static String setEx(String key, String value, int exTime) {
26         Jedis jedis = null;
27         String result = null;
28         try {
29             jedis = RedisPool.getJedis();
30             result = jedis.setex(key, exTime, value);
31         } catch (Exception e) {
32             log.error("setex key:{} value:{} error", key, value, e);
33             RedisPool.returnBrokenResource(jedis);
34             return result;
35         }
36         RedisPool.returnResource(jedis);
37         return result;
38     }
39 
40     public static String set(String key, String value) {
41         Jedis jedis = null;
42         //jedis返回的结果
43         String result = null;
44         try {
45             jedis = RedisPool.getJedis();
46             //设置key-value
47             result = jedis.set(key, value);
48         } catch (Exception e) {
49             log.error("set key:{} value:{} error", key, value, e);
50             RedisPool.returnBrokenResource(jedis);
51             return result;
52         }
53         RedisPool.returnResource(jedis);
54         return result;
55     }
56 
57     public static String get(String key) {
58         Jedis jedis = null;
59         String result = null;
60         try {
61             jedis = RedisPool.getJedis();
62             //根据key获取value值
63             result = jedis.get(key);
64         } catch (Exception e) {
65             log.error("set key:{} error", key, e);
66             RedisPool.returnBrokenResource(jedis);
67             return result;
68         }
69         RedisPool.returnResource(jedis);
70         return result;
71     }
72 
73     public static Long del(String key) {
74         Jedis jedis = null;
75         Long result = null;
76         try {
77             jedis = RedisPool.getJedis();
78             //根据key删除key-value
79             result = jedis.del(key);
80         } catch (Exception e) {
81             log.error("set key:{} error", key, e);
82             RedisPool.returnBrokenResource(jedis);
83             return result;
84         }
85         RedisPool.returnResource(jedis);
86         return result;
87     }
88 
89     //单步调试上面的各个方法
90 //    public static void main(String[] args) {
91 //        Jedis jedis = RedisPool.getJedis();
92 //        RedisPoolUtil.set("keyTest", "value");
93 //        String value = RedisPoolUtil.get("keyTest");
94 //        RedisPoolUtil.setEx("keyex", "valueex", 60*10);
95 //        RedisPoolUtil.expire("keyTest", 60*20);
96 //        RedisPoolUtil.del("keyTest");
97 //        System.out.println("end");
98 //    }
99 }
View Code

4.json封装jsonUtil及调试

1)封装及调试

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

  1 public class JsonUtil {
  2     private static ObjectMapper objectMapper = new ObjectMapper();
  3     static {
  4         //序列化
  5         //对象的所有字段全部列入
  6         objectMapper.setSerializationInclusion(Inclusion.ALWAYS);
  7         //取消默认转换timestamps形式
  8         objectMapper.configure(SerializationConfig.Feature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);
  9         //忽略空Bean转json的错误
 10         objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
 11         //所有的日期格式都统一为以下的样式,即yyyy-MM-dd HH:mm:ss
 12         objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT));
 13 
 14         //反序列化
 15         //忽略在json字符串中存在,但在java对象中不存在对应属性的情况,防止错误
 16         objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
 17     }
 18 
 19     //对象转json字符串
 20     public static <T> String obj2String(T obj) {
 21         if(obj == null) {
 22             return null;
 23         }
 24         try {
 25             return obj instanceof String ? (String)obj : objectMapper.writeValueAsString(obj);
 26         } catch (Exception e) {
 27             log.warn("Parse object to String error", e);
 28             return null;
 29         }
 30     }
 31 
 32     //返回格式化好的json字符串
 33     public static <T> String obj2StringPretty(T obj) {
 34         if(obj == null) {
 35             return null;
 36         }
 37         try {
 38             return obj instanceof String ? (String)obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
 39         } catch (Exception e) {
 40             log.warn("Parse Object to String error", e);
 41             return null;
 42         }
 43     }
 44 
 45     //json字符串转对象
 46     //只能针对一个对象的情况,如果是list,就不适用
 47     public static <T> T string2Obj(String str, Class<T> clazz) {
 48         if(StringUtils.isEmpty(str) || clazz == null) {
 49             return null;
 50         }
 51         try {
 52             return clazz.equals(String.class) ? (T)str : objectMapper.readValue(str, clazz);
 53         } catch (Exception e) {
 54             log.warn("Parse String to Object error", e);
 55             return null;
 56         }
 57     }
 58 
 59     //可以针对list的情况,只要第二个参数传入new TypeReference<List<User>>就可以
 60     public static <T> T string2Obj(String str, TypeReference<T> typeReference) {
 61         if(StringUtils.isEmpty(str) || typeReference == null) {
 62             return null;
 63         }
 64         try {
 65             return (T)(typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference));
 66         } catch (Exception e) {
 67             log.warn("Parse String to Object error", e);
 68             return null;
 69         }
 70     }
 71 
 72     //可以针对List的情况,只要传入List.class和User.class就可以了
 73     public static <T> T string2Obj(String str,Class<?> collectionClass,Class<?>... elementClasses){
 74         JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass,elementClasses);
 75         try {
 76             return objectMapper.readValue(str,javaType);
 77         } catch (Exception e) {
 78             log.warn("Parse String to Object error",e);
 79             return null;
 80         }
 81     }
 82 
 83 /*    public static void main(String[] args) {
 84         User u1 = new User();
 85         u1.setId(1);
 86         u1.setEmail("kkk@163.com");
 87         User u2 = new User();
 88         u2.setId(2);
 89         u2.setEmail("iii@163.com");
 90 
 91         String user1Json = JsonUtil.obj2String(u1);
 92         String user1JsonPretty = JsonUtil.obj2StringPretty(u1);
 93 
 94         log.info("user1Json:{}", user1Json);
 95         log.info("user1JsonPretty:{}", user1JsonPretty);
 96 
 97         User user = JsonUtil.string2Obj(user1Json, User.class);
 98 
 99         List<User> userList = Lists.newArrayList();
100         userList.add(u1);
101         userList.add(u2);
102         String userListStr = JsonUtil.obj2StringPretty(userList);
103 
104         log.info("------------------");
105         log.info(userListStr);
106 
107   //      List<User> userListObj = JsonUtil.string2Obj(userListStr, List.class);
108  //       List<User> userListObj = JsonUtil.string2Obj(userListStr, new TypeReference<List<User>>() {
109  //       });
110         List<User> userListObj = JsonUtil.string2Obj(userListStr, List.class, User.class);
111         System.out.println("end");
112     }*/
113 }
View Code

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

 1 public class CookieUtil {
 2     //将这个cookie放在一级域名下,则二级域名www.mall.com、user.mall.com等都可以访问到这个cookie,而同级域名是访问不到的
 3     private final static String COOKIE_DOMAIN = ".mall.com";
 4     //这个名字会由服务端种到客户端的浏览器上,
 5     private final static String COOKIE_NAME = "mall_login_token";
 6 
 7     //获取cookie
 8     public static String readLoginToken(HttpServletRequest request) {
 9         Cookie[] cks = request.getCookies();
10         if(cks != null) {
11             for(Cookie ck : cks) {
12                 log.info("cookieName:{}, cookieValue:{}", ck.getName(), ck.getValue());
13                 if(StringUtils.equals(ck.getName(), COOKIE_NAME)) {
14                     log.info("return cookie_name:{}, cookie_value:{}", ck.getName(), ck.getValue());
15                     return ck.getValue();
16                 }
17             }
18         }
19         return null;
20     }
21 
22     //X:domain=".mall.com",a,b,c,d,e都能拿到这个cookie
23     //a,b不能看到彼此的cookie
24     //a:A.mall.com      cookie:domain = A.mall.com;path = "/"
25     //b:B.mall.com      cookie:domain = B.mall.com;path = "/"
26     //c,d能共享a的cookie,因为domain相同;c,d也能共享e的cookie,因为domain和path
27     //c,d不能看到彼此的cookie,也不能看到b的cookie
28     //c:A.mall.com/test/cc      cookie:domain = A.mall.com;path = "/test/cc"
29     //d:A.mall.com/test/dd      cookie:domain = A.mall.com;path = "/test/dd"
30     //e:A.mall.com/test     cookie:domain = A.mall.com;path = "/test"
31     //登录时,写入cookie,这个token就是sessionId
32     public static void writeLoginToken(HttpServletResponse response, String token) {
33         Cookie ck = new Cookie(COOKIE_NAME, token);
34         ck.setDomain(COOKIE_DOMAIN);
35         //"/"代表设置在根目录,
36         ck.setPath("/");
37         //禁止通过脚本访问cookie,可以防止脚本攻击泄露信息
38         ck.setHttpOnly(true);
39         //如果是-1,代表永久,单位是s;如果不设置这个变量,则cookie不会写入硬盘,而只是写在内存,值在当前页面有效
40         ck.setMaxAge(60 * 60 * 24 * 365);
41         log.info("write cookie_name:{}, cookie_value:{}", ck.getName(), ck.getValue());
42         response.addCookie(ck);
43     }
44 
45     //注销时删除cookie
46     public static void delLoginToken(HttpServletRequest request,  HttpServletResponse response) {
47         Cookie[] cks = request.getCookies();
48         if(cks != null) {
49             for(Cookie ck : cks) {
50                 if(StringUtils.equals(ck.getName(), COOKIE_NAME)) {
51                     ck.setDomain(COOKIE_DOMAIN);
52                     ck.setPath("/");
53                     //设置成0,代表删除此cookie
54                     ck.setMaxAge(0);
55                     log.info("del cookie_name:{}, cookie_value:{}", ck.getName(), ck.getValue());
56                     response.addCookie(ck);
57                     return;
58                 }
59             }
60         }
61     }
62 }
View Code

7.SessionExpireFilter重置session有效期

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

Filter类:

 1 public class SessionExpireFilter implements Filter {
 2 
 3     @Override
 4     public void init(FilterConfig filterConfig) throws ServletException {
 5 
 6     }
 7 
 8     @Override
 9     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
10         HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;
11         String loginToken = CookieUtil.readLoginToken(httpServletRequest);
12         if(StringUtils.isNotEmpty(loginToken)) {
13             //判断loginToken是否为空或者""
14             //如果不为空的话,符合条件,继续拿user信息
15             String userJsonStr = RedisShardedPoolUtil.get(loginToken);
16             User user = JsonUtil.string2Obj(userJsonStr, User.class);
17             if(user != null) {
18                 //如果user不为空,则重置session的时间,即调用expire命令
19                 RedisShardedPoolUtil.expire(loginToken, Const.RedisCacheExtime.REDIS_SESSION_EXTIME);
20             }
21         }
22         filterChain.doFilter(servletRequest, servletResponse);
23     }
24 
25     @Override
26     public void destroy() {
27 
28     }
29 }
View Code

web.xml配置过滤器:

1     <filter>
2         <filter-name>sessionExpireFilter</filter-name>
3         <filter-class>com.mall.controller.common.SessionExpireFilter</filter-class>
4     </filter>
5     <filter-mapping>
6         <filter-name>sessionExpireFilter</filter-name>
7         <url-pattern>*.do</url-pattern>
8     </filter-mapping>
View Code

8.用户session相关模块重构 

9.Guava cache迁移redis缓存

  1 public class JsonUtil {
  2     private static ObjectMapper objectMapper = new ObjectMapper();
  3     static {
  4         //序列化
  5         //对象的所有字段全部列入
  6 objectMapper.setSerializationInclusion(Inclusion.ALWAYS);
  7 //取消默认转换timestamps形式
  8 objectMapper.configure(SerializationConfig.Feature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);
  9 //忽略空Bean转json的错误
 10 objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
 11 //所有的日期格式都统一为以下的样式,即yyyy-MM-dd HH:mm:ss
 12 objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT));
 13 
 14 //反序列化
 15         //忽略在json字符串中存在,但在java对象中不存在对应属性的情况,防止错误
 16 objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
 17 }
 18 
 19     //对象转json字符串
 20 public static <T> String obj2String(T obj) {
 21         if(obj == null) {
 22             return null;
 23 }
 24         try {
 25             return obj instanceof String ? (String)obj : objectMapper.writeValueAsString(obj);
 26 } catch (Exception e) {
 27             log.warn("Parse object to String error", e);
 28             return null;
 29 }
 30     }
 31 
 32     //返回格式化好的json字符串
 33 public static <T> String obj2StringPretty(T obj) {
 34         if(obj == null) {
 35             return null;
 36 }
 37         try {
 38             return obj instanceof String ? (String)obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
 39 } catch (Exception e) {
 40             log.warn("Parse Object to String error", e);
 41             return null;
 42 }
 43     }
 44 
 45     //json字符串转对象
 46     //只能针对一个对象的情况,如果是list,就不适用
 47 public static <T> T string2Obj(String str, Class<T> clazz) {
 48         if(StringUtils.isEmpty(str) || clazz == null) {
 49             return null;
 50 }
 51         try {
 52             return clazz.equals(String.class) ? (T)str : objectMapper.readValue(str, clazz);
 53 } catch (Exception e) {
 54             log.warn("Parse String to Object error", e);
 55             return null;
 56 }
 57     }
 58 
 59     //可以针对list的情况,只要第二个参数传入new TypeReference<List<User>>就可以
 60 public static <T> T string2Obj(String str, TypeReference<T> typeReference) {
 61         if(StringUtils.isEmpty(str) || typeReference == null) {
 62             return null;
 63 }
 64         try {
 65             return (T)(typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference));
 66 } catch (Exception e) {
 67             log.warn("Parse String to Object error", e);
 68             return null;
 69 }
 70     }
 71 
 72     //可以针对List的情况,只要传入List.class和User.class就可以了
 73 public static <T> T string2Obj(String str,Class<?> collectionClass,Class<?>... elementClasses){
 74         JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass,elementClasses);
 75         try {
 76             return objectMapper.readValue(str,javaType);
 77 } catch (Exception e) {
 78             log.warn("Parse String to Object error",e);
 79             return null;
 80 }
 81     }
 82 
 83 /*    public static void main(String[] args) {
 84         User u1 = new User();
 85         u1.setId(1);
 86         u1.setEmail("kkk@163.com");
 87         User u2 = new User();
 88         u2.setId(2);
 89         u2.setEmail("iii@163.com");
 90 
 91         String user1Json = JsonUtil.obj2String(u1);
 92         String user1JsonPretty = JsonUtil.obj2StringPretty(u1);
 93 
 94         log.info("user1Json:{}", user1Json);
 95         log.info("user1JsonPretty:{}", user1JsonPretty);
 96 
 97         User user = JsonUtil.string2Obj(user1Json, User.class);
 98 
 99         List<User> userList = Lists.newArrayList();
100         userList.add(u1);
101         userList.add(u2);
102         String userListStr = JsonUtil.obj2StringPretty(userList);
103 
104         log.info("------------------");
105         log.info(userListStr);
106 
107   //      List<User> userListObj = JsonUtil.string2Obj(userListStr, List.class);
108  //       List<User> userListObj = JsonUtil.string2Obj(userListStr, new TypeReference<List<User>>() {
109  //       });
110         List<User> userListObj = JsonUtil.string2Obj(userListStr, List.class, User.class);
111         System.out.println("end");
112     }*/
113 }
View Code

 

posted on 2018-06-04 10:11  二十年后20  阅读(505)  评论(0编辑  收藏  举报

导航