0

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());
}
zkristic
  • 629
  • 1
  • 9
  • 24
  • Security is applied using Spring AOP, meaning proxies are used, which leads to your annotations being useless. Only method calls into the object pass through the proxy, you are making internal method calls so no proxy, no security. – M. Deinum Oct 30 '15 at 10:11
  • @M.Deinum I'm new to Spring Security so could you explain a little more? What are my options? I know what you mean by `internal method calls`, I can easily move @PreAuth annotations to @RequestMapping, but that doesn't solve anything. If you can point to another SO question/answer or a blog post, I would be grateful – zkristic Oct 30 '15 at 10:20
  • 1
    You also need to have the `` tag in the context containing the beans with the annotation. I assume you also have a `DispatcherServlet` which is responsible for the `@Controller`s containing the `@RequestMapping`. AOP defined in the parent context (`ContextLoaderListener` one) don't influence beans in the child context (`DispatcherServlet`). – M. Deinum Oct 30 '15 at 10:22
  • I do have ``, but I'm not sure xml is being read even though config path is specified in web.xml. I don't have any `DispatcherServlet`'s... I didn't know I need one since everything seemed to work fine (and [Spring guide](https://spring.io/guides/tutorials/spring-security-and-angular-js/) didn't say I need one). – zkristic Oct 30 '15 at 10:33
  • Are you following the guide or not? As the guide uses Spring boot and that basically discards your web.xml and xml configuration. You need to add `@EnableGlobalMethodSecurity` to your application class. – M. Deinum Oct 30 '15 at 10:35
  • That was it, thank you. Had to configure `antMatchers` with authorities, now it works as expected. Again, thank you, and sorry for my idiocy. – zkristic Oct 30 '15 at 10:46
  • Post the correct solution as an answer @zkristic . – We are Borg Oct 30 '15 at 13:45
  • @WeareBorg Done, and sorry for late response – zkristic Nov 03 '15 at 12:01

1 Answers1

2

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 ;)

zkristic
  • 629
  • 1
  • 9
  • 24
  • Example for multiple role and multiple uri can be antMatchers("/drug/deleteDrug/**","/drug/editDrug/**","/drug/saveDrugs/**","/drug/newDrug/**").hasAnyAuthority("ADMIN","MANAGER") – mnhmilu Feb 03 '17 at 15:30