Sa-Token 是一個(gè)輕量級(jí) java 權(quán)限認(rèn)證框架,主要解決登錄認(rèn)證、權(quán)限認(rèn)證、單點(diǎn)登錄、OAuth2、微服務(wù)網(wǎng)關(guān)鑒權(quán) 等一系列權(quán)限相關(guān)問題。
Gitee 開源地址:https://gitee.com/dromara/sa-token
本篇將介紹 Sa-Token 中的多賬號(hào)認(rèn)證操作。
一、需求分析
有的時(shí)候,我們會(huì)在一個(gè)項(xiàng)目中設(shè)計(jì)兩套賬號(hào)體系,比如一個(gè)電商系統(tǒng)的 user表 和 admin表, 在這種場(chǎng)景下,如果兩套賬號(hào)我們都使用 StpUtil 類的API進(jìn)行登錄鑒權(quán),那么勢(shì)必會(huì)發(fā)生邏輯沖突。
在Sa-Token中,這個(gè)問題的模型叫做:多賬號(hào)體系認(rèn)證。
要解決這個(gè)問題,我們必須有一個(gè)合理的機(jī)制將這兩套賬號(hào)的授權(quán)給區(qū)分開,讓它們互不干擾才行。
二、演進(jìn)思路
假如說我們的 user表 和 admin表 都有一個(gè) id=10001 的賬號(hào),它們對(duì)應(yīng)的登錄代碼:StpUtil.login(10001)
是一樣的,
那么問題來了:在StpUtil.getLoginId()
獲取到的賬號(hào)id如何區(qū)分它是User用戶,還是Admin用戶?
你可能會(huì)想到為他們加一個(gè)固定前綴,比如StpUtil.login("User_" + 10001)
、StpUtil.login("Admin_" + 10001)
,這樣確實(shí)是可以解決問題的,
但是同樣的:你需要在StpUtil.getLoginId()
時(shí)再裁剪掉相應(yīng)的前綴才能獲取真正的賬號(hào)id,這樣一增一減就讓我們的代碼變得無比啰嗦。
那么,有沒有從框架層面支持的,更優(yōu)雅的解決方案呢?
SaManager
提供了動(dòng)態(tài)創(chuàng)建 StpLogic 的能力:
// 在當(dāng)前會(huì)話登錄 Admin 賬號(hào) 10001
SaManager.getStpLogic("admin").login(10001);
// 在當(dāng)前會(huì)話登錄 User 賬號(hào) 10001
SaManager.getStpLogic("user").login(10001);
這是一種解決方案,但仍然需要我們每次調(diào)用方法時(shí)指定賬號(hào)類型參數(shù),代碼略顯啰嗦,接下來我們介紹方案二:自定義 StpUtil 鑒權(quán)工具類。
三、自定義 StpUtil 鑒權(quán)工具類
前面幾篇介紹的api調(diào)用,都是經(jīng)過 StpUtil 類的各種靜態(tài)方法進(jìn)行授權(quán)認(rèn)證,
而如果我們深入它的源碼,點(diǎn)此閱覽
就會(huì)發(fā)現(xiàn),此類并沒有任何代碼邏輯,唯一做的事就是對(duì)成員變量stpLogic
的各個(gè)API包裝一下進(jìn)行轉(zhuǎn)發(fā)。
這樣做有兩個(gè)優(yōu)點(diǎn):
- StpLogic 類的所有函數(shù)都可以被重寫,按需擴(kuò)展。
- 在構(gòu)造方法時(shí)隨意傳入一個(gè)不同的
loginType
,就可以再造一套賬號(hào)登錄體系。
四、操作示例
比如說,對(duì)于原生StpUtil
類,我們只做admin賬號(hào)
權(quán)限認(rèn)證,而對(duì)于user賬號(hào)
,我們則:
- 新建一個(gè)新的權(quán)限認(rèn)證類,比如:
StpUserUtil.java
。 - 將
StpUtil.java
類的全部代碼復(fù)制粘貼到StpUserUtil.java
里。 - 更改一下其
LoginType
, 比如:
public class StpUserUtil {
/**
* 賬號(hào)體系標(biāo)識(shí)
*/
public static final String TYPE = "user"; // 將 LoginType 從`login`改為`user`
// 其它代碼 ...
}
- 接下來就可以像調(diào)用
StpUtil.java
一樣調(diào)用StpUserUtil.java
了,這兩套賬號(hào)認(rèn)證的邏輯是完全隔離的。
成品樣例參考:碼云 StpUserUtil.java
五、在多賬戶模式下使用注解鑒權(quán)
框架默認(rèn)的注解鑒權(quán) 如@SaCheckLogin
只針對(duì)原生StpUtil
進(jìn)行鑒權(quán)。
例如,我們?cè)谝粋€(gè)方法上加上@SaCheckLogin
注解,這個(gè)注解只會(huì)放行通過StpUtil.login(id)
進(jìn)行登錄的會(huì)話,
而對(duì)于通過StpUserUtil.login(id)
進(jìn)行登錄的會(huì)話,則始終不會(huì)通過校驗(yàn)。
那么如何告訴@SaCheckLogin
要鑒別的是哪套賬號(hào)的登錄會(huì)話呢?很簡(jiǎn)單,你只需要指定一下注解的type屬性即可:
// 通過type屬性指定此注解校驗(yàn)的是我們自定義的`StpUserUtil`,而不是原生`StpUtil`
@SaCheckLogin(type = StpUserUtil.TYPE)
@RequestMapping("info")
public String info() {
return "查詢用戶信息";
}
注:@SaCheckRole("xxx")
、@SaCheckPermission("xxx")
同理,亦可根據(jù)type屬性指定其校驗(yàn)的賬號(hào)體系,此屬性默認(rèn)為""
,代表使用原生StpUtil
賬號(hào)體系。
六、使用注解合并簡(jiǎn)化代碼
交流群里有同學(xué)反應(yīng),雖然可以根據(jù) @SaCheckLogin(type = "user")
指定賬號(hào)類型,但幾十上百個(gè)注解都加上這個(gè)的話,還是有些繁瑣,代碼也不夠優(yōu)雅,有么有更簡(jiǎn)單的解決方案?
我們期待一種[注解繼承/合并]
的能力,即:自定義一個(gè)注解,標(biāo)注上@SaCheckLogin(type = "user")
,
然后在方法上標(biāo)注這個(gè)自定義注解,效果等同于標(biāo)注@SaCheckLogin(type = "user")
。
很遺憾,JDK默認(rèn)的注解處理器并沒有提供這種[注解繼承/合并]
的能力,不過好在我們可以利用 Spring 的注解處理器,達(dá)到同樣的目的。
1、重寫Sa-Token默認(rèn)的注解處理器:
@Configuration
public class SaTokenConfigure {
@Autowired
public void rewriteSaStrategy() {
// 重寫Sa-Token的注解處理器,增加注解合并功能
SaStrategy.me.getAnnotation = (element, annotationClass) -> {
return AnnotatedElementUtils.getMergedAnnotation(element, annotationClass);
};
}
}
2、自定義一個(gè)注解:
/**
* 登錄認(rèn)證(User版):只有登錄之后才能進(jìn)入該方法
* <p> 可標(biāo)注在函數(shù)、類上(效果等同于標(biāo)注在此類的所有方法上)
*/
@SaCheckLogin(type = "user")
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE})
public @interface SaUserCheckLogin {
}
3、接下來就可以使用我們的自定義注解了:
// 使用 @SaUserCheckLogin 的效果等同于使用:@SaCheckLogin(type = "user")
@SaUserCheckLogin
@RequestMapping("info")
public String info() {
return "查詢用戶信息";
}
注:其它注解 @SaCheckRole("xxx")
、@SaCheckPermission("xxx")
同理,
完整示例參考:碼云:自定義注解。
七、同端多登陸
假設(shè)我們不僅需要在后臺(tái)同時(shí)集成兩套賬號(hào),我們還需要在一個(gè)客戶端同時(shí)登陸兩套賬號(hào)(業(yè)務(wù)場(chǎng)景舉例:一個(gè)APP中可以同時(shí)登陸商家賬號(hào)和用戶賬號(hào))。
如果我們不做任何特殊處理的話,在客戶端會(huì)發(fā)生token覆蓋
,新登錄的token會(huì)覆蓋掉舊登錄的token從而導(dǎo)致舊登錄失效。
那么如何解決這個(gè)問題?
很簡(jiǎn)單,我們只要更改一下 StpUserUtil
的 TokenName
即可,參考示例如下:
public class StpUserUtil {
// 使用匿名子類 重寫`stpLogic對(duì)象`的一些方法
public static StpLogic stpLogic = new StpLogic("user") {
// 重寫 StpLogic 類下的 `splicingKeyTokenName` 函數(shù),返回一個(gè)與 `StpUtil` 不同的token名稱, 防止沖突
@Override
public String splicingKeyTokenName() {
return super.splicingKeyTokenName() + "-user";
}
// 同理你可以按需重寫一些其它方法 ...
};
// ...
}
再次調(diào)用 StpUserUtil.login(10001)
進(jìn)行登錄授權(quán)時(shí),token的名稱將不再是 satoken
,而是我們重寫后的 satoken-user
。
八、不同體系不同 SaTokenConfig 配置
如果自定義的 StpUserUtil 需要使用不同 SaTokenConfig 對(duì)象, 也很簡(jiǎn)單,參考示例如下:
public class StpUserUtil {
// 使用匿名子類 重寫`stpLogic對(duì)象`的一些方法
public static StpLogic stpLogic = new StpLogic("user") {
// 首先自定義一個(gè) Config 對(duì)象
SaTokenConfig config = new SaTokenConfig()
.setTokenName("satoken")
.setTimeout(2592000)
// ... 其它set
;
// 然后重寫 stpLogic 配置獲取方法
@Override
public SaTokenConfig getConfig() {
return config;
}
};
// ...
}
九、多賬號(hào)體系混合鑒權(quán)
QQ群中有小伙伴提問:在多賬號(hào)體系下,怎么在 SaInterceptor
攔截器中給一個(gè)接口登錄鑒權(quán)?文章來源:http://www.zghlxwxcb.cn/news/detail-600118.html
其實(shí)這個(gè)問題,主要是靠你的業(yè)務(wù)需求來決定,以后臺(tái) Admin 賬號(hào)和前臺(tái) User 賬號(hào)為例:文章來源地址http://www.zghlxwxcb.cn/news/detail-600118.html
// 注冊(cè) Sa-Token 攔截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SaInterceptor(handle -> {
// 如果這個(gè)接口,要求客戶端登錄了后臺(tái) Admin 賬號(hào)才能訪問:
SaRouter.match("/art/getInfo").check(r -> StpUtil.checkLogin());
// 如果這個(gè)接口,要求客戶端登錄了前臺(tái) User 賬號(hào)才能訪問:
SaRouter.match("/art/getInfo").check(r -> StpUserUtil.checkLogin());
// 如果這個(gè)接口,要求客戶端同時(shí)登錄 Admin 和 User 賬號(hào),才能訪問:
SaRouter.match("/art/getInfo").check(r -> {
StpUtil.checkLogin();
StpUserUtil.checkLogin();
});
// 如果這個(gè)接口,要求客戶端登錄 Admin 和 User 賬號(hào)任意一個(gè),就能訪問:
SaRouter.match("/art/getInfo").check(r -> {
if(StpUtil.isLogin() == false && StpUserUtil.isLogin() == false) {
throw new SaTokenException("請(qǐng)登錄后再訪問接口");
}
});
})).addPathPatterns("/**");
}
參考資料
- Sa-Token 文檔:https://sa-token.cc
- Gitee 倉庫地址:https://gitee.com/dromara/sa-token
- GitHub 倉庫地址:https://github.com/dromara/sa-token
到了這里,關(guān)于Sa-Token 多賬號(hào)認(rèn)證:同時(shí)為系統(tǒng)的 Admin 賬號(hào)和 User 賬號(hào)提供鑒權(quán)操作的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!