Search code examples
serializationspring-bootspring-securitydependency-injectionspring-session

Dependency injection and serialization Object in spring


I have an application that is configured with spring security.
Everything is working as well.
The UserDetailService is:

@Service(value = "userDetail")
public class UserDetailServiceImpl implements UserDetailsService, Serializable {
    private static final long serialVersionUID = 3487495895819392L;

    @Autowired
    private IUserDetailRepository iUserDetailRepository;

    @Autowired
    private IUserRoleRepository iUserRoleRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = iUserDetailRepository.loadUserByUsername(username);
        List<UserRole> roles = iUserRoleRepository.getAllRoleByUserName(username);
        UserDetails u = new UserDetails() {

            @Override
            public boolean isEnabled() {
                // return user.getEnable();
                return true;
            }

            @Override
            public boolean isCredentialsNonExpired() {
                return true;
            }

            @Override
            public boolean isAccountNonLocked() {
                return true;
            }

            @Override
            public boolean isAccountNonExpired() {
                return true;
            }

            @Override
            public String getUsername() {
                // return user.getUsername();
                return "reyhane";
            }

            @Override
            public String getPassword() {
                // return user.getPassword();
                return "reyhane";
            }

            @Override
            public Collection<? extends GrantedAuthority> getAuthorities() {
                Collection<GrantedAuthority> authorities = new ArrayList<>();
                SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ADMIN");
                authorities.add(authority);

                return authorities;

            }
        };
        return u;
    }

}

After that I was forced to config spring session over this configuration for saving the context of spring security into Redis.
There exists a problem in UserDetailService.
UserDetailService have two autowired filed as shown above are:

@Autowired
private IUserDetailRepository iUserDetailRepository;

@Autowired
private IUserRoleRepository iUserRoleRepository;

This two autowired field are for getting User and User's roles information from DB.
It is interesting here that when I comment this two autowired like this:

//@Autowired
private IUserDetailRepository iUserDetailRepository;

//@Autowired
private IUserRoleRepository iUserRoleRepository;

and fill manually user and user's role.Spring session is working as well but when this two @Autowired exist in the class the below exception is raised:

Caused by: org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.io.NotSerializableException: org.springframework.dao.support.PersistenceExceptionTranslationInterceptor at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:68) ~[spring-core-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:35) ~[spring-core-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:90) ~[spring-data-redis-1.7.2.RELEASE.jar:na] ... 35 common frames omitted Caused by: java.io.NotSerializableException: org.springframework.dao.support.PersistenceExceptionTranslationInterceptor at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184) ~[na:1.8.0_111] at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548) ~[na:1.8.0_111]

Everybody know that what is happen?
I do not understand what is happen.
I do not know there is what key for searching in google
I want to know what is happen between spring session and spring security and how to solve it.
Thanks for your helping


Solution

  • This has actually nothing to do with those dependencies, it is due to the fact that you are using an (non static) inner class. You should either use an embedded static class or simply use the regular User class from Spring Security. (More on inner, (non) static embedded classes are found in this answer.

    In short due to the fact of you using an inner classes it requires an instance of the outer class to exist. Now when serializing the UserDetails it will also try to serialize the outer class.

    The simplest solution is to rewrite your code and use the Spring Security provided User class (assuming you are using Spring Security 4.2 you can use the UserBuilder).

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = iUserDetailRepository.loadUserByUsername(username);
        List<UserRole> roles = iUserRoleRepository.getAllRoleByUserName(username);
    
        return User.withUsername(user.getUsername())
                            .password(user.getPassword())
                            .disabled(!user.getEnable())
                            .roles(roles.stream().map(<convert-to-SimpleGrantedAuthority>).collect(Collectors.toList()))
                            .build();
    }
    

    If you are on Spring Security before 4.2 you can simply construct a User object using the constructor.

    return new User(user.getUsername(), user.getPassword(), user.getEnable(), true, true, true, <convert-roles-to-SimpleGrantedAuthoritu>);
    

    When return a concrete class it doesn't require an instance of the outer class (as it isn't an inner class) and it will not try to serialize it. It will only serialize the User and nothing else.