3

I'm currently working on a Spring Boot/ Angular project in my university. I'm struggeling with the matches function of BCryptPasswordEncoder, which always returns false.

User.java

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue
    private Long id;

    @NotNull
    @Column(unique = true)
    private String userName;

    @NotNull
    private String password;

    @ElementCollection(fetch = FetchType.EAGER)
    List<Role> roles;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }
}

UserService.java

@Autowired
PasswordEncoder passwordEncoder;

public User register(UserDto userDto) throws EntityExistsException {
    User existingUser = userRepository.findUserByUserName(userDto.userName);
    if (existingUser != null)
        throw new EntityExistsException();

    User newUser = new User();
    newUser.setUserName(userDto.userName);
    newUser.setPassword(passwordEncoder.encode(userDto.password));
    List<Role> roles = new ArrayList<Role>();
    roles.add(Role.ROLE_USER);
    newUser.setRoles(roles);

    try {
        newUser = userRepository.save(newUser);
    } catch (Exception e) {
        return null;
    }

    return newUser;
}

public boolean credentialsAreCorrect(String username, String password){
    User user = userRepository.findUserByUserName(username);
    if ( user == null)
        return false;

    return passwordEncoder.matches(password, user.getPassword());
}

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

In Angular I'm registering with following function:

  register() {
    const userName = 'testUser';
    const password = 'testPassword';

    this.httpClient.post('http://localhost:8080/user/register', {userName, password})
      .subscribe(value => console.log(value));
  }

After successfull registration I'm using the same credentials with the UserService.credentialsAreCorrect()method. But it always return false. The parameters of the credentialsAreCorrect() method are correct and the user is found.

Edit

When I was debugging the server I noticed, that the value of the password parameter of the credentialsAreCorrect(username, password) method was always "PROTECTED". I tought that was only visualy for debugging but the value really is "PROTECTED".

I get the password from the request header in the following interceptor:

@Component
public class AuthChannelInterceptor implements ChannelInterceptor {
    private static final String USERNAME_HEADER = "login";
    private static final String PASSWORD_HEADER = "passcode";

    @Autowired
    private WebSocketAuthenticatorService webSocketAuthenticatorService;

    @Override
    public Message<?> preSend(final Message<?> message, final MessageChannel channel) throws AuthenticationException {
        final StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);

        if (StompCommand.CONNECT == accessor.getCommand()) {
            final String username = accessor.getFirstNativeHeader(USERNAME_HEADER);
            final String password = accessor.getFirstNativeHeader(PASSWORD_HEADER);
            final UsernamePasswordAuthenticationToken user = webSocketAuthenticatorService.getAuthenticatedOrFail(username, password);

            accessor.setUser(user);
        }
        return message;
    }
}
MucoBey
  • 171
  • 9

2 Answers2

0

I finally solved it by changing the password header to usercode. I still don't understand, why the value gets "PROTECTED", but at least it works.

New Interceptor

@Component
public class AuthChannelInterceptor implements ChannelInterceptor {
    private static final String USERNAME_HEADER = "login";
    private static final String PASSWORD_HEADER = "usercode";

    @Autowired
    private WebSocketAuthenticatorService webSocketAuthenticatorService;

    @Override
    public Message<?> preSend(final Message<?> message, final MessageChannel channel) throws AuthenticationException {
        final StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);

        if (StompCommand.CONNECT == accessor.getCommand()) {
            final String username = accessor.getFirstNativeHeader(USERNAME_HEADER);
            final String password = accessor.getFirstNativeHeader(PASSWORD_HEADER);
            final UsernamePasswordAuthenticationToken user = webSocketAuthenticatorService.getAuthenticatedOrFail(username, password);

            accessor.setUser(user);
        }
        return message;
    }
}
Community
  • 1
  • 1
MucoBey
  • 171
  • 9
0

Im not sure if the issue has been addressed but here are my five cents: instead of using final String password = accessor.getFirstNativeHeader(PASSWORD_HEADER); you have to use final String password = accessor.getPasscode(); as StompHeaderAccessor.StompPasscode is not directly accessible, i.e toString() produces 'PROTECTED'. As you already mentioned accessor.getFirstNativeHeader("XXXX") works for the other fields. (from what I understood the client sets STOMP_PASSCODE_HEADER to "PROTECTED" and that's why this happens)

Anthony T
  • 11
  • 1
  • 4