Spring中集成Shiro授权实例

授权流程回顾

首先说一句,使用授权的前提当然是先要实现身份验证,也就是要保证用户登录之后才可能考虑授权的问题。关于身份验证之前已经写过了,还不清楚的童鞋可以点这里

上一篇文章介绍了Shiro中授权的一些基础知识和原理。学了就要用,本篇文章就介绍如何在项目中应用Shiro的授权。这里为了方便大家阅读,先贴出上一篇文章中分析出的授权流程:

  1. 当我们调用Subject.hasRole(...)
  2. 首先会委托给securityManager来处理,而securityManager内部有一个Authorizer来做真正的授权,默认实现为ModularRealmAuthorizer
  3. ModularRealmAuthorizer可以根据多个Realm来判断是否拥有相应的角色,其中只要某个Realm匹配,则返回true
  4. AuthorizingRealm对于判断hasRole的逻辑也很简单(这里的AuthorizationInfo是通过覆盖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实例
  • 然后获取用户的角色,并通过RolePermissionResolver解析角色对应的权限集合(默认没有实现,可以自己提供)

接下来,用PermissionList进行匹配,其中遍历用户权限的匹配方法就是implies(...),这个单词可以理解成“蕴含”的意思,这样就比较好理解了。

由于各组件角色定位都很清楚,所以我们这里实现授权只需要实现AuthorizingRealm就可以了。那么Shiro是否有线程的AuthorizingRealm供我们使用呢?答案是肯定的。AuthorizingRealm的实现有:JdbcRealmInitRealmPropertiesRealm等。由于我决定将授权数据存在数据库中(大多数情况也应该是这样),所以这里使用JdbcRealm。注意:下文直接用细粒度的模式来实现授权(即用户到角色,角色关联到权限的方式)

准备工作

首先,我们要建两张表,一张是用户到角色的关联表,一张的角色到权限的关联表。

  • 首先是用户到角色的关联表 user_roles
idusernamerole_name
  • 然后是角色到权限关联表 roles_permissions
idrole_namepermission

配置Shiro

Shiro提供了两个可配置的SQL语句来取得某用户的所有权限:userRolesQuerypermissionsQuery,其中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默认是不会执行的。需要配置permissionsLookupEnabledtrue,才能使之生效。另外别忘了配置数据源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>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值