Shiro 授予身份和切換身份

2020-12-04 09:49 更新

授予身份及切換身份

在一些場景中,比如某個(gè)領(lǐng)導(dǎo)因?yàn)橐恍┰虿荒苓M(jìn)行登錄網(wǎng)站進(jìn)行一些操作,他想把他網(wǎng)站上的工作委托給他的秘書,但是他不想把帳號 / 密碼告訴他秘書,只是想把工作委托給他;此時(shí)和我們可以使用 Shiro 的 RunAs 功能,即允許一個(gè)用戶假裝為另一個(gè)用戶(如果他們允許)的身份進(jìn)行訪問。

本章代碼基于《第十六章 綜合實(shí)例》,請先了解相關(guān)數(shù)據(jù)模型及基本流程后再學(xué)習(xí)本章。

表及數(shù)據(jù) SQL

請運(yùn)行 shiro-example-chapter21/sql/ shiro-schema.sql 表結(jié)構(gòu)
請運(yùn)行 shiro-example-chapter21/sql/ shiro-schema.sql 數(shù)據(jù)

實(shí)體

具體請參考 com.github.zhangkaitao.shiro.chapter21 包下的實(shí)體。

public class UserRunAs implements Serializable {
    private Long fromUserId;//授予身份帳號
    private Long toUserId;//被授予身份帳號
}

該實(shí)體定義了授予身份帳號(A)與被授予身份帳號(B)的關(guān)系,意思是 B 帳號將可以假裝為 A 帳號的身份進(jìn)行訪問。

DAO

具體請參考 com.github.zhangkaitao.shiro.chapter21.dao 包下的 DAO 接口及實(shí)現(xiàn)。

Service

具體請參考 com.github.zhangkaitao.shiro.chapter21.service 包下的 Service 接口及實(shí)現(xiàn)。

public interface UserRunAsService {
    public void grantRunAs(Long fromUserId, Long toUserId);
    public void revokeRunAs(Long fromUserId, Long toUserId);
    public boolean exists(Long fromUserId, Long toUserId);
    public List<Long> findFromUserIds(Long toUserId);
    public List<Long> findToUserIds(Long fromUserId);
}

提供授予身份、回收身份、關(guān)系存在判斷及查找 API。

Web 控制器 RunAsController

該控制器完成:授予身份 / 回收身份 / 切換身份功能。

展示當(dāng)前用戶能切換到身份列表,及授予給其他人的身份列表

@RequestMapping
public String runasList(@CurrentUser User loginUser, Model model) {
    model.addAttribute("fromUserIds", 
        userRunAsService.findFromUserIds(loginUser.getId()));
    model.addAttribute("toUserIds", userRunAsService.findToUserIds(loginUser.getId()));
    List<User> allUsers = userService.findAll();
    allUsers.remove(loginUser);
    model.addAttribute("allUsers", allUsers);
    Subject subject = SecurityUtils.getSubject();
    model.addAttribute("isRunas", subject.isRunAs());
    if(subject.isRunAs()) {
        String previousUsername =
                (String)subject.getPreviousPrincipals().getPrimaryPrincipal();
        model.addAttribute("previousUsername", previousUsername);
    }
    return "runas";
}
  1. Subject.isRunAs():表示當(dāng)前用戶是否是 RunAs 用戶,即已經(jīng)切換身份了;
  2. Subject.getPreviousPrincipals():得到切換身份之前的身份,一個(gè)用戶可以切換很多次身份,之前的身份使用棧數(shù)據(jù)結(jié)構(gòu)來存儲;

授予身份

把當(dāng)前用戶身份授予給另一個(gè)用戶,這樣另一個(gè)用戶可以切換身份到該用戶。

