Martin
Martin

Reputation: 3753

Can't get @PreAuthorize to work with role hierarchies

I have a custom authentication provider that's returning a concrete implementation of 'AbstractAuthenticationTokenwithROLE_ADMINonly. I have a method annotated with@PreAuthorize("hasRole('ROLE_USER')")`. I'm trying to set up a role hierarchy to give my admin user access to this method.

I have the following in my spring-security.xml:

<beans:bean id="roleHierarchy" class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
    <beans:property name="hierarchy">
        <beans:value>
            ROLE_ADMIN > ROLE_USER
        </beans:value>
    </beans:property>
</beans:bean>

<beans:bean id="methodExpressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
    <beans:property name="roleHierarchy" ref="roleHierarchy" />
</beans:bean>

<sec:global-method-security pre-post-annotations="enabled">
    <sec:expression-handler ref="methodExpressionHandler" />
</sec:global-method-security>

The request to the protected method gets denied, although it works if I change the annotation to @PreAuthorize("hasRole('ROLE_ADMIN')").

I put a breakpoint on the AccessDeniedException, which was being thrown from AffirmativeBased.decide(...). The issue appears to be that the PreInvocationAuthorizationAdviceVoter's expressionHandler is null. That suggests to me that there's something wrong in my wiring up of my roleHierarchy or methodExpressionHandler.

I there anything obviously wrong with my spring-security.xml? Is there something I'm misunderstanding about how this stuff should be working?

Upvotes: 1

Views: 1642

Answers (2)

Martin
Martin

Reputation: 3753

Oh how silly of me... There's not enough context in my question to answer this, but I've resolved it.

I had <global-method-security> in both my spring-security.xml and my dispatcher-servlet.xml. I was only making changes within spring-security.

The give-away was when I put a break point on DefaultMethodSecurityExpressionHandler's constructor. It was being called twice. One was having setRoleHierarchy called, the other wasn't.

The solution was:

  1. move the common role hierarchy definition out to a separate file to be imported by both spring-security.xml and dispatcher-servlet.xml.
  2. Move <global-method-security> and the methodExpressionHandler bean from spring-security.xml to dispatcher-servlet.xml.

Upvotes: 1

Maksym Demidas
Maksym Demidas

Reputation: 7817

Try to inject roleHierarhy into roleVoter. Example from the official documentation:

<bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter">
    <constructor-arg ref="roleHierarchy" />
</bean>
<bean id="roleHierarchy"
        class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
    <property name="hierarchy">
        <value>
            ROLE_ADMIN > ROLE_STAFF
            ROLE_STAFF > ROLE_USER
            ROLE_USER > ROLE_GUEST
        </value>
    </property>
</bean>

Upvotes: 0

Related Questions