Damien C
Damien C

Reputation: 1137

Spring Boot - Specific URL AccessDecisionVoter

I want to apply specific security rules to 2 URLs on my Spring Boot app.

I want to use AccessDecisionVoter feature to manage that.
All works good... event too good.

My new rules apply, on my 2 specific URL, but unfortunately on all my URLs.

My AccessDecisionManager declaration:

    @Bean
    public AccessDecisionManager playerResourceDecisionManager() {
        List<AccessDecisionVoter<? extends Object>> decisionVoters = Arrays.asList(
            new AuthenticatedVoter(),
            hashSerialSecurityVoter
        );
        return new UnanimousBased(decisionVoters);
    }

Here is my SecurityConfig main code

        http.csrf()
        .disable().cors().and().exceptionHandling()
        // All Login Stuff
        .authenticationEntryPoint(new Http403ForbiddenEntryPoint() {})
            .and().authenticationProvider(getProvider())
                .formLogin().loginProcessingUrl("/login")
                    .successHandler(new AuthentificationLoginSuccessHandler())
                    .failureHandler(new SimpleUrlAuthenticationFailureHandler())
            .and().logout().logoutUrl("/logout")
                .logoutSuccessHandler(new AuthentificationLogoutSuccessHandler())
                .invalidateHttpSession(true).and().authorizeRequests()

         ...
        // Spring boot actuator - App Status
        .antMatchers("/actuator/*").permitAll()

        // Static Token end points
        .antMatchers("/my-filtered-url1", "/sub/my-filtered-url2/*")
            .permitAll()
            .accessDecisionManager(playerResourceDecisionManager())
            /* Here is the problem : 
             *   I want this accessDescisionManager apply only on my antMatchers
             *    (2 specific URLs), 
             * But it runs on every app calls.
             */

        .antMatchers(HttpMethod.POST, "/log/*").permitAll() 
        /* Login */
        .antMatchers("/login").permitAll()
        .antMatchers("/auth/**").permitAll()
        .antMatchers(HttpMethod.POST,"/user/lost-password").permitAll()

        .antMatchers("/user").hasAuthority("ADMIN")

        .anyRequest().authenticated();


I'd rather not putting specific code in my hashSerialSecurityVoter class with URL declaration. How can I do that ?

Regards.

Upvotes: 0

Views: 872

Answers (1)

Andreas
Andreas

Reputation: 159185

The security config setup works like this:

  • http is a builder (type HttpSecurity).

  • When you call authorizeRequests(), it gives you a 2nd builder (ExpressionInterceptUrlRegistry).

  • When you call antMatchers(), it gives you a 3rd builder (AuthorizedUrl).

  • When you call permitAll(), it returns you to the 2nd builder.

Which means you're calling accessDecisionManager() on the *Registry builder, not the AuthorizedUrl builder, i.e. the call is global, unrelated to the matcher.

Your indentations are wrong and that's why you're confused:

    .antMatchers("/my-filtered-url1", "/sub/my-filtered-url2/*")
        .permitAll()
    .accessDecisionManager(playerResourceDecisionManager())

The access manager and its underlying voters are not responsible for specifying which access rules should be applied to a particular URL, that is the job of access expression applied to the AuthorizedUrl, e.g.

  • permitAll() - Short for access("permitAll")

  • authenticated() - Short for access("authenticated")

  • hasRole("ADMIN") - Short for access("hasRole('ROLE_ADMIN')")

  • hasAuthority("HASHSERIAL") - Short for access("hasAuthority('HASHSERIAL')")

  • . . .

So, if you want a custom AccessDecisionVoter to vote on particular URLs, then you implement the supports(ConfigAttribute attribute) method of the voter to recognize a particular attribute, you register the voter globally, and then specify that the particular URLs requires it:

    .antMatchers("/my-filtered-url1", "/sub/my-filtered-url2/*")
        .hasAuthority("HASHSERIAL")
class HashSerialSecurityVoter implements AccessDecisionVoter<Object> {

    public boolean supports(ConfigAttribute attribute) {
        if ((attribute.getAttribute() != null)
                && attribute.getAttribute().equals("HASHSERIAL")) {
            return true;
        }
        else {
            return false;
        }
    }

    public boolean supports(Class<?> clazz) {
        return true;
    }

    public int vote(Authentication authentication, Object object,
                    Collection<ConfigAttribute> attributes) {
        // Your logic here
    }
}

Upvotes: 1

Related Questions