zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    c# 抽象类(abstract)
    c# 虚方法(virtual)与 多态(Polymorphism)
    02.JavaScript基础上
    第 12 章 Ajax
    第 11 章 动画效果
    第 10 章 高级事件
    第 9 章 事件对象
    第 8 章 基础事件
    第 7 章 表单选择器
    第6章 DOM节点操作
  • 原文地址:https://www.cnblogs.com/cing/p/9131980.html
Copyright © 2011-2022 走看看