0

私の Web アプリでは、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);

    }
}
4

1 に答える 1

0

これは Spring が直接の原因ではありませんが、SDN がオブジェクトを作成しようとしたときに NPE が原因であり、setPassword() を呼び出して、null である passwordEncoder フィールドを使用しようとしました。

このフィールドを自動配線しようとしましたが、この質問はエンティティが自動配線しないことを示しています。

さらに、とにかく自動配線を機能させることができなかったので、setPassword メソッドで回避策を作成しました。

于 2015-07-27T17:12:05.853 に答える