Spring security external config

Viewed 206

Usually SecurityConfig we are doing like

protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers("/index.html").permitAll()
            .antMatchers("/profile/**").authenticated()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .antMatchers("/management/**").hasAnyRole("ADMIN", "MANAGER")
            .antMatchers("/api/public/test1").hasAuthority("ACCESS_TEST1")
            .antMatchers("/api/public/test2").hasAuthority("ACCESS_TEST2")
            .antMatchers("/api/public/users").hasRole("ADMIN")
            .and()
            .httpBasic();
}

I want to get this endpoints ("/api/public/test2") and required authorities "ACCESS_TEST2" from property file or DB (config SecurityConfig extrnaly).

Can I do it? how can I do it?

2 Answers

Yes of course, customize UrlFilterInvocationSecurityMetadataSource to implement FilterInvocationSecurityMetadataSource and override getAttributes() method to obtain the role permission information needed to access the url.

In this articles, permission to access the url will be given by user role. Click for the first article! Click for the second article!

I don't know if there is security mechanism for loading from file, therefore I suggest a custom implementation:

@Configuration
@EnableWebSecurity
@ConfigurationProperties("secure")
@PropertySource(value = "classpath:security-config.yml", factory = YamlPropertySourceFactory.class)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private Map<String, List<String>> urlToAuthorityMapping;
    private Map<String, List<String>> urlToRoleMapping;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();

        urlToAuthorityMapping.forEach((urlPattern, authorities) ->
                registry.antMatchers(urlPattern).hasAnyAuthority(authorities.toArray(new String[0])));

        urlToRoleMapping.forEach((urlPattern, roles) ->
                registry.antMatchers(urlPattern).hasAnyRole(roles.toArray(new String[0])));

        registry.and().httpBasic();
    }

    public void setAuthorities(Map<String, List<String>> authorities) {
        this.urlToAuthorityMapping = invert(authorities);
    }

    public void setRoles(Map<String, List<String>> roles) {
        this.urlToRoleMapping = invert(roles);
    }

    private Map<String, List<String>> invert(Map<String, List<String>> source) {
        Map<String, List<String>> invertedMapping = new HashMap<>();
        for (Map.Entry<String, List<String>> entry : source.entrySet()) {
            String authority = entry.getKey();
            List<String> urlPattens = entry.getValue();
            for (String urlPattern : urlPattens) {
                List<String> authorities = invertedMapping.getOrDefault(urlPattern, new ArrayList<>());
                authorities.add(authority);
                invertedMapping.put(urlPattern, authorities);
            }
        }
        return invertedMapping;
    }
}
public class YamlPropertySourceFactory implements PropertySourceFactory {

    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) {
        YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
        factory.setResources(encodedResource.getResource());

        Properties properties = factory.getObject();

        return new PropertiesPropertySource(encodedResource.getResource().getFilename(), properties);
    }
}

security-config.yml

secure:
  authorities:
    ADMIN:
      - /api/public/users
      - /admin/**
    ACCESS_TEST2:
      - /api/public/test2
    MANAGER:
      - /api/public/users
  roles:
    ACCESS_TEST1:
      - /api/public/test1
    ACCESS_TEST2:
      - /api/public/test2
  authenticated:
    - /profile/**
  permitAll:
    - /public-content/**
Related