0

在我的网络应用程序中,我针对 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 直接引起的,而是由 NPE 在 SDN 尝试创建对象时引起的,调用 setPassword() 反过来尝试使用为 null 的 passwordEncoder 字段。

我试图自动装配这个字段,但这个问题表明实体不会自动装配。

此外,无论如何我都无法让自动装配工作,所以我在 setPassword 方法中做了一个解决方法。

于 2015-07-27T17:12:05.853 回答