@PreAuthorize不能与方法安全规则和方法参数一起使用。

11

我正在为一个Spring项目添加Spring安全性。 该系统的架构是REST,用户可以访问不同的资源。

我想将个人信息的访问权限授予管理员和拥有此信息的用户。 我已经从简单的过滤用户配置文件开始:

在我的服务层中,我想使用方法注释并包括方法参数..

@PreAuthorize("hasRole('ROLE_ADMIN') or principal.userId == #id")
public Usuario getUser(int id) throws DAOException {
    ...
}

但这根本不起作用。 当请求此URL时(Web层),任何用户都可以查看所有配置文件(包括管理员和所有用户):

@RequestMapping(value="/user/{uid}", method=RequestMethod.GET)
    public ModelAndView getUser(@PathVariable int uid) throws DAOException {
        userDAO = new UsuarioJPADAO();
        userService.setUsuarioDAO(userDAO);

    return new ModelAndView("user", "user", userService.getUser(uid));
}

这是我的 security.xml 文件。

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
       xmlns:beans="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
       http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security-3.1.xsd">

<!-- Security Annotations -->
    <global-method-security 
        pre-post-annotations="enabled"/>

<http auto-config="true" use-expressions="true">
    <intercept-url pattern="/css/**" access="permitAll" />
    <intercept-url pattern="/images/**" access="permitAll" />
    <intercept-url pattern="/js/**" access="permitAll" />
    <intercept-url pattern="/favicon.ico" access="permitAll" />
    <intercept-url pattern="/login" access="permitAll" />

    <intercept-url pattern="/users" access="hasRole('ROLE_ADMIN')" />
    <intercept-url pattern="/users/page/*" access="hasRole('ROLE_ADMIN')" />
    <intercept-url pattern="/customers" access="hasRole('ROLE_ADMIN')" />
    <intercept-url pattern="/employees" access="hasRole('ROLE_ADMIN')" />

    <intercept-url pattern="/search/*" access="hasRole('ROLE_ADMIN')" />

    <intercept-url pattern="/*" access="hasAnyRole('ROLE_ADMIN, ROLE_EMPLOYEE, ROLE_PARTNER, ROLE_USER')" />
    <intercept-url pattern="/*/*" access="hasAnyRole('ROLE_USER, ROLE_ADMIN')" />
    <intercept-url pattern="/*/*/*" access="hasAnyRole('ROLE_USER, ROLE_ADMIN')" />
    <intercept-url pattern="/*/*/*/*" access="hasAnyRole('ROLE_USER, ROLE_ADMIN')" />
    <intercept-url pattern="/*/*/*/*/*" access="hasAnyRole('ROLE_USER, ROLE_ADMIN')" />
    <intercept-url pattern="/*/*/*/*/*/*" access="hasAnyRole('ROLE_USER, ROLE_ADMIN')" />
    <intercept-url pattern="/*/*/*/*/*/*/*" access="hasAnyRole('ROLE_USER, ROLE_ADMIN')" />
    <form-login login-page="/login" login-processing-url="/doLogin" 
                authentication-failure-url="/login?error"
                username-parameter="username" password-parameter="password"
                default-target-url="/default" />

    <logout invalidate-session="true" logout-success-url="/login?logout" logout-url="/logout"/>
</http>
<authentication-manager>
    <authentication-provider user-service-ref="UsuarioService">
    </authentication-provider>
</authentication-manager>    

我查看了Spring Security 3.1书籍,我的配置方式似乎与书中建议的一致。我阅读了其他Stack Overflow文章(在这里在这里),但没有成功。

更新: 添加application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"       
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.1.xsd
       http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security-3.1.xsd">

    <context:annotation-config />

<context:component-scan base-package="com.pe.fs" />

<mvc:annotation-driven />

<mvc:resources mapping="/**" location="/" />   

<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
    <property name="paramName" value="lang" />
</bean>
</mvc:interceptors>