@RequestMapping("/grant/{toUserId}")
public String grant(
        @CurrentUser User loginUser,
        @PathVariable("toUserId") Long toUserId,
        RedirectAttributes redirectAttributes) {
    if(loginUser.getId().equals(toUserId)) {
        redirectAttributes.addFlashAttribute("msg", "自己不能切換到自己的身份");
        return "redirect:/runas";
    }
    userRunAsService.grantRunAs(loginUser.getId(), toUserId);
    redirectAttributes.addFlashAttribute("msg", "操作成功");
    return "redirect:/runas";
}
  1. 自己不能授予身份給自己;
  2. 調(diào)用 UserRunAsService. grantRunAs 把當(dāng)前登錄用戶的身份授予給相應(yīng)的用戶;

回收身份

把授予給某個(gè)用戶的身份回收回來。

@RequestMapping("/revoke/{toUserId}")
public String revoke(
        @CurrentUser User loginUser,
        @PathVariable("toUserId") Long toUserId,
        RedirectAttributes redirectAttributes) {
    userRunAsService.revokeRunAs(loginUser.getId(), toUserId);
    redirectAttributes.addFlashAttribute("msg", "操作成功");
    return "redirect:/runas";
}

切換身份

@RequestMapping("/switchTo/{switchToUserId}")
public String switchTo(
        @CurrentUser User loginUser,
        @PathVariable("switchToUserId") Long switchToUserId,
        RedirectAttributes redirectAttributes) {
    Subject subject = SecurityUtils.getSubject();
    User switchToUser = userService.findOne(switchToUserId);
    if(loginUser.equals(switchToUser)) {
        redirectAttributes.addFlashAttribute("msg", "自己不能切換到自己的身份");
        return "redirect:/runas";
    }
    if(switchToUser == null || !userRunAsService.exists(switchToUserId, loginUser.getId())) {
        redirectAttributes.addFlashAttribute("msg", "對方?jīng)]有授予您身份,不能切換");
        return "redirect:/runas";
    }
    subject.runAs(new SimplePrincipalCollection(switchToUser.getUsername(), ""));
    redirectAttributes.addFlashAttribute("msg", "操作成功");
    redirectAttributes.addFlashAttribute("needRefresh", "true");
    return "redirect:/runas";
}
  1. 首先根據(jù) switchToUserId 查找到要切換到的身份;
  2. 然后通過 UserRunAsService. exists() 判斷當(dāng)前登錄用戶是否可以切換到該身份;
  3. 通過 Subject.runAs() 切換到該身份;

切換到上一個(gè)身份

@RequestMapping("/switchBack")
public String switchBack(RedirectAttributes redirectAttributes) {
    Subject subject = SecurityUtils.getSubject();
    if(subject.isRunAs()) {
       subject.releaseRunAs();
    }
    redirectAttributes.addFlashAttribute("msg", "操作成功");
    redirectAttributes.addFlashAttribute("needRefresh", "true");
    return "redirect:/runas";
}
  1. 通過 Subject.releaseRunAs() 切換會上一個(gè)身份;

此處注意的是我們可以切換多次身份,如 A 切換到 B,然后再切換到 C;那么需要調(diào)用兩次 Subject. releaseRunAs() 才能切換會 A;即內(nèi)部使用棧數(shù)據(jù)結(jié)構(gòu)存儲著切換過的用戶;Subject. getPreviousPrincipals() 得到上一次切換到的身份,比如當(dāng)前是 C;那么調(diào)用該 API 將得到 B 的身份。

其他代碼和配置和《第十六章 綜合實(shí)例》一樣,請參考該章。

測試

1、首先訪問 http://localhost:8080/chapter21/,輸入 admin/123456 進(jìn)行登錄;會看到如下界面:

2、點(diǎn)擊切換身份按鈕,跳到如下界面:

在該界面可以授權(quán)身份給其他人(點(diǎn)擊授權(quán)身份可以把自己的身份授權(quán)給其他人 / 點(diǎn)擊回收身份可以把之前授予的身份撤回)、或切換到其他身份(即假裝為其他身份運(yùn)行);

3、點(diǎn)擊切換到該身份按鈕,切換到相應(yīng)的身份運(yùn)行,如:

此時(shí) zhang 用戶切換到 admin 身份;如果點(diǎn)擊切換回該身份,會把當(dāng)前身份切換會 zhang。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號