Shiro authorization authorization using annotation does not work

Platform: Shiro 1.1.0, Spring 3.0.5

I am trying to protect MVC Controller methods using Shiro annotation. However, something is wrong with annotations. Regular calls work fine. Shiro also has nothing specific.

My configuration:

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

I can do the following correctly:

 @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"); } } 

But below does not work:

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

Am I missing something? Please, help.

+6
source share
6 answers

You were absolutely right. Seeing your comment, I started thinking about it. Well, then I found out that this is NOT an implementation problem with Shiro, but jar problems were not configured correctly. Shiro pom.xml should have a dependency for cglib2 too.

So, for me the following changes have changed:

  • Include all of these four jar files.

    aspectjrt-1.6.11.jar,
    aspectjweaver-1.6.12.jar,
    CGLIB-2.2.2.jar,
    ASM-3.3.1.jar,

If you are using maven then:
 <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> 

And finally, placing aop: aspectj-autoproxy in the webApplicationContext.xml file

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

Note. The two above configurations should be placed together in the same spring -webApplicationContext.xml. Otherwise it will not work. Also, remove the context: annotation-config if you used it in your configuration. context: the scan component already scans all annotations.

Once you start testing, set log4j to debug mode or (better) trace. Whenever you start your server, in your logs you will find the following entry:

08: 16: 24,684 DEBUG AnnotationAwareAspectJAutoProxyCreator: 537 - Creating an implicit proxy for the bean 'userController' with 0 common interceptor and 1 specific interceptor

+9
source

Guess Shiro was built when Spring 2.0 was in place. Shiros annotations (RequiresRoles, etc.) work well for Spring container-managed beans (service level), but it does not work with @Controller annotation. This is because @Controller is a component scanned by the Spring framework. I used AOP to solve the problem. Below is a solution that worked for me. For the solution below to work, you need to include the following four banks:

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

If you are using maven, then the below configuration will be useful.

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

Below is the controller class

 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"; } } 

Create the following aspect of the annotation (RequiresRoles). You can use the same principle to create a pointcut for 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())); } } 

In spring -webApplicationContext.xml, wherever you mention

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

Note The two above configurations should be placed together in the same spring -webApplicationContext.xml. Otherwise it will not work. Moreover, delete the context: annotation-config if you used it in your configuration. context: the scan component already scans all annotations.

+3
source

I used the spring-hibernate example from the example. To use annotations such as @RequiresPermissions and others, I tried the setup with a simple guide, the setup from this post, but I either could not compile or run the valid URLs. Therefore, I only commented on all @RequiresPermissions from ManageUserController and started using it in the implementation of the service. For example, in the DefaultUserService in the getAllUsers method, I added the @RequiresPermissions annotation ("user: manage"). Magically, the application now works as expected. Whenever the url management address is called, it displays a list page if the user has the user role: manages and throws the user to / unauthorized if the user does not have this permission.

I even configured the application to use mysql. To make permissions role-independent according to the new RBAC ( http://www.stormpath.com/blog/new-rbac-resource-based-access-control ), I created a new class called 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 

The role class is now configured as

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

And finally, SampleRealm as

  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); } 

He creates five tables as | permissions | | roles | | role-transfers | | users | | users_roles |

And permissions are independent of others. According to the new RBAC, you have two methods (explicit and implicit) for authorizing resources.

+1
source

If you avoid Spring XML and primarily use Java configuration and annotations, the easiest way to fix this is to add

 @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) 

for all of your @Controller classes. You need cglib on the way to the classes.

+1
source

You need to write an AuthorizationAttributeSourceAdvisor to enable Shiro bean annotations according to the Syro Documentation

If you wrote the ShiroConfiguration class, be sure to include this:

 @Bean(name = "lifecycleBeanPostProcessor") public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Bean @ConditionalOnMissingBean public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultSecurityManager securityManager) { // This is to enable Shiro 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; } 

ShiroConfiguration example on Github

0
source

I had the same problem. My fix changed my jersey version from 2.2 to 2.22.2, and all @RequiresPermissions worked on my controllers.

0
source

Source: https://habr.com/ru/post/899151/


All Articles