Shiro授权简单来说分为两种类型:
对于粗粒度来说,若角色对应权限有改变的话,那么则需要更改代码,很不方便。而细粒度的好处显而易见,所以一般项目中应该都采用细粒度的权限配置。
那么Shiro中是如何来完成权限检验的呢?
通过调用Subject.hasRoles(...)
或Subject.isPermitted(...)
另外还有Subject.checkRoles(...)
或Subject.checkPermissions(...)
前两种和后两种的区别是,后两种在没有权限时会抛出异常。
关于角色,相信你都会定义,因为就是一个名字,比如admin、vip、user等。
而对于权限的定义,就需要有一套规则了。在Shiro中我们可以这么来定义权限字符串:资源标识符:操作:对象实例ID。另外这里支持通配符配置,
比如:
user:create
表示对user有create的权限(这里省略了:对象实例ID,等价于user:create:*
)
user:*
表示对user拥有所有操作的权限(create,update,delete,view)
user:view:1
表示对user的1实例有view的权限
差不多就类似这样,就不多说了。Shiro内部会有一个PermissionResolver来负责将这类字符串解析为Permission对象。
下面分析一下授权的流程:
Subject.hasRole(...)
后 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实例而JdbcRealm的实现是使用了stringPermissions,可能第一想法以为它是用了RolePermissionResolver吧。
好了,接下来,用Permission和List进行匹配,其中遍历用户权限的匹配方法就是implies(...)
,这个单词可以理解成“蕴含”的意思,这样就比较好理解了。
private boolean isPermitted(Permission permission, AuthorizationInfo info) {
Collection<Permission> perms = getPermissions(info);
if (perms != null && !perms.isEmpty()) {
for (Permission perm : perms) {
if (perm.implies(permission)) {
return true;
}
}
}
return false;
}
举个例子,比如user:*
权限impliesuser:create
权限,这就是一种蕴含关系。
一般有编程式和声明式两种:
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有权限
} else {
//无权限
}
@RequiresRoles("admin")
public void hello() {
//有权限
}
没有权限的话就抛出相应的异常。而我们只需要捕获异常做出相应的处理就可以了,在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;
}
}
当然对于编程式的也可以直接使用subject.checkRoles(“admin”)
,这样一来也会抛出异常并被ExceptionHandler捕获了。
这里若启用声明式的鉴权,还需要启用aspectJ自动代理,并支持对类的代理。
<aop:aspectj-autoproxy proxy-target-class="true" />
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
下一篇文章会写一个Spring项目中整合JdbcRealm来实现鉴权的实例。
原文地址:http://blog.csdn.net/u012345283/article/details/44034695