Search code examples
spring-securityspring-bootbasic-authenticationspring-4embedded-tomcat-7

spring boot basic http authentication with multiple roles throws 403 forbidden error


I am trying to configure spring boot-Embedded Tomcat basic HTTP authentication with multiple roles, with most of the url's similar but few of them specific to each role. Here for first role the basic HTTP authentication pops up and working fine. With below code,

    @Configuration
    @EnableWebMvcSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)

public class TestSecurityAdapter extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests().antMatchers(null, getAppAdminRolePaths()).authenticated()
                .anyRequest().hasAnyRole("APPADMIN")
                .and()
                .httpBasic();

        http.csrf().disable()
                .authorizeRequests().antMatchers(null, getAppUserRolePaths()).authenticated()
                .anyRequest().hasAnyRole("APPUSER")
                .and()
                .httpBasic();

        http.authorizeRequests().antMatchers(null, new String[]{"/app/appOwnerView.html"}).authenticated()
                .anyRequest().hasAnyRole("APPOWNER")
                .and()
                .httpBasic();
    }

    @Override
    @Autowired
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("appadminname").password("appadminpwd").roles("APPADMIN").and()
        .withUser("appusername").password("appuserpwd").roles("APPUSER").and()
        .withUser("appownername").password("appoownerpwd").roles("APPOWNER");
    }

    private static String[] getAppAdminRolePaths(){
        return new String[]{"/appweb/*",
                "/app/checkService.html",               
                "/app/index.html",                
                "/app/testData.html",    
                "/app/adminView.html", 
                "/app/demo.html"};
    }

    private static String[] getAppUserRolePaths(){
        return new String[]{"/appweb/*",
                "/app/checkService.html",               
                "/app/index.html",                
                "/app/testData.html",    
                "/app/userView.html", 
                "/app/demo.html"};
    }
}

For HTTP username/password popup in browser with url http://localhost:8080/app/index.html say with appadminname/appadminpwd it works fine. But for same url if I enter appusername/appuserpwd it throws HTTP 403 Forbidden access error. Here why is the second role APPUSER configured is throwing this error is I am not sure. Please let know if some way to get this resolved.

Thanks


Solution

  • I appreciate this question is a little old now, but this may still be useful to someone.

    Firstly, I'm not sure why your calls to antMatchers() supply null as the first argument; antMatchers() expects a list of strings defining the URLs to be covered by this rule, so I'm not sure what null is expected to match in this case.

    Secondly, anyRequest() means that this rule will be applied to any request made to the application regardless of the URL used, and Spring will apply security rules in the order that they are defined. You would typically define URLs and their associated roles first, and then default to a rule for any other request that must be authenticated (but does not necessarily need any specific roles) with something like anyRequest().authenticated()

    Your first rule says that any request made to the application must be made by users with the role APPADMIN, which denies you access when you try to log in as appusername, so the second rule to allow APPUSERs is not even processed.

    Thirdly, you are making multiple calls to http.authorizeRequests() when you should probably actually be chaining them together, for example:

    http.csrf().disable().authorizeRequests()
        .antMatchers( getAppAdminRolePaths() ).hasRole("APPADMIN")
        .antMatchers( getAppUserRolePaths() ).hasRole("APPUSER")
        .anyRequest().authenticated();
    


    Lastly, when you have just a single role to check against, you can use hasRole() instead of hasAnyRole().

    You also don't need to supply authenticated() and hasRole() in the same rule because hasRole() implies that the user is already authenticated.

    You can find more explanations and examples in the Spring documentation: http://docs.spring.io/spring-security/site/docs/4.0.3.RELEASE/reference/htmlsingle/#authorize-requests