Spring Security: AccessDecisionVoter

2019-04-17 02:17发布

问题:

@Service
public class MyVoter implements AccessDecisionVoter<Entity> {

    @Override
    public boolean supports(ConfigAttribute attribute) {        
        boolean myBool = false;
        return myBool;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return clazz == Project.class;
    }

    @Override
    public int vote(Authentication authentication, Entity someEntity,
            Collection<ConfigAttribute> config) {
        return ACCESS_GRANTED;
    }
}

Can you explain me, how the first supports method is supposed to work? No matter how i change myBool, the vote-method is always invoked. It seems like only supports(Class clazz) has effect on the invokation.

Any ideas?

EDIT:

@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
ApplicationContext context;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable();
    http
        .authorizeRequests()
            .antMatchers("/").permitAll()
            .anyRequest().authenticated();
    http
        .formLogin()
            .loginPage("/login")
            .permitAll()               
            .and()
        .logout()
            .permitAll();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .inMemoryAuthentication()
            .withUser("user").password("password").roles("USER");
}

@Bean
public AffirmativeBased accessDecisionManager() {
    Map<String, AccessDecisionVoter> beans = context
            .getBeansOfType(AccessDecisionVoter.class);

    List<AccessDecisionVoter> decisionVoters = new ArrayList<>(
            beans.values());

    AffirmativeBased affirmativeBased = new AffirmativeBased(decisionVoters);
    return affirmativeBased;
}
}

This is basically my only config.

This is how I used the AccessDecisionManager:

    /* AUTHORIZATION */
    Authentication authentication = SecurityContextHolder.getContext()
            .getAuthentication();

    Collection<ConfigAttribute> config = new HashSet<ConfigAttribute>();
    config.add(new SecurityConfig("Something"));

    try {
        adm.decide(authentication, project, config);
    } catch (Exception e) {
        // .. Exception Handling
    }

回答1:

Without your Spring Security Application Context Configuration, It is hard to give a correct answer but for your question, The Javadoc for the method states the following;

Indicates whether this AccessDecisionVoter is able to vote on the 
passed ConfigAttribute.

This method is actual invoked for ConfigAttribute like the following "isAnonymous()" for WebExpressionVoter

<security:http auto-config="true" use-expressions="true">
        <security:intercept-url pattern="/login*"
            access="isAnonymous()" />
</security:http>

Or for RoleVoter something like "ROLE_ADMIN"

<security:http auto-config="true" use-expressions="true">
        <security:intercept-url pattern="/admin/**"
            access="ROLE_ADMIN" />
</security:http>

Both WebExpressionVoter and RoleVoter are implementations of AccessDecisionVoter. Unless you are not trying to evaluate any ConfigAttributes as mentioned above. Your method will never be invoked thus you won't see any effect whether you return true or false. Hope this helps.

EDIT

If you look at the AffirmativeBased AccessDecisionManager's decide method.

public void More ...decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
46            throws AccessDeniedException {
47        int deny = 0;
48
49        for (AccessDecisionVoter voter : getDecisionVoters()) {
50            int result = voter.vote(authentication, object, configAttributes);
51
52            if (logger.isDebugEnabled()) {
53                logger.debug("Voter: " + voter + ", returned: " + result);
54            }
55
56            switch (result) {
57            case AccessDecisionVoter.ACCESS_GRANTED:
58                return;
59
60            case AccessDecisionVoter.ACCESS_DENIED:
61                deny++;
62
63                break;
64
65            default:
66                break;
67            }
68        }
69
70        if (deny > 0) {
71            throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
72                    "Access is denied"));
73        }
74
75        // To get this far, every AccessDecisionVoter abstained
76        checkAllowIfAllAbstainDecisions();
77    }

It doesn't make use of supports(ConfigAttribute con) method at all. Thus you have to modify your coding to check as below in order to it to work.

@Service
public class MyVoter implements AccessDecisionVoter<Entity> {

    @Override
    public boolean supports(ConfigAttribute attribute) {        
        boolean myBool = false;
        return myBool;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return clazz == Project.class;
    }

    @Override
    public int vote(Authentication authentication, Entity someEntity,
            Collection<ConfigAttribute> config) {
        if(supports(config)) { // Add this check
            return ACCESS_GRANTED;
        } else {
            return ACCESS_DENIED; // Abstain Based on your requirement
        }
    }
}