首先说一句,使用授权的前提当然是先要实现身份验证,也就是要保证用户登录之后才可能考虑授权的问题。关于身份验证之前已经写过了,还不清楚的童鞋可以点这里
上一篇文章介绍了Shiro中授权的一些基础知识和原理。学了就要用,本篇文章就介绍如何在项目中应用Shiro的授权。这里为了方便大家阅读,先贴出上一篇文章中分析出的授权流程:
Subject.hasRole(...)
后doGetAuthorizationInfo(PrincipalCollection principals)
方法来得到的) protected boolean hasRole(String roleIdentifier, AuthorizationInfo info) {
return info != null && info.getRoles() != null && info.getRoles().contains(roleIdentifier);
}
而对于Subject.isPermitted(...)
稍微会显得复杂一些,前三步都一样,就是AuthorizingRealm对于判断isPermitted的逻辑稍微有点不一样。它会先将权限字符串转换成Permission,将用户的权限转换成List<Permission>
,这里的List<Permission>
由三个部分组成:
AuthorizationInfo.getObjectPermissions()
得到Permission实例集合AuthorizationInfo. getStringPermissions()
得到字符串集合并通过PermissionResolver解析为Permission实例接下来,用Permission和List进行匹配,其中遍历用户权限的匹配方法就是implies(...)
,这个单词可以理解成“蕴含”的意思,这样就比较好理解了。
由于各组件角色定位都很清楚,所以我们这里实现授权只需要实现AuthorizingRealm就可以了。那么Shiro是否有线程的AuthorizingRealm供我们使用呢?答案是肯定的。AuthorizingRealm的实现有:JdbcRealm、InitRealm、PropertiesRealm等。由于我决定将授权数据存在数据库中(大多数情况也应该是这样),所以这里使用JdbcRealm。注意:下文直接用细粒度的模式来实现授权(即用户到角色,角色关联到权限的方式)
首先,我们要建两张表,一张是用户到角色的关联表,一张的角色到权限的关联表。
id | username | role_name |
---|
id | role_name | permission |
---|
Shiro提供了两个可配置的SQL语句来取得某用户的所有权限:userRolesQuery和permissionsQuery,其中userRolesQuery是用来得到该用户的所有角色,而permissionsQuery是根据该用户的所有角色来取出所有的权限。这里我们配置的SQL为:
userRolesQuery= select role_name from user_roles where username = ?
permissionsQuery = select permission from roles_permissions where role_name = ?
这也是Shiro提供的默认的SQL内容,由于我们表名和列名都是参照该SQL建立的,所以直接使用默认的SQL即可。
另外还有一点需要注意的是,JdbcRealm默认是不支持权限查找的,也就是permissionsQuery默认是不会执行的。需要配置permissionsLookupEnabled为true,才能使之生效。另外别忘了配置数据源datasource,下面贴上配置文件:
<bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"></property>
<property name="authenticationQuery" value="select password from user where username = ?"></property>
<property name="dataSource" ref="dataSource"></property>
<property name="permissionsLookupEnabled" value="true"></property>
</bean>
配置好后就可以像上一篇文章里写的那样使用了:
比如我们在Controller中写一个方法
@RequiresPermissions("news:view")
@RequestMapping("/newsList")
public String newsList() {
return "newsList";
}
当该用户没有news:view权限时,就会抛出UnauthorizedException异常,我们捕获该类异常并提供“未授权”页面返回给用户。在Spring中是可以配置一个全局的ExceptionHandler的。当然在Spring3.2之后的版本还提供了@ControllerAdvice来增强Controller,其中定义的所有@ExceptionHandler方法将会应用于所有的@RequestMapping方法,那么我们可以这么来定义异常处理类:
@ControllerAdvice
public class DefaultExceptionHandler {
@ExceptionHandler( { UnauthorizedException.class })
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public ModelAndView processUnauthorizedException(
NativeWebRequest request, UnauthorizedException e) {
ModelAndView mv = new ModelAndView();
mv.addObject("contextPath", request.getContextPath());
mv.addObject("exception", e);
mv.setViewName("special/unauthorized");
return mv;
}
}
这里若启用声明式的鉴权,还需要启用aspectJ自动代理,并支持对类的代理。
<aop:aspectj-autoproxy proxy-target-class="true" />
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
原文地址:http://blog.csdn.net/u012345283/article/details/44133561