redis+cookie+json+filter實現單點登陸

目錄:java

1.項目集成redis客戶端jedisweb

引入Jedis pomredis

2.redis鏈接池構建及調試json

1)JedisPoolConfig源碼解析瀏覽器

2)JedisPool源碼解析緩存

3)JedisPool回收資源tomcat

4)封裝redisPoolcookie

 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封裝及調試session

封裝RedisPoolUtilapp

 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
相關文章
相關標籤/搜索