How do I restrict access to a URL using Spring security and a property from the spring security object?

Viewed 1658

I'm using Spring 5.1 and Spring security 4.2. I'm configured access rules using an XML file. My question is, how do I write an intercept rule (access control to a URL) based on a property in the Spring security context? That is, I have a variable

productList

in the security context that is of type java.util.ArrayList. I would like to restrict access to a URL if this list is empty or null. How do I write this? I have

<http name="defaultSecurity" security-context-repository-ref="myContextRepository"
    auto-config="false" use-expressions="true" authentication-manager-ref="authenticationManager"
    entry-point-ref="loginUrlAuthenticationEntryPoint">
    ...
    <intercept-url pattern="/myurl" access="length(principal.productList) > 0" />
    ...
</http>

but of course, teh above

length(principal.productList) > 0   

expression is completely wrong. Is there a right way to write it?

2 Answers

Security related expressions have quite limited set of operations in Spring. You can extend this set by providing custom implementation of org.springframework.security.access.expression.SecurityExpressionOperations interface. Here is a brief guide how to do it:

  1. Create wrapper over SecurityExpressionOperations and implement desired operations:
class MySecurityExpressionOperations implements SecurityExpressionOperations {
    private SecurityExpressionOperations delegate;

    public MySecurityExpressionOperations(SecurityExpressionOperations delegate) {
        this.delegate = delegate;
    }

    public boolean hasProducts() {
        MyUser user = (MyUser) delegate.getAuthentication().getPrincipal();
        return !user.getProductList().isEmpty();
    }

    // Other methods
}
  1. Extend org.springframework.security.web.access.expression.WebExpressionVoter and replace standard expression handler:
class MyWebExpressionVoter extends WebExpressionVoter {
    public MyWebExpressionVoter() {
        setExpressionHandler(new DefaultWebSecurityExpressionHandler() {
            @Override
            protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi) {
                SecurityExpressionOperations delegate = super.createSecurityExpressionRoot(authentication, fi);
                return new MySecurityExpressionOperations(delegate);
            }
        });
    }
 }
  1. Provide custom access decision manager:
<bean id="affirmativeBased" class="org.springframework.security.access.vote.AffirmativeBased">
    <constructor-arg>
        <list>
            <bean class="my.company.MyWebExpressionVoter"/>
        </list>
    </constructor-arg>
</bean>
  1. Apply custom access decision manager:
<http pattern="/**" use-expressions="true" access-decision-manager-ref="affirmativeBased">
    <!-- ... -->
</http>
  1. Protect one of URLs with additional security operation:
<intercept-url pattern="/products" access="hasProducts()"/>

if you are looking a to check the number of elements in productList here is a workaround for that :

 <intercept-url pattern="/myurl" access="principal.productList.size() > 0" />
Related