Spring Security Jwt: getAuthorities() method of custom userDetails class returns no values(roles)

2.9k Views Asked by At

So, I have two different types of users, a User entity that has a role of USER and a HOST entity that has a role of HOST, and roles have permissions accordingly. I created a UserPrincipal that implements UserDetails where i override the getAuthorities() method and add a simpleGrantedAuthority. I created roles and permissions as Enums. This is the ApplicationUserPermission Enum that specifies the permissions.

public enum ApplicationUserPermission {

USER_READ("user:read"),
USER_WRITE("user:write"),
HOST_READ("host:read"),
HOST_WRITE("host:write"),
APARTMENT_READ("apartment:read"),
APARTMENT_WRITE("apartment:write");


private final String permission;

ApplicationUserPermission(String permission) {
    this.permission = permission;
}

public String getPermission() {
    return permission;
}

and then i have the ApplicationUserRole that sets these permissions to two different roles: USER and HOST.

public enum ApplicationUserRole {
USER(Sets.newHashSet(USER_READ, USER_WRITE, APARTMENT_READ)),
HOST(Sets.newHashSet(HOST_READ, HOST_WRITE, APARTMENT_READ, APARTMENT_WRITE));


private final Set<ApplicationUserPermission> permissions;

ApplicationUserRole(Set<ApplicationUserPermission> permissions) {
    this.permissions = permissions;
}

public Set<ApplicationUserPermission> getPermissions() {
    return permissions;
}

Then I have the UserPrincipal that implements UserDetails and sets the authorities for User entity

public class UserPrincipal implements UserDetails {

private final User user;

public UserPrincipal(User user) {
    super();
    this.user = user;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    return Collections.singleton(new SimpleGrantedAuthority("ROLE_"+USER.name()));
}

Then I have my custom UserDetailsService that implements UserDetailsService

@Service
public class MyUserDetailsService implements UserDetailsService {

@Autowired
private UserRepository userRepository;

@Autowired
private HostRepository hostRepository;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    String lastDigits = username.substring(username.length() - 3);
    User user;
    Host host;

    if (username.contains("@")) {
        host = hostRepository.findByEmail(username);
        if (host == null) {
            throw new UsernameNotFoundException("Server 404");
        }
        return new HostPrincipal(host);
    }
    user = userRepository.findByUsername(username);
    if (user == null) {
        throw new UsernameNotFoundException("Server 404");
    }
    return new UserPrincipal(user);
}

This is my ApplicationSecurityConfig.java:

@Configuration
@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {

@Qualifier("myUserDetailsService")
@Autowired
private UserDetailsService userDetailsService;

@Autowired
private AuthenticationEntryPoint authenticationEntryPoint;

@Bean
public UserDetailsService userDetailsService() {
    return super.userDetailsService();
}

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

@Bean
public AuthenticationProvider authenticationProvider() {
    DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
    provider.setUserDetailsService(userDetailsService);
    provider.setPasswordEncoder(passwordEncoder());
    return provider;
}

@Override
protected void configure(AuthenticationManagerBuilder auth) {
    auth.authenticationProvider(authenticationProvider());
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .cors().disable()
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and() // check if user exist in database and create a token for specific user
            .addFilter(new JwtUsernameAndPasswordAuthAuthenticationFilter(authenticationManager()))
            .addFilterAfter(new JwtTokenVerifier(), JwtUsernameAndPasswordAuthAuthenticationFilter.class)
            .authorizeRequests()
            .antMatchers("/", "/login", "/createHost", "/createUser", "/getAllApartments").permitAll()
            .antMatchers("/users/**").hasRole("USER")
            .antMatchers("/hosts/**").hasRole(HOST.name()).anyRequest().authenticated()
            .httpBasic().authenticationEntryPoint(authenticationEntryPoint);
}

This is my JwtUsernameAndPasswordAuthFilter.java:

public class JwtUsernameAndPasswordAuthAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

private static final Logger logger = LoggerFactory.
        getLogger(JwtUsernameAndPasswordAuthAuthenticationFilter.class);

private final AuthenticationManager authenticationManager;

public JwtUsernameAndPasswordAuthAuthenticationFilter(AuthenticationManager authenticationManager) {
    this.authenticationManager = authenticationManager;
}

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
    try {
        UsernamePasswordAuthenticationRequest authenticationRequest =
                new ObjectMapper().readValue(request.getInputStream(),
                        UsernamePasswordAuthenticationRequest.class);

        Authentication authentication = new UsernamePasswordAuthenticationToken(
                authenticationRequest.getUsername(),
                authenticationRequest.getPassword()
        );

        authenticationManager.authenticate(authentication);
        return authentication;

    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {

    logger.info("Auth result: {}", authResult);
    logger.info("Authorities: {}", authResult.getAuthorities());
    String key = "securesecuresecuresecuresecuresecuresecuresecuresecuresecuresecure";

    String token = Jwts.builder()
            .setSubject(authResult.getName())
            .claim("authorities", authResult.getAuthorities())
            .setIssuedAt(Date.valueOf(LocalDate.now().plusWeeks(2)))
            .signWith(Keys.hmacShaKeyFor(key.getBytes()))
            .compact();

    String tokenAsJson = new JSONObject()
            .put("token", token)
            .toString();

    response.addHeader("Authorization", "Bearer " + token);
    response.setContentType("application/json");
    response.getWriter().write(tokenAsJson);
 }

EDIT: This is my Authorization filter

    public class JwtTokenVerifier extends BasicAuthenticationFilter {

    public JwtTokenVerifier(AuthenticationManager authenticationManager){
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain) throws ServletException, IOException {

        String authorizationHeader = request.getHeader("Authorization");
        if (Strings.isNullOrEmpty(authorizationHeader) || !authorizationHeader.startsWith("Bearer ")) {
            filterChain.doFilter(request, response);
            return;
        }

        String secretKey = "securesecuresecuresecuresecuresecuresecuresecuresecuresecuresecure";
        String token = authorizationHeader.replace("Bearer ", "");
        try {
            Jws<Claims> claimsJws = Jwts.parser().
                    setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())).parseClaimsJws(token);

            Claims body = claimsJws.getBody();
            String username = body.getSubject();

            var authorities = (List<Map<String, String>>) body.get("authorities");

            Set<SimpleGrantedAuthority> simpleGrantedAuthoritySet = authorities.stream()
                    .map(m -> new SimpleGrantedAuthority(m.get("authority"))).collect(Collectors.toSet());

            Authentication authentication = new UsernamePasswordAuthenticationToken(username,
                    null, simpleGrantedAuthoritySet);
            SecurityContextHolder.getContext().setAuthentication(authentication);

        } catch (JwtException e) {
            throw new IllegalStateException(String.format("Token %s cannot be trusted", token));
        }
        filterChain.doFilter(request, response);

    }
}

authResult.getAuthorities() returns empty []. If I try to access "/users/**" after I logged in as a User it returns 403 Forbidden.

0

There are 0 best solutions below