Spring Boot not returning username using CustomAuthenticationProvider

matgr :

I have been following Baeldung's Spring 2FA tutorial to implement 2FA. I have created a CustomAuthenticationProvider as instructed, however it's not behaving as expected.

The odd thing is that after login, a username format that I'm not familiar with is displayed when using Principal.getName():

com.appname.models.User@69080b62

As parts of the application rely on this for fetching details, this isn't appropriate but I'm struggling to understand where I've gone wrong. I have done some research but without the correct nomenclature and the name of the format, I'm struggling to find the appropriate result.

    public Authentication authenticate(Authentication auth) throws AuthenticationException {

        User user = userRepository.findByUsername(auth.getName());

        if(user == null) {

            throw new BadCredentialsException("Invalid username or password");

        }

        if(user.getTwoFactor()) {

            //as per tutorial

        }
        //this returns the "correct" username
        System.out.println(user.getUsername());

        final Authentication result = super.authenticate(auth);
        //I suspect it's here that the issue is occurring, though any pointers in the right direction would be appreciated
        return new UsernamePasswordAuthenticationToken(user, result.getCredentials(), result.getAuthorities());

}

I'm expecting the actual username rather than...however it is currently being returned - i.e. an email address of a user.

matgr :

I have "solved" the problem by changing the last few lines to:

final Authentication result = super.authenticate(auth);

UserDetails userDetails = userDetailsService.loadUserByUsername(auth.getName());

return new UsernamePasswordAuthenticationToken(userDetails, 
        result.getCredentials(), userDetails.getAuthorities());

...where userDetailsService points to a simple implementation of the Spring Security UserDetailsService which returns a Spring Security UserDetails object, like so:

@Override
@Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) {

    User user = userRepository.findByUsername(username);

    if(user == null) throw new UsernameNotFoundException(username);

    Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
    for (Role role : user.getRoles()) {

        grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));

    }

    return new org.springframework.security.core.userdetails.User(user.getUsername(), 
            user.getPassword(), user.getEnabled(), user.getNonExpired(), 
            user.getCredentialsNonExpired(), user.getNonLocked(), grantedAuthorities);

}

This works elsewhere in my application so I figured it might work here as well. I believe I could have left the final argument as result.getAuthorities(). I think I can also refactor so I'm not hitting the database twice, but for now I'm just glad that it works.

I'm not 100% sure why my relatively simple User model would not return the username as the Principal name, it may be that there is some more work which should be done to my User object to explicitly mark the username String as the principal name.

If anyone is interested in any further updates, or can provide any more information for anyone else experiencing uncertainty on this issue, please leave a comment or provide another (likely better) answer.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=148861&siteId=1