6

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?

Dave
  • 15,639
  • 133
  • 442
  • 830
  • Try to read [this](https://www.baeldung.com/spring-security-create-new-custom-security-expression) how to write custom security expression – Kamil Leis Sep 06 '18 at 21:06
  • This link talks about customizing security using Java but I would like an option that only relies on an XML configuration file and no Java coding. – Dave Sep 06 '18 at 22:01
  • This will be not possible since you do not have length method in the class that will evaluate the parts of expression out of the box and and in addition getProductList is not defined in common Principal class. See: [documentation](https://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html) and [javadoc WebSecurityExpressionRoot](https://docs.spring.io/spring-security/site/docs/4.2.7.RELEASE/apidocs/org/springframework/security/web/access/expression/WebSecurityExpressionRoot.html) – Kamil Leis Sep 07 '18 at 08:36

2 Answers2

2

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()"/>
briarheart
  • 1,906
  • 2
  • 19
  • 33
0

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" />