【问题标题】:Shiro Authorization Permission check using Annotation not working使用注释的 Shiro 授权权限检查不起作用
【发布时间】:2011-12-06 07:45:28
【问题描述】:

平台:Shiro 1.1.0、Spring 3.0.5

我正在尝试使用 Shiro 注释来保护 MVC 控制器方法。但是注释有问题。定期通话工作正常。 Shiro 调试中也没有什么特别的。

我的 shiro 配置:

<!-- Security Manager -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="sessionMode" value="native" />
        <property name="realm" ref="jdbcRealm" />
        <property name="cacheManager" ref="cacheManager"/>
    </bean>

    <!-- Caching -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManager" ref="ehCacheManager" />
    </bean>

    <bean id="ehCacheManager"
        class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" />

    <bean id="sessionDAO"
        class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO" />

    <bean id="sessionManager"
        class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <property name="sessionDAO" ref="sessionDAO" />
    </bean>


    <!-- JDBC Realm Settings -->
    <bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">
        <property name="name" value="jdbcRealm" />
        <property name="dataSource" ref="dataSource" />
        <property name="authenticationQuery"
            value="SELECT password FROM system_user_accounts WHERE username=? and status=1" />
        <property name="userRolesQuery"
            value="SELECT role_name FROM system_roles r, system_user_accounts u, system_user_roles ur WHERE u.user_id=ur.user_id AND r.role_id=ur.role_id AND u.username=?" />
        <property name="permissionsQuery"
            value="SELECT permission_name FROM system_roles r, system_permissions p, system_role_permission rp WHERE r.role_id=rp.role_id AND p.permission_id=rp.permission_id AND r.role_name=?" />
        <property name="permissionsLookupEnabled" value="true"></property>
    </bean>

    <!-- Spring Integration -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

    <!-- Enable Shiro Annotations for Spring-configured beans. Only run after 
        the lifecycleBeanProcessor has run: -->
    <bean id="annotationProxy"
        class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
        depends-on="lifecycleBeanPostProcessor" />
    <bean
        class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager" />
    </bean>

    <!-- Secure Spring remoting: Ensure any Spring Remoting method invocations 
        can be associated with a Subject for security checks. -->
    <bean id="secureRemoteInvocationExecutor"
        class="org.apache.shiro.spring.remoting.SecureRemoteInvocationExecutor">
        <property name="securityManager" ref="securityManager" />
    </bean>

    <!-- Shiro filter -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="/login" />
        <property name="successUrl" value="/dashboard" />
        <property name="unauthorizedUrl" value="/error" />
        <property name="filterChainDefinitions">
            <value> 
                <!-- !!! Order matters !!! -->
                /authenticate = anon
                /login = anon
                /logout = anon
                /error = anon
                /** = authc
            </value>
        </property>
    </bean>

我可以正常工作:

@RequestMapping(value="/form") 
public String viewPatientForm(Model model, @RequestParam(value="patientId", required=false) Long patientId){    
   if (!SecurityUtils.getSubject().isPermitted("hc:viewPatient")){
      logger.error("Operation not permitted");
      throw new AuthorizationException("No Permission"); 
   }
}

但以下不起作用:

@RequiresPermissions("hc:patientView")
    @RequestMapping(value="/form")
    public String viewPatientForm(Model model, @RequestParam(value="patientId", required=false) Long patientId){    

我错过了什么吗?请帮忙。

【问题讨论】:

  • 您好,我也遇到了与您在此处发布的问题非常相似的问题:stackoverflow.com/questions/6859432/…,到目前为止还没有具体的问题。我真的很想知道为什么人们对此如此沉默。您以任何方式找到任何解决方案?
  • 还没有解决办法,因为我不知道问题出在哪里。调试中也没有任何内容。我认为 Shiro 团队不再在 Stack Overflow 上发帖。不过shiro-user.582556.n2.nabble.com 有很好的活动。我们应该去那里拿东西。

标签: spring shiro


【解决方案1】:

你完全正确。看到你的评论后,我开始考虑。然后我发现这不是 Shiro 的实现问题,但是 jar 依赖项没有正确配置。 Shiro 的 pom.xml 也应该对 cglib2 有依赖。

所以以下更改对我有用:

  1. 包括所有这四个 jar 文件。

    aspectjrt-1.6.11.jar,
    aspectjweaver-1.6.12.jar,
    cglib-2.2.2.jar,
    asm-3.3.1.jar,

如果您使用的是 Maven,那么:
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.6.11</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.6.12</version>
</dependency>    
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

最后将 aop:aspectj-autoproxy 放在 webApplicationContext.xml 中

<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- Annotation, so that it's easier to search controllers/components -->
<context:component-scan base-package="com.pepsey.soft.web.controller"/>

注意:以上两个配置应该放在同一个spring-webApplicationContext.xml中。否则它将无法正常工作。此外,如果您在配置中使用了 context:annotation-config,请删除它。 context:component-scan 已经扫描了所有的注解。

一旦开始测试,请将 log4j 设置为调试或(更好的)跟踪模式。每当您启动服务器时,您都会在日志中的某处找到以下条目:

08:16:24,684 调试 AnnotationAwareAspectJAutoProxyCreator:537 - 使用 0 common 为 bean 'userController' 创建隐式代理 拦截器和 1 个特定拦截器

【讨论】:

    【解决方案2】:

    Guess Shiro 是在 Spring 2.0 到位时构建的。 Shiro 的注解(RequiresRoles 等)适用于 Spring 容器管理的 bean(服务层),但不适用于 @Controller 注解。这是因为 @Controller 正在被 spring 框架扫描组件。我使用 AOP 来解决这个问题。以下是对我有用的解决方案。 要使以下解决方案起作用,您必须包含以下四个罐子:

    aspectjrt-1.6.11.jar
    aspectjweaver-1.6.12.jar
    cglib-2.2.2.jar
    asm-3.3.1.jar
    

    如果你使用的是 maven,那么下面的配置会很有帮助。

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.6.11</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.6.12</version>
    </dependency>    
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.2.2</version>
    </dependency> 
    

    下面是一个控制器类

    import org.apache.shiro.authz.annotation.RequiresRoles;
    
    @Controller
    public class PatientController {
    
        @RequiresRoles(“admin,warden”)
        @RequestMapping(value="/form") 
        public String viewPatientForm(Model model,  @RequestParam(value="patientId", required=false) Long patientId){    
    
            return “somePatientFormJsp”;
        }
    }
    

    为注释创建以下方面 (RequiresRoles)。您可以使用相同的原则为 RequiresPermission 创建切入点。

    import java.util.Arrays;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authz.annotation.RequiresRoles;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class WebAuthorizationAspect {
    
        @Before("@target(org.springframework.stereotype.Controller) && @annotation(requiresRoles)")
        public void assertAuthorized(JoinPoint jp, RequiresRoles requiresRoles) {
            SecurityUtils.getSubject().checkRoles(Arrays.asList(requiresRoles.value()));
        }
    }
    

    在你提到的 spring-webApplicationContext.xml 中

    <aop:aspectj-autoproxy proxy-target-class="true"/>
    <!-- Annotation, so that it's easier to search controllers/components -->
    <context:component-scan base-package="com.example.controller"/>
    

    注意:以上两个配置要一起放在 spring-webApplicationContext.xml 中。否则将无法正常工作。此外,remove context:annotation-config 如果您在配置中使用过它。 context:component-scan 已经扫描了所有的注解。

    【讨论】:

    • 德巴什,我明白你的意思。但是,Service 层也是在我的应用程序中扫描的组件,因此我无法真正缩小 [at]Controller 和 [at]Service 之间的真正区别。无论如何,让我试试你的方法。我希望 Shiro 团队能够提出更直接的内置内容。谢谢。
    • 您好,我刚刚尝试了您的建议,它似乎有效。但我有一个问题。我们应该把 的东西放在哪里?在配置应用程序上下文中或在dispather应用程序上下文中或两者兼而有之?
    【解决方案3】:

    如果您避免使用 Spring XML 并主要使用 Java 和注释配置,解决此问题的最简单方法是添加

    @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
    

    致所有@Controller 课程。您需要在类路径上使用 cglib。

    【讨论】:

    • 这对我来说非常有效,谢谢让我发疯!我不需要显式添加 cglib,或进行任何其他依赖项更改。
    【解决方案4】:

    我只使用了示例中的 spring-hibernate 示例。要使用 @RequiresPermissions 之类的注释和其他注释,我尝试了 shiro 手册中的配置,这篇文章中的配置,但我要么编译失败,要么运行有效的 url。所以我只注释了来自 ManageUserController 的所有 @RequiresPermissions 并开始在服务实现中使用它。例如,在 getAllUsers 方法的 DefaultUserService 中,我添加了注释 @RequiresPermissions("user:manage")。神奇的是,现在应用程序按预期工作。每当调用 url manageUsers 时,如果用户具有角色 user:manage,它会显示列表页面,如果用户没有该权限,则将用户扔到 /unauthorized。

    我什至将应用程序配置为使用 mysql。为了根据新的 RBAC(http://www.stormpath.com/blog/new-rbac-resource-based-access-control) 使权限独立于角色,我创建了一个名为 Permission as 的新类

    @Entity
    @Table(name = "permissions")
    @Cache(usage= CacheConcurrencyStrategy.READ_WRITE)
    public class Permission {
        @Id
        @GeneratedValue
        private Long id;
        private String element;
        private String description;
        // setter and getter
    

    现在角色类被配置为

     @CollectionOfElements
        @JoinTable(name="roles_permissions")
        @Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
        public Set<Permission> getPermissions() {
            return permissions;
        }
    

    最后是 SampleRealm

     for (Role role : user.getRoles()) {
            info.addRole(role.getName());
    
            System.out.println("Roles " + role.getName());
    
            // Get permissions first
            Set<Permission> permissions = role.getPermissions();
            Set<String> permissionsStrings = new HashSet<String>();
    
            for (Permission permission : permissions) {
                permissionsStrings.add(permission.getelement());
                System.out
                        .println("Permissions " + permission.getelement());
            }
    
            info.addStringPermissions(permissionsStrings);
        }
    

    它创建了五个表作为 |权限 | |角色 | |角色权限 | |用户 | |用户角色 |

    并且权限独立于任何其他权限。根据新的 RBAC,您可以通过两种方式(显式和隐式)授权资源。

    【讨论】:

      【解决方案5】:

      您需要编写AuthorizationAttributeSourceAdvisor 以按照Shiro documentation 启用Shiro 的注释bean

      如果您编写了ShiroConfiguration 类,请确保包含以下内容:

      @Bean(name = "lifecycleBeanPostProcessor")
      public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
          return new LifecycleBeanPostProcessor();
      }
      
      @Bean
      @ConditionalOnMissingBean
      public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultSecurityManager securityManager) {
          // This is to enable Shiro's security annotations
          AuthorizationAttributeSourceAdvisor sourceAdvisor = new AuthorizationAttributeSourceAdvisor();
          sourceAdvisor.setSecurityManager(securityManager);
          return sourceAdvisor;
      }
      
      @ConditionalOnMissingBean
      @Bean(name = "defaultAdvisorAutoProxyCreator")
      @DependsOn("lifecycleBeanPostProcessor")
      public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
          DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
          proxyCreator.setProxyTargetClass(true);
          return proxyCreator;
      }
      

      Example ShiroConfiguration on Github

      【讨论】:

        【解决方案6】:

        我遇到了同样的问题。我的解决方法是将我的球衣版本从 2.2 更改为 2.22.2,并且所有 @RequiresPermissions 都在我的控制器上运行。

        【讨论】:

          猜你喜欢
          • 2016-09-13
          • 2019-11-23
          • 2012-05-11
          • 2021-07-30
          • 2016-12-08
          • 1970-01-01
          • 1970-01-01
          • 2015-05-24
          • 2013-02-24
          相关资源
          最近更新 更多