package de.javakaffee.web.msm.serializer.kryo; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * Provides a custom kryo serializer for the Spring Security User class. * <p> * This is needed because the User class internally contains a collection * of {@link GrantedAuthority}, which is actually a TreeSet with a * Comparator. During deserialization kryo creates a TreeSet *without* * any comparator and therefore expects that the contained items are * Comparable, which is not the case for SimpleGrantedAuthority - ClassCastException. * </p> * <p> * Motivated by <a href="http://code.google.com/p/memcached-session-manager/issues/detail?id=145"> * issue #145: Deserialization fails on ConcurrentHashMap in Spring User object * </a>. * </p> * @author Martin Grotzke */ public class SpringSecurityUserRegistration implements KryoCustomization { @Override public void customize(final Kryo kryo) { kryo.register( User.class, new SpringSecurityUserSerializer( kryo ) ); } static class SpringSecurityUserSerializer extends Serializer<User> { private final Kryo _kryo; public SpringSecurityUserSerializer(final Kryo kryo) { _kryo = kryo; } @Override public User read(Kryo kryo, Input input, Class<User> type) { final String password = input.readString(); final String username = input.readString(); final int size = input.readInt(true); final List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(size); for (int i = 0; i < size; i++) { authorities.add((GrantedAuthority)_kryo.readClassAndObject(input)); } final boolean accountNonExpired = input.readBoolean(); final boolean accountNonLocked = input.readBoolean(); final boolean credentialsNonExpired = input.readBoolean(); final boolean enabled = input.readBoolean(); return new User(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); } @Override public void write(Kryo kryo, Output output, User user) { output.writeString(user.getPassword()); output.writeString(user.getUsername()); final Collection<GrantedAuthority> authorities = user.getAuthorities(); output.writeInt(authorities.size(), true); for (final GrantedAuthority item : authorities) { _kryo.writeClassAndObject(output, item); } output.writeBoolean(user.isAccountNonExpired()); output.writeBoolean(user.isAccountNonLocked()); output.writeBoolean(user.isCredentialsNonExpired()); output.writeBoolean(user.isEnabled()); } } }