使用场景

  • 比如某个领导因故不能登录网站进行一些操作,他想把网站上的工作委托给他的秘书,但却不想把账号、密码告诉她。此时我们可以使用Shiro的RunAs功能,即允许一个用户假装成另一个用户的身份进行访问。

示例

  • 示例基于第十六章,主要功能是用户、公司、角色和资源之间关系的调整,如修改用户所属公司、所有角色,资源所需权限、角色所有资源等。
  • 数据库:新增user_runas表,映射原用户与代用户,必须有相应记录才能切换身份。
  • 实体:新增UserRunAs类。
public class UserRunAs implements Serializable {
    private Long fromUserId;//授予身份帐号A(老板)
    private Long toUserId;//被授予身份帐号B(秘书)
}
  • dao:新增RunAs增删查改方法。
  • service:新增RunAs增删查改方法。
@Service("userRunAsService")
public class UserRunAsServiceImpl implements UserRunAsService {
    @Autowired
    private UserRunAsDao userRunAsDao;

    /**
     * @Author haien
     * @Description 授予身份,实际就是新增记录
     * @Date 2019/4/10
     * @Param [fromUserId, toUserId]
     * @return void
     **/
    @Override
    public void grantRunAs(Long fromUserId, Long toUserId) {
        userRunAsDao.grantRunAs(fromUserId, toUserId);
    }

    /**
     * @Author haien
     * @Description 回收身份,实际就是删除记录
     * @Date 2019/4/10
     * @Param [fromUserId, toUserId]
     * @return void
     **/
    @Override
    public void revokeRunAs(Long fromUserId, Long toUserId) {
        userRunAsDao.revokeRunAs(fromUserId, toUserId);
    }

    /**
     * @Author haien
     * @Description 关系存在判断,实际就是查找
     * @Date 2019/4/10
     * @Param [fromUserId, toUserId]
     * @return boolean
     **/
    @Override
    public boolean exists(Long fromUserId, Long toUserId) {
        return userRunAsDao.exists(fromUserId, toUserId);
    }

    /**
     * @Author haien
     * @Description 根据秘书查找其代理的老板
     * @Date 2019/4/10
     * @Param [toUserId]
     * @return java.util.List<java.lang.Long>
     **/
    @Override
    public List<Long> findFromUserIds(Long toUserId) {
        return userRunAsDao.findFromUserIds(toUserId);
    }

    /**
     * @Author haien
     * @Description 根据老板查找其委托的秘书
     * @Date 2019/4/10
     * @Param [fromUserId]
     * @return java.util.List<java.lang.Long>
     **/
    @Override
    public List<Long> findToUserIds(Long fromUserId) {
        return userRunAsDao.findToUserIds(fromUserId);
    }
}
  • controller:新增RunAs增删查改方法,真正实现身份切换。
//判断当前用户是否为代理老板
Subject subject = SecurityUtils.getSubject();
//当前用户是老板,但是个代理老板
if(subject.isRunAs()) {
    //获取之前的身份信息,一个用户可以切换很多身份,之前的身份使用栈存储
    String previousUsername =
            (String)subject.getPreviousPrincipals().getPrimaryPrincipal(); 
            //得到最近一个
}
  • Subject.isRunAs(): 判断当前用户是否是代理得来的身份。
  • Subject.getPreviousPrincipals():得到之前的身份,一个用户可多次切换身份,它们被存在栈中,栈顶为最近用过的身份。
@RequestMapping("/switchTo/{switchToUserId}")
public String switchTo(@CurrentUser User loginUser, //当前用户
                @PathVariable("switchToUserId") Long switchToUserId, //要切换到的用户
                RedirectAttributes redirectAttributes) {

    //判断当前用户要切换的身份是否为自身

    //查找目标用户的身份
    User switchToUser = userService.findOne(switchToUserId);

    //切换
    subject.runAs(new SimplePrincipalCollection(switchToUser.getUsername(), ""));
}
  • subject.runAs(PrincipalCollection, realmName): 切换到指定用户。
Subject subject = SecurityUtils.getSubject();
if(subject.isRunAs()) {
    //切换回上一个身份;如果A切换到B,B又切换到C,那么需要执行两次才能回到A
    subject.releaseRunAs();
}
  • subject.releaseRunAs():切换回上一个身份。

  • 测试:

  1. 修改数据库,使admin的角色为超管,而zhang无角色,即zhang比admin低。
  2. 登录admin账号,点击切换身份,将身份授予用户zhang。
  3. 退出,登录账号zhang,点击切换身份,切换到admin,刷新当前页面,原本空白的菜单栏会出现all选项,因为拥有admin的权限了。