archangle
archangle

Reputation: 227

Spring Security - 'global-method-security' does not work

I am a newbie regarding Spring & Spring Security Frameworks and trying to secure a Java EE 7 REST App running on latest stable Glassfish build using Spring Security v3.1.4.

Everything is fine but i cannot manage to make 'global-method-security' work! Here are my configs, any help would be greatly appreciated.

web.xml:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/spring/*.xml
    </param-value>
</context-param>

<filter>
    <filter-name>filterChainProxy</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
    <filter-name>filterChainProxy</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>    

Spring application context is configured for pre-authentication and looks like:

<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor">
        <property name="alwaysUseJndiLookup" value="true" />
    </bean>

    <bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
        <sec:filter-chain-map request-matcher="ant">
            <sec:filter-chain pattern="/api/authentication" filters="none"/>
            <sec:filter-chain pattern="/**" filters="sif,requestHeaderAuthFilder,logoutFilter,etf,fsi"/>
        </sec:filter-chain-map>
    </bean>

    <bean id="sif" class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/>

    <sec:authentication-manager alias="authenticationManager">
        <sec:authentication-provider ref='preAuthenticatedAuthenticationProvider'/>
    </sec:authentication-manager>

    <bean id="preAuthenticatedAuthenticationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
        <property name="preAuthenticatedUserDetailsService">
            <bean id="userDetailsServiceWrapper"
                  class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
                <property name="userDetailsService" ref="preAuthenticatedUserDetailsService"/>
            </bean>         
        </property>
    </bean>

    <bean id="preAuthenticatedUserDetailsService"
          class="org.someUserDetailsService"/>

    <bean id="requestHeaderAuthFilder" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="principalRequestHeader" value="sometoken"/>      
    </bean>

    <bean id="preAuthenticatedProcessingFilterEntryPoint"
          class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/>

    <bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
        <constructor-arg value="/"/>
        <constructor-arg>
            <list>
                <bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
            </list>
        </constructor-arg>
    </bean>

    <bean id="servletContext" class="org.springframework.web.context.support.ServletContextFactoryBean"/>

    <bean id="etf" class="org.springframework.security.web.access.ExceptionTranslationFilter">
        <property name="authenticationEntryPoint" ref="preAuthenticatedProcessingFilterEntryPoint"/>
    </bean>

    <bean id="httpRequestAccessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
        <property name="allowIfAllAbstainDecisions" value="false"/>
        <property name="decisionVoters">
            <list>
                <ref bean="roleVoter"/>
                <ref bean="authenticatedVoter"/>
            </list>
        </property>
    </bean>

    <bean id="fsi" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>      
        <property name="securityMetadataSource">
            <sec:filter-security-metadata-source use-expressions="true">
                <!--<sec:intercept-url  pattern="/api/authentication" access="permitAll"/>-->
                <sec:intercept-url pattern="/**" access="isAuthenticated()"/>
            </sec:filter-security-metadata-source>
        </property>

    </bean>

    <bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter"/>
    <bean id="authenticatedVoter" class="org.springframework.security.web.access.expression.WebExpressionVoter"/>

    <bean id="securityContextHolderAwareRequestFilter" class="org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter"/>  

And following is the Spring ACL Configuration:

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


    <bean id="expressionHandler"             class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
        <property name="permissionEvaluator" ref="aclPermissionEvaluator" />
    </bean>

    <bean name="aclPermissionEvaluator"
          class="org.CustomPermissionEvaluator">
        <constructor-arg name="aclService" ref="aclService" />
        <property name="sidRetrievalStrategy" ref="someSidRetrievalStrategy" />
    </bean>   

    <bean id="lookupStrategy"
          class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
        <constructor-arg name="dataSource" ref="dataSource" />
        <constructor-arg name="aclCache" ref="aclCache" />
        <constructor-arg name="aclAuthorizationStrategy" ref="aclAuthorizationStrategy" />
        <constructor-arg name="auditLogger" ref="auditLogger" />
    </bean>

     <bean id="aclCache"
          class="org.springframework.security.acls.domain.EhCacheBasedAclCache">
        <constructor-arg>
            <bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
                <property name="cacheManager">
                    <bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" />
                </property>
                <property name="cacheName" value="aclCache" />
            </bean>
        </constructor-arg>
    </bean>

    <bean id="aclAuthorizationStrategy"
          class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
        <constructor-arg name="auths">
            <list>
                <!-- authority for taking ownership -->
                <bean
                    class="org.springframework.security.core.authority.GrantedAuthorityImpl">
                    <constructor-arg value="ROLE_ADMIN" />
                </bean>
                <!-- authority to modify auditing -->
                <bean
                    class="org.springframework.security.core.authority.GrantedAuthorityImpl">
                    <constructor-arg value="ROLE_ADMIN" />
                </bean>
                <!-- authority to make general changes -->
                <bean
                    class="org.springframework.security.core.authority.GrantedAuthorityImpl">
                    <constructor-arg value="ROLE_ADMIN" />
                </bean>
            </list>
        </constructor-arg>
        <property name="sidRetrievalStrategy" ref="someSidRetrievalStrategy"/>
    </bean>

    <bean id="someSidRetrievalStrategy" class="org.SomeSidRetrievalStrategyImpl"/>

    <bean id="auditLogger"
          class="org.springframework.security.acls.domain.ConsoleAuditLogger" />


    <jee:jndi-lookup 
        id="dataSource" 
        jndi-name="springacl" /> 


    <bean id="aclService" name="aclService"
          class="org.springframework.security.acls.jdbc.JdbcMutableAclService">
        <constructor-arg name="dataSource" ref="dataSource" />
        <constructor-arg name="lookupStrategy" ref="lookupStrategy" />
        <constructor-arg name="aclCache" ref="aclCache" />
        <property name="sidIdentityQuery" value="select currval(pg_get_serial_sequence('acl_sid', 'id'))" />
        <property name="classIdentityQuery" value="select currval(pg_get_serial_sequence('acl_class', 'id'))" />
    </bean>

And the method i want to apply permission check looks like:

    @GET
    @Path("/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    @PreAuthorize("hasPermission(#id, 'read')")
    public Project getProject(@PathParam("id") Long id) {
    }


Where do i make things wrong?

Upvotes: 3

Views: 8535

Answers (2)

archangle
archangle

Reputation: 227

I solved the problem so i want to share details here to help the others searching for the reasons why global security feature of SS might be not working.

Thanks to Arten Bilan for pointing me to the right direction. The direct answer of my question might be: if you want to secure your code which are not defined as Spring Beans, you should use "AspectJ auto-proxying" (mode="aspectj"). However, you may need more to make it work, as i did.

The thing is, to enable aspectj, you should woven your code with the AnnotationSecurityAspect from the spring-security-aspects module as discussed here. Especially Luke Taylor's post was helpful to me.

To make this happen, you should compile your code using aspectj compiler. If you are using Maven as me doing now, following configurations may be helpful (as discussed here):

First set using mode aspectj:

    <sec:global-method-security             
        pre-post-annotations="enabled"
        mode="aspectj"
        proxy-target-class="true">
        <sec:expression-handler ref="expressionHandler" />
    </sec:global-method-security>

Add dependency for spring-security-aspects module:

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-aspects</artifactId>
            <version>3.1.4.RELEASE</version>
        </dependency>

And finally add following maven plugin and configurations for compile time weaving:

        <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.4</version>
                <configuration>
                    <showWeaveInfo>true</showWeaveInfo>
                    <source>1.7</source>
                    <target>1.7</target>
                    <Xlint>ignore</Xlint>
                    <complianceLevel>1.7</complianceLevel>
                    <encoding>UTF-8</encoding>
                    <verbose>false</verbose>
                    <aspectLibraries>
                        <aspectLibrary>
                            <groupId>org.springframework.security</groupId>
                            <artifactId>spring-security-aspects</artifactId>
                        </aspectLibrary>
                    </aspectLibraries>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjrt</artifactId>
                        <version>${aspectj.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjtools</artifactId>
                        <version>${aspectj.version}</version>
                    </dependency>
                </dependencies>
            </plugin>

This did the work for me.

Upvotes: 8

Artem Bilan
Artem Bilan

Reputation: 121590

Looks like you should follow with recomendation from Spring Security Reference Manual:

The annotated methods will only be secured for instances which are defined as Spring beans (in the same application context in which method-security is enabled).

A similar problem is discussed here: How can <global-method-security> work on my controller by Spring-Security? See the last post.

Upvotes: 3

Related Questions