3

我是有关 Spring 和 Spring 安全框架的新手,并尝试使用 Spring Security v3.1.4 保护在最新稳定 Glassfish 构建上运行的 Java EE 7 REST 应用程序。

一切都很好,但我无法让“全局方法安全”工作!这是我的配置,任何帮助将不胜感激。

网页.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 应用程序上下文配置为预身份验证,如下所示:

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

以下是 Spring ACL 配置:

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

我要应用权限检查的方法如下所示:

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


我在哪里做错了?

4

2 回答 2

8

我解决了这个问题,所以我想在这里分享详细信息,以帮助其他人寻找 SS 的全局安全功能可能无法正常工作的原因。

感谢Arten Bilan为我指明了正确的方向。我的问题的直接答案可能是:如果您想保护未定义为 Spring Beans 的代码,您应该使用 "AspectJ auto-proxying" (mode="aspectj")。但是,您可能需要更多才能使其正常工作,就像我一样。

问题是,要启用 aspectj,您应该使用此处讨论的spring-security-aspects模块中的AnnotationSecurityAspect编织您的代码。尤其是Luke Taylor的帖子对我很有帮助。

为此,您应该使用 aspectj 编译器编译您的代码。如果您现在像我一样使用Maven,以下配置可能会有所帮助(如此所述):

首先使用模式aspectj设置:

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

为 spring-security-aspects 模块添加依赖:

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

最后添加以下 maven 插件和配置以进行编译时编织:

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

这为我做了工作。

于 2013-09-10T04:35:48.900 回答
3

看起来您应该遵循 Spring Security Reference Manual 的建议:

注释的方法只会对定义为 Spring bean 的实例进行保护(在启用方法安全的同一应用程序上下文中)。

这里讨论了一个类似的问题:<global-method-security> 如何通过 Spring-Security 在我的控制器上工作? 见最后一个帖子。

于 2013-09-07T08:40:21.020 回答