Nginx负载环境下Java的CAS 4.0 Token(TGT)使用存入Redis集群存储

应用服务 01/17 阅读 413 views次 人气 0
摘要:

Nginx负载,Tomcat使用Redis实现Http Session共享。

Nginx conf:

如何Tomcat 不做SESSION共享可以开启IP_HASH.

upstream tomcat {
    ##ip_hash;
    server 200.10.10.67:8110;
    server 200.10.10.67:8120;
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Tomcat Session 参照【1】

–注意jar版本号

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

CAS 相应开发配置

Pom.xml 配置:

相应的POM文件引入dependency

<dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.8.1</version>
        </dependency>

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ticketRegistry.xml配置内容

index=0 是redis 集群地址

index=1 是TGT 过期时间

index=2 是TGT 过期时间

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <description>
        Configuration for the default TicketRegistry which stores the tickets in-memory and cleans them out as specified intervals.
    </description>

  <!-- Ticket Registry -->
    <bean id="ticketRegistry" class="org.jasig.cas.ticket.registry.RedisTicketRegistry">
            <constructor-arg index="0" value="Centos6701:6379,Centos6701:6380,Centos6702:6380,Centos6702:6379,Centos6703:6380,Centos6703:6379" />
            <constructor-arg index="1" value="28800" />
            <constructor-arg index="2" value="200" />
    </bean>
    </beans>

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Redis 存储Token

RedisTicketRegistry.java
/* * Licensed to Jasig under one or more contributor license * agreements. See the NOTICE file distributed with this work * for additional information regarding copyright ownership. * Jasig licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a * copy of the License at the following location: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */
package org.jasig.cas.ticket.registry;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.validation.constraints.Min;

import org.jasig.cas.ticket.ServiceTicket;
import org.jasig.cas.ticket.Ticket;
import org.jasig.cas.ticket.TicketGrantingTicket;
import org.jasig.cas.ticket.registry.util.RedisTools;
import org.jasig.cas.ticket.registry.util.serialize.ObjectsTranscoder;

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;

/** * Key-value ticket registry implementation that stores tickets in redis keyed on the ticket ID. * * @author lumz * @author 2016-7-12 10:17:30 * @since 1.0 */
public final class RedisTicketRegistry extends AbstractDistributedTicketRegistry {


    private JedisCluster jedisCluster;
    private RedisTools handle;
    private String redisServers;
    /** * TGT cache entry timeout in seconds. */
    @Min(0)
    private final int tgtTimeout;

    /** * ST cache entry timeout in seconds. */
    @Min(0)
    private final int stTimeout;

    public RedisTicketRegistry(String redisServers,int tgtTimeout,int stTimeout){
        logger.info("CoreRdisTicketRegistry {true} Init Redis Service : " + redisServers);
        this.redisServers = redisServers;
        this.tgtTimeout=tgtTimeout;
        this.stTimeout=stTimeout;
        Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();

        for (String server : redisServers.split(",")) {
            jedisClusterNodes.add(new HostAndPort(server.split(":")[0], Integer
                    .parseInt(server.split(":")[1])));
        }
        jedisCluster = new JedisCluster(jedisClusterNodes);
        handle = new RedisTools(jedisCluster);
        logger.info("Init Redis Service sucess!");
        logger.info("---------------------------CoreRdisTicketRegistry {true} ----------------------------=" + redisServers);
    }
    @Override
    public void addTicket(Ticket ticket) {
        logger.debug("Adding ticket {}", ticket);
        try {

            /*reidsTemplate.opsForValue().set(ticket.getId(),ticket, getTimeout(ticket), TimeUnit.SECONDS);*/
            //序列化
            ObjectsTranscoder<Ticket> objTranscoder =  new ObjectsTranscoder<Ticket>();
            byte[] result1 = objTranscoder.serialize(ticket);
// Ticket userA_userA = objTranscoder.deserialize(result1); 
            handle.set(ticket.getId().getBytes(), result1, getTimeout(ticket));
            logger.info("---------------------------CoreRdisTicketRegistry { add ticket } -----------------------------" + ticket.getId());
        } catch (final Exception e) {
            logger.error("Failed adding {}", ticket, e);
        }
    }

    @Override
    public Ticket getTicket(String ticketId) {
         Ticket tic =null;
         try {

                /*final Ticket t = (Ticket) this.reidsTemplate.opsForValue().get(ticketId);*/
                String defaultvalue = "";
                byte[] result = this.handle.get(ticketId.getBytes(), defaultvalue.getBytes());
                if ("".equals(result)) {
                    return null;
                } 
                //序列化
                ObjectsTranscoder<Ticket> objTranscoder =  new ObjectsTranscoder<Ticket>();
                tic = objTranscoder.deserialize(result); 
                if (tic != null) {
                    logger.debug("Ticket [{}] found in registry.", ticketId);
                   /* return getProxiedTicketInstance(tic);*/
                }
                logger.info("---------------------------CoreRdisTicketRegistry { GET ticket } -----------------------------" + ticketId);
            } catch (final Exception e) {
                logger.error("Failed fetching {} ", ticketId, e);
            }
            return tic;
    }

    @Override
    public boolean deleteTicket(String ticketId) {
         logger.debug("Deleting ticket {}", ticketId);
            try {

                  /*this.reidsTemplate.delete(ticketId);*/
                 this.handle.del(ticketId);
                 logger.info("---------------------------CoreRdisTicketRegistry { DELETE ticket } -----------------------------" + ticketId);
                 return true;
            } catch (final Exception e) {
                logger.error("Failed deleting {}", ticketId, e);
            }
            return false;
    }

    @Override
    public Collection<Ticket> getTickets() {
         throw new UnsupportedOperationException("GetTickets not supported.");
    }

    @Override
    protected void updateTicket(Ticket ticket) {
     logger.debug("Updating ticket {}", ticket);
        try {

              /*this.reidsTemplate.delete(ticket.getId()); reidsTemplate.opsForValue().set(ticket.getId(),ticket, getTimeout(ticket), TimeUnit.SECONDS);*/
            this.handle.del(ticket.getId());
            //序列化
            ObjectsTranscoder<Ticket> objTranscoder =  new ObjectsTranscoder<Ticket>();
            byte[] result1 = objTranscoder.serialize(ticket);
            handle.set(ticket.getId(), result1.toString(), getTimeout(ticket));
            logger.info("---------------------------CoreRdisTicketRegistry { UPDATE ticket } -----------------------------" + ticket.getId());
        } catch (final Exception e) {
            logger.error("Failed updating {}", ticket, e);
        }
    }

    @Override
    protected boolean needsCallback() {
        // TODO Auto-generated method stub
        return true;
    }
    private int getTimeout(final Ticket t) {
        if (t instanceof TicketGrantingTicket) {
            return this.tgtTimeout;
        } else if (t instanceof ServiceTicket) {
            return this.stTimeout;
        }
        throw new IllegalArgumentException("Invalid ticket type");
    }
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

序列化

/** * */
package org.jasig.cas.ticket.registry.util.serialize;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import org.apache.log4j.Logger;
/** * @author lumz * */
public class ObjectsTranscoder<M extends Serializable> extends SerializeTranscoder {
    protected static Logger logger = Logger.getLogger(SerializeTranscoder.class);
    /* (non-Javadoc) * @see org.jasig.cas.ticket.registry.util.serialize.SerializeTranscoder#serialize(java.lang.Object) */
    @SuppressWarnings("unchecked")
      @Override
      public byte[] serialize(Object value) {
        if (value == null) {  
          throw new NullPointerException("Can't serialize null");  
        }  
        byte[] result = null;  
        ByteArrayOutputStream bos = null;  
        ObjectOutputStream os = null;  
        try {  
          bos = new ByteArrayOutputStream();  
          os = new ObjectOutputStream(bos);
          M m = (M) value;
          os.writeObject(m);  
          os.close();  
          bos.close();  
          result = bos.toByteArray();  
        } catch (IOException e) {  
          throw new IllegalArgumentException("Non-serializable object", e);  
        } finally {  
          close(os);  
          close(bos);  
        }  
        return result;  
      }

    /* (non-Javadoc) * @see org.jasig.cas.ticket.registry.util.serialize.SerializeTranscoder#deserialize(byte[]) */
    @SuppressWarnings("unchecked")
      @Override
      public M deserialize(byte[] in) {
        M result = null;  
        ByteArrayInputStream bis = null;  
        ObjectInputStream is = null;  
        try {  
          if (in != null) {  
            bis = new ByteArrayInputStream(in);  
            is = new ObjectInputStream(bis);  
            result = (M) is.readObject();  
            is.close();  
            bis.close();  
          }  
        } catch (IOException e) {  
            logger.error(String.format("Caught IOException decoding %d bytes of data",  
              in == null ? 0 : in.length) + e);  
        } catch (ClassNotFoundException e) {  
            logger.error(String.format("Caught CNFE decoding %d bytes of data",  
              in == null ? 0 : in.length) + e);  
        } finally {  
          close(is);  
          close(bis);  
        }  
        return result;  
      }

}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

RedisTools.java

“`

/**

*

*/

package org.jasig.cas.ticket.registry.util;

import java.util.ArrayList;

import java.util.List;

import java.util.Map;

import java.util.Set;

import redis.clients.jedis.JedisCluster;

import redis.clients.jedis.ScanParams;

import redis.clients.jedis.ScanResult;

import redis.clients.jedis.Tuple;

/**

* @author lumz 2016-7-12 10:13:17

*

*/

public class RedisTools {

private JedisCluster jedisCluster;

public RedisTools(JedisCluster jedisCluster) {
        super();
        this.jedisCluster = jedisCluster;
    }

    // ----------------------- base handle -------------------------

    /**
     * 根据KEY删除记录
     *
     * @param key
     * @return
     */
    public boolean del(String key) {
        jedisCluster.del(key);
        return true;
    }

    /**
     * 判断Key是否存在
     *
     * @param key
     * @return
     */
    public boolean exist(String key) {
        return jedisCluster.exists(key);
    }

    /**
     * 判断key+domain是否存在
     *
     * @param domain 域名
     * @param key    键值
     * @return
     */

    public boolean existsHSet(String domain, String key) {
        return jedisCluster.hexists(key, domain);
    }

    /**
     * 全局扫描hset
     *
     * @param match field匹配模式
     * @return
     */
    public List<Map.Entry<String, String>> scanHSet(String key, String match) {
        int cursor = 0;
        ScanParams scanParams = new ScanParams();
        scanParams.match(match);
        ScanResult<Map.Entry<String, String>> scanResult;
        List<Map.Entry<String, String>> list = new ArrayList<Map.Entry<String, String>>();
        do {
            scanResult = jedisCluster.hscan(key, String.valueOf(cursor));// .hscan(,
            // scanParams);
            list.addAll(scanResult.getResult());
            cursor = Integer.parseInt(scanResult.getStringCursor());
        } while (cursor > 0);
        return list;
    }

    /**
     * 返回 domain 指定的哈希集中所有字段的value值
     *
     * @param domain
     * @return
     */
    public List<String> hvals(String domain) {
        return jedisCluster.hvals(domain);
    }

    /**
     * 返回 domain 指定的哈希集中所有字段的key值
     *
     * @param domain
     * @return
     */
    public Set<String> hkeys(String domain) {
        return jedisCluster.hkeys(domain);
    }

    /**
     * 返回 domain 指定的哈希key值总数
     *
     * @param domain
     * @return
     */
    public long lenHset(String domain) {
        return jedisCluster.hlen(domain);
    }

    /**
     * 添加一个Key对应的Value,并设置有效期
     *
     * @param key
     * @param value
     * @param second
     * @return
     */
    public boolean set(String key, String value, int second) {
        jedisCluster.setex(key, second, value);
        return true;
    }

    /**
     * 添加一个Key对应的Value,并设置有效期
     *
     * @param key
     * @param value
     * @param second
     * @return
     */
    public boolean set(byte[] key, byte[] value, int second) {
        jedisCluster.setex(key, second, value);
        return true;
    }
    /**
     * 添加一个Key对应的Value
     *
     * @param key
     * @param value
     * @return
     */
    public boolean set(String key, String value) {
        jedisCluster.set(key, value);
        return true;
    }

    /**
     * 追加一个Key对应的Value
     *
     * @param key
     * @param value
     * @return
     */
    public boolean append(String key, String value) {
        jedisCluster.append(key, value);
        return true;
    }

    /**
     * 获得Key对应的Value
     *
     * @param key
     * @param defaultValue
     * @return
     */
    public String get(String key, String defaultValue) {
        String value = jedisCluster.get(key);
        if (null != value && !"".equals(value)) {
            return value;
        } else {
            return defaultValue;
        }
    }
    /**
     * 获得Key对应的Value
     *
     * @param key
     * @param defaultValue
     * @return
     */
    public byte[] get(byte[] key, byte[] defaultValue) {
        byte[] value = jedisCluster.get(key);
        if (null != value && !"".equals(value)) {
            return value;
        } else {
            return defaultValue;
        }
    }

    /**
     * 重命名key
     *
     * @param key
     * @param newKey
     * @return
     */
    public boolean rename(String key, String newKey) {
        jedisCluster.rename(key, newKey);
        return true;
    }

    /**
     * 数值+1
     *
     * @param key
     * @return
     */
    public long incr(String key) {
        return jedisCluster.incr(key);
    }

    /**
     * 数值-1
     *
     * @param key
     * @return
     */
    public long decr(String key) {
        return jedisCluster.decr(key);
    }

    /**
     * 设置过期时间
     *
     * @param key
     * @param seconds
     */
    public void expire(String key, int seconds) {
        jedisCluster.expire(key, seconds);
    }

    // ----------------------- hSet handle -------------------------

    /**
     * 设置HashSet对象
     *
     * @param domain 域名
     * @param key    键值
     * @param value  Json String or String value
     * @return
     */

    public boolean setHSet(String key, String domain, String value) {
        jedisCluster.hset(key, domain, value);
        return true;
    }

    /**
     * 获得HashSet对象
     *
     * @param domain 域名
     * @param key    键值
     * @return Json String or String value
     */
    public String getHSet(String domain, String key) {
        return jedisCluster.hget(key, domain);
    }

    /**
     * 获取存储在键的散列的所有字段和值
     *
     * @param key 键值
     * @return
     */
    public Map<String, String> getHGetAll(String key) {
        return jedisCluster.hgetAll(key);
    }

    /**
     * 删除HashSet对象
     *
     * @param domain 域名
     * @param key    键值
     * @return 删除的记录数
     */

    public long delHSet(String domain, String key) {
        return jedisCluster.hdel(key, domain);
    }

    /**
     * 删除HashSet对象的指定domains
     *
     * @param domain 域名
     * @param key    键值
     * @return 删除的记录数
     */

    public long delHSet(String key, String... domain) {
        return jedisCluster.hdel(key, domain);
    }

    // ----------------------- sortedSet handle -------------------------

    /**
     * 设置排序集合
     *
     * @param key
     * @param score
     * @param value
     * @return
     */
    public boolean setSortedSet(String key, long score, String value) {
        jedisCluster.zadd(key, score, value);
        return true;
    }

    /**
     * 获得排序集合
     *
     * @param key
     * @param startScore
     * @param endScore
     * @param orderByDesc
     * @return
     */
    public Set<String> getSoredSet(String key, long startScore, long endScore, boolean orderByDesc) {
        if (orderByDesc) {
            return jedisCluster.zrevrangeByScore(key, endScore, startScore);
        } else {
            return jedisCluster.zrangeByScore(key, startScore, endScore);
        }
    }

    /**
     * 获得排序集合
     *
     * @param key
     * @param startScore
     * @param endScore
     * @param count
     * @param offset
     * @param orderByDesc
     * @return
     */
    public Set<String> getSoredSet(String key, long startScore, long endScore, int offset, int count,
            boolean orderByDesc) {
        if (orderByDesc) {
            return jedisCluster.zrevrangeByScore(key, endScore, startScore, offset, count);
        } else {
            return jedisCluster.zrangeByScore(key, startScore, endScore, offset, count);
        }
    }

    /**
     * 计算排序长度
     *
     * @param key
     * @param startScore
     * @param endScore
     * @return
     */
    public long countSoredSet(String key, long startScore, long endScore) {
        Long count = jedisCluster.zcount(key, startScore, endScore);
        return count == null ? 0L : count;
    }

    /**
     * 计算排序长度
     *
     * @param key
     * @return
     */
    public long countSoredSet(String key) {
        Long count = jedisCluster.zcard(key);
        return count == null ? 0L : count;
    }

    /**
     * 删除排序集合
     *
     * @param key
     * @param value
     * @return
     */
    public boolean delSortedSet(String key, String value) {
        long count = jedisCluster.zrem(key, value);
        return count > 0;
    }

    /**
     * 删除排序集合
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public boolean delRankSortedSet(String key, long start, long end) {
        long count = jedisCluster.zremrangeByRank(key, start, end);
        return count > 0;
    }

    /**
     * 删除排序部分集合
     *
     * @param key
     * @return
     */
    public boolean delSortedSet(String key, long minScore, long maxScore) {
        long count = jedisCluster.zremrangeByScore(key, minScore, maxScore);
        return count > 0;
    }

    /**
     * 根据分数排序获得前N个
     *
     * @param key
     * @param topN
     * @return
     */
    public Set<Tuple> zrevrangeWithScores(String key, int topN) {
        return jedisCluster.zrevrangeWithScores(key, 0, topN);
    }

    /**
     * 根据分数排序从指定位置获得数据
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<Tuple> zrevrangeWithScores(String key, long start, long end) {
        return jedisCluster.zrevrangeWithScores(key, start, end);
    }

    // ----------------------- List handle -------------------------

    /**
     * 列表lpush操作
     * 命令将一个或多个值插入到列表头部
     *
     * @param key
     * @param value
     */
    public void lpush(String key, String... value) {
        jedisCluster.lpush(key, value);
    }

    /**
     * 列表rpush操作
     * 将一个或多个值插入到列表的尾部(最右边)
     *
     * @param key
     * @param value
     */
    public void rpush(String key, String... value) {
        jedisCluster.rpush(key, value);
    }

    /**
     * 列表长度
     *
     * @param key
     */
    public long llen(String key) {
        return jedisCluster.llen(key);
    }

    /**
     * 根据start和stop获取列表
     *
     * @param key
     * @param start
     * @param stop
     * @return
     */
    public List<String> lrange(String key, int start, int stop) {
        return jedisCluster.lrange(key, start, stop);
    }

    /**
     * 列表删除
     *
     * @param key
     * @param count count > 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。 count < 0 :
     *              从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。 count = 0 : 移除表中所有与
     *              value 相等的值。
     * @param value
     * @return
     */
    public long lrem(String key, int count, String value) {
        return jedisCluster.lrem(key, count, value);
    }

    /**
     * 列表清理
     *
     * @param key
     * @param start
     * @param end
     */
    public void ltrim(String key, int start, int end) {
        jedisCluster.ltrim(key, start, end);
    }

    /**
     * 将一个或多个成员元素加入到集合中,已经存在于集合的成员元素将被忽略
     *
     * @param key
     * @param member
     * @return
     */
    public Long sadd(String key, String... member) {
        return jedisCluster.sadd(key, member);
    }

    /**
     * 删除成员
     *
     * @param key
     * @param member
     * @return
     */
    public Long srem(String key, String... member) {
        return jedisCluster.srem(key, member);
    }

    /**
     * 获取set所有成员
     *
     * @param key
     * @return
     */
    public Set<String> smembers(String key) {
        return jedisCluster.smembers(key);
    }

    /**
     * 判断member元素是否是集合key的成员
     *
     * @param key
     * @param member
     * @return
     */
    public boolean sismember(String key, String member) {
        return jedisCluster.sismember(key, member);
    }

    // ----------------------- mSet handle -------------------------
    public String setMSet(String key, Map<String, String> hash) {
        return jedisCluster.hmset(key, hash);
    }

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

}

“`SerializeTranscoder

package org.jasig.cas.ticket.registry.util.serialize;

import java.io.Closeable;

import org.apache.log4j.Logger;

/**

* @author lumz

*

* @date 2016-7-12 10:44:07

*/

public abstract class SerializeTranscoder {

protected static Logger logger = Logger.getLogger(SerializeTranscoder.class);

  public abstract byte[] serialize(Object value);

  public abstract Object deserialize(byte[] in);

  public void close(Closeable closeable) {
    if (closeable != null) {
      try {
        closeable.close();
      } catch (Exception e) {
         logger.info("Unable to close " + closeable, e); 
      }
    }
  }

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

}

参考资料

【1】 Tomcat redis session manager 

【2】 实现CAS Ticket 基于redis的集群 

 

其他相关资料->

·SSO单点登录LDAP代理服务器  http://www.what21.com/sys/view/java_jndi_1456896158385.html

·统一身份认证平台 LDAP代理服务器  http://www.what21.com/sys/view/service_appsoft_1456896336635.html

小奋斗文章

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

评论

该文章不支持评论!

分享到: