spring boot 配置sa-token
前言
- sa-token一個國產(chǎn)的茂嗓,權(quán)限認證框架。
- 功能上類似Apache Shiro
科阎、
Spring Security等述吸。但是更加強大、容易上手 - 主要解決: 登錄認證锣笨、權(quán)限認證蝌矛、Session會話 等一系列權(quán)限相關(guān)問題。大部分api調(diào)用都是一行代碼就能解決
- sa-token功能很強大错英。以下簡單記錄登錄認證入撒、權(quán)限認證、路由攔截鑒權(quán)等功能
- 在
sa-token
中椭岩,登錄授權(quán)就是如此的簡單茅逮,不需要什么全局過濾器璃赡,不需要各種亂七八糟的配置!只需要一行簡單的API調(diào)用献雅,即可完成會話的登錄授權(quán)碉考! - 只要完成登錄認證、權(quán)限認證挺身、路由攔截鑒權(quán)就可以簡單的為項目增加一個鑒權(quán)系統(tǒng)侯谁。
- 如果只需要登錄認證,權(quán)限認證都不需要章钾,就更簡單了墙贱。。寫一個路由攔截配置文件贱傀、用戶登錄調(diào)用login方法就搞定一切了惨撇。
1.spring boot
引入sa-token
<!-- Sa-Token 權(quán)限認證, 在線文檔:http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.29.0</version>
</dependency>
2.配置
application.yml文件增加如下配置項目,當(dāng)然也可以不增加:
# Sa-Token配置 sa-token: # token名稱 (同時也是cookie名稱) token-name: satoken # token有效期窍箍,單位s 默認30天, -1代表永不過期 timeout: 2592000 # token臨時有效期 (指定時間內(nèi)無操作就視為token過期) 單位: 秒 activity-timeout: -1 # 是否允許同一賬號并發(fā)登錄 (為true時允許一起登錄, 為false時新登錄擠掉舊登錄) is-concurrent: true # 在多人登錄同一賬號時串纺,是否共用一個token (為true時所有登錄共用一個token, 為false時每次登錄新建一個token) is-share: false # token風(fēng)格 token-style: uuid # 是否輸出操作日志 is-log: false
3.登錄認證
所謂登錄認證,說白了就是限制某些API接口必須登錄后才能訪問椰棘。
如何判斷一個會話是否已經(jīng)登錄纺棺?用戶登錄后,服務(wù)器端調(diào)用
login方法
邪狞。sa-token會幫我們對會話進行做個標(biāo)記祷蝌。每次需要登錄認證的時候校驗這些標(biāo)記。有標(biāo)記者視為已登錄帆卓,無標(biāo)記者視為未登錄巨朦。默認,不特殊處理的情況下剑令,sa-token是通過cookie來標(biāo)記的糊啡。如果清掉了cookie,則需要重新登錄吁津。
// 標(biāo)記當(dāng)前會話登錄的賬號id // 建議的參數(shù)類型:long | int | String棚蓄, 不可以傳入復(fù)雜類型,如:User碍脏、Admin等等 StpUtil.login(Object id); // 當(dāng)前會話注銷登錄 StpUtil.logout(); // 獲取當(dāng)前會話是否已經(jīng)登錄梭依,返回true=已登錄,false=未登錄 StpUtil.isLogin(); // 檢驗當(dāng)前會話是否已經(jīng)登錄, 如果未登錄典尾,則拋出異常:`NotLoginException` StpUtil.checkLogin() // 獲取當(dāng)前會話賬號id, 如果未登錄役拴,則拋出異常:`NotLoginException` StpUtil.getLoginId(); // 類似查詢API還有: StpUtil.getLoginIdAsString(); // 獲取當(dāng)前會話賬號id, 并轉(zhuǎn)化為`String`類型 StpUtil.getLoginIdAsInt(); // 獲取當(dāng)前會話賬號id, 并轉(zhuǎn)化為`int`類型 StpUtil.getLoginIdAsLong(); // 獲取當(dāng)前會話賬號id, 并轉(zhuǎn)化為`long`類型 // ---------- 指定未登錄情形下返回的默認值 ---------- // 獲取當(dāng)前會話賬號id, 如果未登錄,則返回null StpUtil.getLoginIdDefaultNull(); // 獲取當(dāng)前會話賬號id, 如果未登錄钾埂,則返回默認值 (`defaultValue`可以為任意類型) StpUtil.getLoginId(T defaultValue); // 獲取指定token對應(yīng)的賬號id河闰,如果未登錄科平,則返回 null StpUtil.getLoginIdByToken(String tokenValue); // 獲取當(dāng)前`StpLogic`的token名稱 StpUtil.getTokenName(); // 獲取當(dāng)前會話的token值 StpUtil.getTokenValue(); // 獲取當(dāng)前會話的token信息參數(shù) StpUtil.getTokenInfo();
4.權(quán)限認證
所謂權(quán)限認證,認證的核心就是一個賬號是否擁有一個權(quán)限碼姜性。有匠抗,就讓你通過。沒有污抬?那么禁止訪問!
再往底了說,就是每個賬號都會擁有一個權(quán)限碼集合绳军,我來校驗這個集合中是否包含指定的權(quán)限碼
例如:當(dāng)前賬號擁有權(quán)限碼集合:
["user-add", "user-delete", "user-get"]
印机,這時候我來校驗權(quán)限"user-update"
,則其結(jié)果就是:驗證失敗门驾,禁止訪問所以核心問題如下:
4.1 獲取當(dāng)前賬號的權(quán)限碼集合
你需要做的就是新建一個類射赛,實現(xiàn)
StpInterface
接口添加
@Component
保證此類被SpringBoot掃描,完成Sa-Token的自定義權(quán)限驗證擴展注意方法傳入的loginID奶是。實際項目需要更加loginID實際返回該ID對于的權(quán)限楣责。
/** * 自定義權(quán)限驗證接口擴展 */ @Component // 保證此類被SpringBoot掃描,完成Sa-Token的自定義權(quán)限驗證擴展 public class StpInterfaceImpl implements StpInterface { /** * 返回一個賬號所擁有的權(quán)限碼集合 */ @Override public List<String> getPermissionList(Object loginId, String loginType) { // 本list僅做模擬聂沙,實際項目中要根據(jù)具體業(yè)務(wù)邏輯來查詢權(quán)限 List<String> list = new ArrayList<String>(); list.add("101"); list.add("user-add"); list.add("user-delete"); list.add("user-update"); list.add("user-get"); list.add("article-get"); return list; } /** * 返回一個賬號所擁有的角色標(biāo)識集合 (權(quán)限與角色可分開校驗) */ @Override public List<String> getRoleList(Object loginId, String loginType) { // 本list僅做模擬秆麸,實際項目中要根據(jù)具體業(yè)務(wù)邏輯來查詢角色 List<String> list = new ArrayList<String>(); list.add("admin"); list.add("super-admin"); return list; } }
4.2權(quán)限認證
告訴了sa-token用戶所擁有的權(quán)限或角色后。就可以用以下api來鑒權(quán)了
// 判斷:當(dāng)前賬號是否含有指定權(quán)限, 返回true或false StpUtil.hasPermission("user-update"); // 校驗:當(dāng)前賬號是否含有指定權(quán)限, 如果驗證未通過及汉,則拋出異常: NotPermissionException StpUtil.checkPermission("user-update"); // 校驗:當(dāng)前賬號是否含有指定權(quán)限 [指定多個沮趣,必須全部驗證通過] StpUtil.checkPermissionAnd("user-update", "user-delete"); // 校驗:當(dāng)前賬號是否含有指定權(quán)限 [指定多個,只要其一驗證通過即可] StpUtil.checkPermissionOr("user-update", "user-delete"); // 判斷:當(dāng)前賬號是否擁有指定角色, 返回true或false StpUtil.hasRole("super-admin"); // 校驗:當(dāng)前賬號是否含有指定角色標(biāo)識, 如果驗證未通過坷随,則拋出異常: NotRoleException StpUtil.checkRole("super-admin"); // 校驗:當(dāng)前賬號是否含有指定角色標(biāo)識 [指定多個房铭,必須全部驗證通過] StpUtil.checkRoleAnd("super-admin", "shop-admin"); // 校驗:當(dāng)前賬號是否含有指定角色標(biāo)識 [指定多個,只要其一驗證通過即可] StpUtil.checkRoleOr("super-admin", "shop-admin");
5.路由攔截鑒權(quán)
為什么需要路由攔截鑒權(quán)温眉?
方法1:每個方法手動調(diào)用如上的鑒權(quán)api缸匪。如果沒有權(quán)限就不執(zhí)行。类溢。這個辦法侵入性太高
方法2:注解鑒權(quán)凌蔬。。豌骏。這個辦法同樣不太方便龟梦。。窃躲。需要修改api接口的注解计贰。
方法3:自己動手書寫全局攔截器
方法4:sa-token提供的路由攔截鑒權(quán)。蒂窒。這個是比較方便的躁倒。荞怒。改動比較少。只需要配置相關(guān)配置類秧秉『肿溃基本上所有事情就搞定了。象迎。荧嵌。
增加如下配置類即可。如下代碼注冊了一個登錄認證攔截器砾淌,并且排除了
/user/doLogin
接口用來開放登錄(除了/user/doLogin
以外的所有接口都需要登錄才能訪問)@Configuration public class SaTokenConfigure implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // 注冊路由攔截器啦撮,自定義認證規(guī)則 registry.addInterceptor(new SaRouteInterceptor((req, res, handler)->{ // 根據(jù)路由劃分模塊,不同模塊不同鑒權(quán) SaRouter.match("/user/**", r -> StpUtil.checkPermission("user")); SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin")); SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods")); SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders")); SaRouter.match("/notice/**", r -> StpUtil.checkPermission("notice")); SaRouter.match("/comment/**", r -> StpUtil.checkPermission("comment")); })).addPathPatterns("/**") .excludePathPatterns("/user/doLogin"); } }
在校驗函數(shù)內(nèi)不只可以使用
StpUtil.checkPermission("xxx")
進行權(quán)限校驗汪厨,你還可以寫任意代碼最重要的是日記記錄赃春。。
// 登錄認證 -- 攔截所有路由劫乱,并排除/user/doLogin 用于開放登錄 SaRouter.match("/**", "/user/doLogin", r -> StpUtil.checkLogin()); // 角色認證 -- 攔截以 admin 開頭的路由织中,必須具備 admin 角色或者 super-admin 角色才可以通過認證 SaRouter.match("/admin/**", r -> StpUtil.checkRoleOr("admin", "super-admin")); // 甚至你可以隨意的寫一個打印語句 SaRouter.match("/**", r -> System.out.println("----啦啦啦----")); // 連綴寫法 SaRouter.match("/**").check(r -> System.out.println("----啦啦啦----"));
除了上述示例的 path 路由匹配,還可以根據(jù)很多其它特征進行匹配衷戈,以下是所有可匹配的特征:
// 基礎(chǔ)寫法樣例:匹配一個path狭吼,執(zhí)行一個校驗函數(shù) SaRouter.match("/user/**").check(r -> StpUtil.checkLogin()); // 根據(jù) path 路由匹配 ——— 支持寫多個path,支持寫 restful 風(fēng)格路由 SaRouter.match("/user/**", "/goods/**", "/art/get/{id}").check( /* 要執(zhí)行的校驗函數(shù) */ ); // 根據(jù) path 路由排除匹配 SaRouter.match("/**").notMatch("*.html", "*.css", "*.js").check( /* 要執(zhí)行的校驗函數(shù) */ ); // 根據(jù)請求類型匹配 SaRouter.match(SaHttpMethod.GET).check( /* 要執(zhí)行的校驗函數(shù) */ ); // 根據(jù)一個 boolean 條件進行匹配 SaRouter.match( StpUtil.isLogin() ).check( /* 要執(zhí)行的校驗函數(shù) */ ); // 根據(jù)一個返回 boolean 結(jié)果的lambda表達式匹配 SaRouter.match( r -> StpUtil.isLogin() ).check( /* 要執(zhí)行的校驗函數(shù) */ ); // 多個條件一起使用 SaRouter.match(SaHttpMethod.GET).match("/**").check( /* 要執(zhí)行的校驗函數(shù) */ ); // 可以無限連綴下去 SaRouter .match(SaHttpMethod.GET) .match("/admin/**") .match("/user/**") .notMatch("/**/*.js") .notMatch("/**/*.css") // .... .check( /* 只有上述所有條件都匹配成功脱惰,才會執(zhí)行最后的check校驗函數(shù) */ );