在我的网络应用程序中,我针对 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);
}
}