3

I had thought that my authorization implementation was done but when attempting to retrieve the UserDetails object, all I'm getting is the username.

I'm using oauth with the following particulars.

Configuring the AuthenticationManager:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}

With this done, I can debug into my userDetailsService:

@Service
public class UserServiceImpl implements UserService, UserDetailsService {
@Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        MyUser persistedUser = userRepository.findByEmail(email);

        if (persistedUser == null) {
            throw new UsernameNotFoundException(String.format("The email %s doesn't exist", email));
        }

        List<GrantedAuthority> authorities = new ArrayList<>();

        MyUser inMemoryUser = new MyUser(persistedUser.getEmail(), null, persistedUser.getEnabled(), false,
                false, false, authorities);

        return inMemoryUser;
    }
}

This completes fine and my client gets back the JWT. But I found the following problem when debugging a later controller method.

@GetMapping
public @ResponseBody Iterable<Curriculum> getMyCurriculums(@AuthenticationPrincipal MyUser injectedUser) {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    MyUser principle = (MyUser) auth.getPrincipal();
    return curriculumService.findByUser(principle);
}

In this case, injectedUser = null, auth is an OAuth2Authentication, and principle is a String - the username. It should be MyUser

Lurk21
  • 2,307
  • 12
  • 37
  • 55

1 Answers1

6

You should configure Spring Security to decode jwt token into MyUser object.

First define a custom OAuth2Authentication to encapsulate MyUser.

public class OAuth2AuthenticationUser extends OAuth2Authentication {

    private MyUser myUser;

    public OAuth2AuthenticationUser(OAuth2Request storedRequest, Authentication userAuthentication) {
        super(storedRequest, userAuthentication);
    }

    public MyUser getMyUser() {
        return myUser;
    }

    public void setMyUser(MyUser) {
        this.myUser= myUser;
    }
}

Then in a Security Configuration class configure jwt token decoding as follows:

@Bean
public JwtAccessTokenConverter accessTokenConverter() {
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    converter.setSigningKey("SIGNING_KEY");
    converter.setAccessTokenConverter(getAuthenticationAccessTokenConverter());
    return converter;
}

private DefaultAccessTokenConverter getAuthenticationAccessTokenConverter() {
    return new DefaultAccessTokenConverter() {
        @Override
        public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
            OAuth2Authentication authentication = (OAuth2Authentication) super.extractAuthentication(map);

            OAuth2AuthenticationUser authenticationUser =
                    new OAuth2AuthenticationUser(authentication.getOAuth2Request(), authentication.getUserAuthentication());

            MyUser myUser = new MyUser();

            // Example properties
            myUser.setId(map.get("id") != null ? Long.valueOf(map.get("id").toString()) : null);
            myUser.setUsername(map.get("user_name") != null ? map.get("user_name").toString() : null);
            myUser.setFullName(map.get("fullName") != null ? map.get("fullName").toString() : null);
            myUser.setCustomerId(map.get("customerId") != null ? Long.valueOf(map.get("customerId").toString()) : null);
            myUser.setCustomerName(map.get("customerName") != null ? map.get("customerName").toString() : null);

            // More other properties

            authenticationUser.setMyUser(myUser);

            return authenticationUser;
        }
    };
}

And then you have access to MyUser object from Spring Security context as follows:

private static MyUser getMyUser() {
    OAuth2AuthenticationUser authentication = (OAuth2AuthenticationUser) SecurityContextHolder.getContext().getAuthentication();
    return (authentication != null && authentication.getMyUser() != null ? authentication.getMyUser() : new MyUser());
}

This fits well in a stateless environment as database access for user details is minimized and all you need is jwt token.

lzagkaretos
  • 2,842
  • 2
  • 16
  • 26
  • this method is works, but you need to enhance the jwt token first, see this: https://stackoverflow.com/a/28512607/2647910 – aswzen Jul 27 '19 at 17:03