<!-- DataSource -->
<bean id="jpaDataSource" class="oracle.jdbc.pool.OracleDataSource"
    destroy-method="close" 
    p:driverType="oracle.jdbc.OracleDriver" 
    p:user="**********"
    p:password="**********"
    p:uRL="jdbc:oracle:thin:@localhost:1521:XE"
/>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceXmlLocation" value="classpath*:META-INF/persistence.xml"></property>
    <property name="persistenceUnitName" value="freesunPU" />
    <property name="dataSource" ref="jpaDataSource" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
            <property name="showSql" value="false" />
        </bean>
    </property>
    <property name="loadTimeWeaver">
        <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
    </property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
    p:entityManagerFactory-ref="entityManagerFactory" />

<tx:annotation-driven mode="aspectj"/>

<context:load-time-weaver aspectj-weaving="autodetect" />

更新:我已将spring-security-aspects添加到POM文件中,但未作任何更改。其他答案中建议的更改已经测试过,但是像@PreAuthorize这样的注释仍然无法工作。这可能是上下文之间的问题吗?使用aspectJ可能是原因吗?

我做错了什么?


你的 Usuario getUser(int id) 方法是在某个接口中定义的吗?默认情况下,这种类型的注释可能不起作用(JDK代理被用于在运行时添加授权检查,它们只能针对接口方法)。 - Maksym Demidas
@MaksymDemidas 不是的。我的UsuarioService实现了UserDetailsService接口。我还使用了我自己的UsuarioDetails类,该类扩展了我的domain Usuario并实现了UserDetails接口。 - Spacemonkey
你好,怎么了? :) 请将 ""/////*"" 更改为 "/**": <intercept-url pattern="/**" access="hasAnyRole('ROLE_USER, ROLE_ADMIN')" /> - Yura
@Yura,我不再维护这个应用程序了,但感谢你的建议! - Spacemonkey
3个回答

9

最终我找到了解决方案。 在stackoverflow上面我发现了一些有用的答案。请点击这里这里查看。

我把global-method-security移动到了application-context.xml文件中,这个文件是我的服务的上下文。

<security:global-method-security 
    mode="aspectj"
    secured-annotations="enabled"
    jsr250-annotations="disabled"
    pre-post-annotations="enabled"/>

mode="aspectj"时,根据Javadoc的说法:

...可以用于指定应该使用AspectJ而不是默认的Spring AOP。如果设置了这个选项,则安全类必须使用来自spring-security-aspects模块的AnnotationSecurityAspect进行编织。

当然,我已经在POM中添加了spring-security-aspects

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

很高兴看到你找到了解决方案! - Maksym Demidas
移动 global-method-security 解决了我的问题。 - user227353

0

让它工作的另一种方法是在您的security.xml中添加以下代码

<intercept-url pattern="/user/**" access="hasRole('ROLE_ADMIN')" />

这将确保只有管理员可以访问以模式 /user/ 开头的资源。


0

添加新接口:

public interface UserService extends UserDetailsService {
    Usuario getUser(int id) throws DAOException
}

在您的用户服务中实现它,然后再试一次。Spring将能够使用JDK代理添加所请求的授权检查。

作为另一个选项,您可以配置Spring使用一些更重量级的库,如Javassist甚至AspectJ。在这种情况下,接口将不是必需的。

编辑。 确保global-method-security与您的用户服务bean在同一个Spring上下文中声明。


当添加接口时无法工作。我应该像在我的帖子中那样注释方法,而不是在接口中,对吗? - Spacemonkey
1
请展示您的Spring XML。您可以拥有两个Spring上下文(用于bean和Spring MVC)。您确定您的global-method-security元素是否在与用户服务相同的上下文中定义? - Maksym Demidas
我已经添加了Spring XML。事实上,我在这里没有声明bean。确实,我有另一个上下文:Spring MVC。全局方法安全性是在那里声明的。也许我应该将全局注释移到Spring XML中。 - Spacemonkey
我的意思是你完全不能使用任何注释吗?还是只是@PreAuthorize? - Maksym Demidas
我真的不知道。我想让@PreAuthorize工作起来。然后,如果需要,我会使用@Transactional或其他东西。 - Spacemonkey
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接