SDN4 — ошибка сопоставления GraphModel с экземпляром

В моем веб-приложении я храню и аутентифицирую пользователей на сервере Neo4j.

В моем потоке, если я зарегистрируюсь в приложении (таким образом, сохранив экземпляр пользователя), а затем перейду на страницу входа, я смогу войти в систему нормально. Если я остановлю сервер и запущу его снова, я не смогу войти в систему. Ошибка:

org.springframework.security.authentication.InternalAuthenticationServiceException: Error mapping GraphModel to instance of co.foo.data.models.User

Пользователь находится в базе данных. Если я удалю пользователя, снова зарегистрируюсь и войду в систему, он будет работать нормально, пока я не перезапущу сервер, так что это повторяется.

Использование SDN 4.0.0.RC1

Код

public interface MyUserDetailsService extends UserDetailsService {
    @Override
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException;

    User getUserFromSession();

    @Transactional
    User register(String username, String firstName, String lastName, String password);
}

public interface UserRepository extends GraphRepository<User>, MyUserDetailsService {
    User findByUsername(String username);
}

@Primary
@Service
public class UserRepositoryImpl implements MyUserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    private User findByUsername(String username) {
        return userRepository.findByUsername(username);
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
        final User user = findByUsername(username);
        if (user==null) throw new UsernameNotFoundException("Username not found: " + username);
        return new MyUserDetails(user);
    }

    @Override
    public User getUserFromSession() {
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication = context.getAuthentication();
        Object principal = authentication.getPrincipal();
        if (principal instanceof MyUserDetails) {
            MyUserDetails userDetails = (MyUserDetails) principal;
            return userDetails.getUser();
        }
        return null;    
}


    @Override
    @Transactional
    public User register(String username, String firstName, String lastName, String password) {
        User found = findByUsername(username);
        if (found!=null) throw new RuntimeException("Login already taken: " + username);
        if (firstName==null || firstName.isEmpty()) throw new RuntimeException("No first name provided.");
        if (lastName==null || lastName.isEmpty()) throw new RuntimeException("No last name provided.");
        if (password==null || password.isEmpty()) throw new RuntimeException("No password provided.");
        User user = new User(username, firstName, lastName, password, null, passwordEncoder, User.Roles.ROLE_USER);
        userRepository.save(user);
        setUserInSession(user);
        return user;
    }

    void setUserInSession(User user) {
        SecurityContext context = SecurityContextHolder.getContext();
        MyUserDetails userDetails = new MyUserDetails(user);
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, user.getPassword(),userDetails.getAuthorities());
        context.setAuthentication(authentication);
    }

    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @AuthenticationPrincipal
    public @interface CurrentUser {

    }

}

@Configuration
@EnableWebSecurity
@Order(2)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/login/**", "/resources/**", "/web/register").permitAll()
                .antMatchers("/web/**").authenticated()
                .and()
                .formLogin().loginProcessingUrl("/login").failureUrl("/login?authorization_error=true").defaultSuccessUrl("/web/home").loginPage("/login").permitAll()
                .and()
                .logout().logoutUrl("/logout").logoutSuccessUrl("/login")
                .permitAll();
    }

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
                auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

Объект пользователя

@NodeEntity
public class User extends BaseEntity {

    public static final String HAS_ACCOUNT = "HAS_ACCOUNT";

    @Index(unique = true)
    private String username;
    public String firstName;
    public String lastName;
    private String password;
    private Roles[] roles;

    private YodleeSession yodleeSession;

    @Transient
    private PasswordEncoder passwordEncoder;

    public User() {}

    public User(String username, String firstName, String lastName, String password, YodleeSession yodleeSession, PasswordEncoder passwordEncoder, Roles... roles) {
        this.username = username;
        this.firstName = firstName;
        this.lastName = lastName;
        this.yodleeSession = yodleeSession;
        this.passwordEncoder = passwordEncoder;
        this.roles = roles;
        this.password = encodePassword(password);
    }

    // Getters

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public String getUsername() {
        return this.username;
    }

    public String getPassword() {
        return password;
    }

    public YodleeSession getYodleeSession() {
        return yodleeSession;
    }

    public Roles[] getRoles() {
        return roles;
    }

    // Setters

    public void setUsername(String username) {
        this.username = username;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public void setYodleeSession(YodleeSession yodleeSession) {
        this.yodleeSession = yodleeSession;
    }

    public void setPassword(String password) {
        encodePassword(password);
    }

    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    // Roles
    public enum Roles implements GrantedAuthority {
        ROLE_USER, ROLE_ADMIN;

        @Override
        public String getAuthority() {
            return name();
        }
    }

    public void setRoles(Roles[] roles) {
        this.roles = roles;
    }


    // Bank accounts
    @Relationship(type=HAS_ACCOUNT, direction = Relationship.OUTGOING)
    private Set<Account> accounts;

    public Set<Account> getAccounts() {
        return this.accounts;
    }

    public void addAccount(Account account) {
        if (accounts == null) {
            accounts = new HashSet<Account>();
        }
        accounts.add(account);
    }

    // Password
    private String encodePassword(String password) {
        return passwordEncoder.encode(password);
    }

    public void updatePassword(String old, String newPass1, String newPass2) {
        if (!password.equals(encodePassword(old))) throw new IllegalArgumentException("Existing Password invalid");
        if (!newPass1.equals(newPass2)) throw new IllegalArgumentException("New Passwords don't match");
        this.password = encodePassword(newPass1);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;
        if (nodeId == null) return super.equals(o);
        return nodeId.equals(user.nodeId);

    }
}

person Robin Elvin    schedule 23.07.2015    source источник
comment
Не могли бы вы поделиться соответствующим кодом?   -  person Luanne    schedule 23.07.2015
comment
Код добавлен - дайте мне знать, если вам нужно что-то еще   -  person Robin Elvin    schedule 23.07.2015
comment
Таким образом, исключение возникает, когда вы выполняете userRepository.findByUsername(username)? Можете ли вы поделиться полной трассировкой стека?   -  person Luanne    schedule 27.07.2015


Ответы (1)


Это не было напрямую вызвано Spring, но было вызвано NPE, когда SDN пытается создать объект, вызывая setPassword(), который, в свою очередь, пытается использовать поле passwordEncoder, которое равно null.

Я попытался выполнить автоподключение этого поля, но, поскольку этот этот вопрос показывает, что сущности не подключаются автоматически.

Кроме того, я все равно не мог заставить автопроводку работать, поэтому я нашел обходной путь в методе setPassword.

person Robin Elvin    schedule 27.07.2015