做sso时候,集成cas的rememberMe功能时,报错:java.io.NotSerializableException: java.util.HashMap$KeySet。
首先cas集成rememberMe的具体配置可以可以参照【http://blog.csdn.net/jadyer/article/details/47110353】进行配置,
cas集成redis的配置:
1. 配置redis相关bean
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxTotal" value="1024" /> <property name="maxIdle" value="200" /> <property name="maxWaitMillis" value="1000" /> <property name="testOnBorrow" value="true" /> <property name="testOnReturn" value="true" /> </bean> <!-- redis的连接池pool,不是必选项:timeout/password --> <bean id="jedisPool" class="redis.clients.jedis.JedisPool"> <constructor-arg index="0" ref="jedisPoolConfig" /> <constructor-arg index="1" value="127.0.0.1" /> <constructor-arg index="2" value="6379" type="int" /> <constructor-arg index="3" value="20" type="int" /> </bean>
2.在ticketRegistry.xml将Ticket Registry托管到redis 管理,
<!--Ticket Registry托管到redis 管理, --> <bean id="ticketRegistry" class="com.xxx.xxx.ticketRegistry.RegisTicketRegistry" p:tgtTime="3000000" p:stTime="300000" p:dbnum="8" p:pool-ref="jedisPool" p:logoutManager-ref="logoutManager" />
即可。
3.RedisTicketRegistry中的addTicket(),执行到oos.writeObject(ticket); 抛出异常 java.io.NotSerializableException: java.util.HashMap$KeySet
public void addTicket( final Ticket ticket) { Jedis jedis = pool.getResource(); jedis.select(Integer.valueOf(dbnum)); int seconds = 0; String key = ticket.getId(); if (ticket instanceof TicketGrantingTicket) { seconds = Integer.valueOf(tgtTime) / 1000; } else { seconds = Integer.valueOf(stTime) / 1000; } ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; try { bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(ticket); } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != oos) oos.close(); } catch (Exception e) { e.printStackTrace(); } } byte[] ticketBytes = bos.toByteArray(); jedis.set(key.getBytes(), ticketBytes); jedis.expire(key.getBytes(), seconds); pool.returnResource(jedis); }
4.在执行RedisTicketRegistry.addTicket()方法oos.writeObject(ticket);时候会报错,
java.io.NotSerializableException: java.util.HashMap$KeySet。
最终定位错误原因:是在cas+rememberMe配置时候,(具体配置参照【http://blog.csdn.net/jadyer/article/details/47110353】),
deployerConfigContext.xml,authenticationManager中增加了配置:
<!-- 针对RememberMe需增加的属性配置 --> <property name="authenticationMetaDataPopulators"> <list> <bean class="org.jasig.cas.authentication.SuccessfulHandlerMetaDataPopulator"/> <bean class="org.jasig.cas.authentication.principal.RememberMeAuthenticationMetaDataPopulator"/> </list> </property>
最终错误定位到SuccessfulHandlerMetaDataPopulator.populateAttributes()方法中
public class SuccessfulHandlerMetaDataPopulator implements AuthenticationMetaDataPopulator { /** Attribute name containing collection of handler names that successfully authenticated credential. */ public static final String SUCCESSFUL_AUTHENTICATION_HANDLERS = "successfulAuthenticationHandlers"; @Override public void populateAttributes(final AuthenticationBuilder builder, final Credential credential) { builder.addAttribute(SUCCESSFUL_AUTHENTICATION_HANDLERS, builder.getSuccesses().keySet()); } }
根本原因是jdk中Set没有实现Serializable不支持序列化,如下test实例:
public class SerializableTest { private static Map<String,Object> infoMap = null; private static ByteArrayOutputStream bos = null; private static ObjectOutputStream oos = null; @BeforeClass public static void setUp() throws IOException{ infoMap = new HashMap<String,Object>(); infoMap.put("name", "张三"); infoMap.put("age", 18); infoMap.put("birthdate", new Date()); bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); } @Test public void testSerializableSet() throws IOException{ /** * Set 继承Collection > Iterable 不支持序列化 * 抛出异常 java.io.NotSerializableException: java.util.HashMap$KeySet */ Set<String> set = infoMap.keySet(); oos.writeObject(set); } @Test @SuppressWarnings({ "rawtypes", "unchecked" }) public void testSerializableHashSet() throws IOException{ /** * HashSet实现了java.io.Serializable接口,支持序列化 */ HashSet<String> hashSet = new HashSet(infoMap.keySet()); oos.writeObject(hashSet); } }
最终解决方案:
//builder.addAttribute(SUCCESSFUL_AUTHENTICATION_HANDLERS, builder.getSuccesses().keySet()); builder.addAttribute(SUCCESSFUL_AUTHENTICATION_HANDLERS, new HashSet(builder.getSuccesses().keySet()));