springspring-securityspring-bootpre-authentication

@PreAuthorize annotation in Spring Security doesn't work. Might be misconfigured .xml file


I'm using Spring Security (Spring Boot app) with AngularJS, and I'm trying to allow/deny access to specific mappings based on user's granted authorities.

User.java

public class User implements UserDetails {
    ...
    private String username;
    private String password;
    private boolean enabled;
    private List<GrantedAuthority> grantedAuthorities;

    public User(...) {...}

    private class Permission implements GrantedAuthority {
        String permission;

        public Permission(String permission) {
            this.permission = permission;
        }

        @Override
        public String getAuthority() {
            return permission;
        }
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return grantedAuthorities;
    }
    ...
}

Also, I have my own implementation of UserDetailsService (method loadUserByUsername(String username)) which populates user data with data from database.

Previously mentioned mappings are:

@RequestMapping("yes")
public void doesHaveAuthority() {yes();}

@PreAuthorize("hasAuthority('10001')")
private void yes() {}

@RequestMapping("no")
public void doesntHaveAuthority() {no();}

@PreAuthorize("hasAuthority('00000')")
private void no() {}

Where user does have authority "10001", but doesn't have authority "00000", and access to /yes should be allowed, but access to /no shouldn't. Unfortunately, both paths are allowed.

I have spring-security-config as maven dependency as mentioned here.

My WEB-INF/web.xml

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/security.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

</web-app>

and WEB-INF/security.xml

<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/security
        http://www.springframework.org/schema/security/spring-security.xsd">

    <context:component-scan base-package="com.mycompany.project" />

    <global-method-security pre-post-annotations="enabled" />

</beans:beans>

So, to summarize: UserDetailsService implementation works (I can login with user credentials and logger prints granted authorities so I know they're there), but user can access both yes and /no paths when in reality he/she shouldn't be able to access /no since he/she doesn't have authority (permission) "00000".

Maybe my xml config is not correct or isn't being read at all.

Thanks for your time

UPDATE As M. Deinum said in comments Spring Boot disregards web.xml and security.xml files. Solution was to modify SecurityConfiguration which extends WebSecurityConfigurerAdapter and add corresponding antMatchers with corresponding authorities.

Example:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.httpBasic().and()
        .authorizeRequests()
        .antMatchers("/login.html", "/index.html", "/").permitAll()
        .antMatchers("/yes").hasAuthority("10001")
        .antMatchers("/no").hasAuthority("00000")
        .anyRequest()
        .authenticated().and()
        .addFilterAfter(new CSRFHeaderFilter(), CsrfFilter.class)
        .csrf().csrfTokenRepository(csrfTokenRepository());
}

Solution

  • User "We are Borg" asked me to post solution as an answer so here it goes:

    Basically, since my app is Spring Boot app, it disregarded (ignored my .xml config files). Solution was quite simple: extend WebSecurityConfigurerAdapter and override configure(HttpSecurity) method. When overriding it, make sure that user has authority and/or permission to access path (antMatchers). It should look something like this:

    SecurityConfiguration.java

    @Configuration
    @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.httpBasic().and()
                .authorizeRequests()
                .antMatchers("/login.html", "/index.html", "/").permitAll()
                .antMatchers("/yes").hasAuthority("10001")
                .antMatchers("/no").hasAuthority("00000")
                .anyRequest().authenticated();
        }
    
    }
    

    And you won't be needing @PreAuthorize annotation anymore, nor will you need .xml config files (security.xml, web.xml, etc...)

    Good luck ;)