Spring security Basic Authentication - 401 Unauthorized with correct credentials

Viewed 1390

Here is my Security config class, I am using BCryptPasswordEncoder

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder encoder;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic()
            .and()
            .authorizeRequests()
            .antMatchers(HttpMethod.POST,"/api/auth/register")
            .permitAll()
            .antMatchers(HttpMethod.GET,"/api/auth/resources")
            .hasAuthority("USER")
            .and()
            .csrf().disable();

    }

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

This is my implementation of the UserDetailsService

public class UserDetailsServiceImpl implements UserDetailsService{

    @Autowired
    private AccountRepository repo;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        Account account = repo.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("No user found for username " + username));
        User user = 
                new User(account.getUsername(), account.getPassword(), true, true, true, true, AuthorityUtils.createAuthorityList(account.getAuthorities()));
        return user;
    }

}

The POST method above is where I can provide the username and password which is stored in a MySQL table.

Now when I can call the GET method using Postman using the username and password I just added, I get a 401 Unauthorized error like below

{
    "timestamp": "2020-05-23T20:12:10.165+0000",
    "status": 401,
    "error": "Unauthorized",
    "message": "Unauthorized",
    "path": "/api/auth/resources"
}
1 Answers

Spring Security has ExpressionUrlAuthorizationConfigurer.java which has methods as below. Here prefix ROLE_ makes the difference.

  • For hasAuthority(), hasAnyAuthority() methods we need to pass the authority with prefix ROLE_ i.e ROLE_USER

  • For hasRole() method automatically adds the prefix ROLE_, so that we can only pass the authority name as USER


private static String hasRole(String role) {
    Assert.notNull(role, "role cannot be null");
    if (role.startsWith("ROLE_")) {
        throw new IllegalArgumentException("role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'");
    } else {
        return "hasRole('ROLE_" + role + "')";
    }
}

private static String hasAuthority(String authority) {
    return "hasAuthority('" + authority + "')";
}

private static String hasAnyAuthority(String... authorities) {
    String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','");
    return "hasAnyAuthority('" + anyAuthorities + "')";
}
Related