<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          再見(jiàn),Shiro!這個(gè)開(kāi)源的權(quán)限認(rèn)證框架功能真強(qiáng)大!很香

          共 16930字,需瀏覽 34分鐘

           ·

          2021-09-18 12:34

          在我們做SpringBoot項(xiàng)目的時(shí)候,認(rèn)證授權(quán)是必不可少的功能!我們經(jīng)常會(huì)選擇Shiro、Spring Security這類(lèi)權(quán)限認(rèn)證框架來(lái)實(shí)現(xiàn),但這些框架使用起來(lái)有點(diǎn)繁瑣,而且功能也不夠強(qiáng)大。最近發(fā)現(xiàn)一款功能強(qiáng)大的權(quán)限認(rèn)證框架Sa-Token,它使用簡(jiǎn)單、API設(shè)計(jì)優(yōu)雅,推薦給大家!

          Sa-Token簡(jiǎn)介

          Sa-Token是一款輕量級(jí)的Java權(quán)限認(rèn)證框架,可以用來(lái)解決登錄認(rèn)證、權(quán)限認(rèn)證、Session會(huì)話、單點(diǎn)登錄、OAuth2.0、微服務(wù)網(wǎng)關(guān)鑒權(quán)等一系列權(quán)限相關(guān)問(wèn)題。

          框架集成簡(jiǎn)單、開(kāi)箱即用、API設(shè)計(jì)優(yōu)雅,通過(guò)Sa-Token,你將以一種極其簡(jiǎn)單的方式實(shí)現(xiàn)系統(tǒng)的權(quán)限認(rèn)證部分,有時(shí)候往往只需一行代碼就能實(shí)現(xiàn)功能。

          Sa-Token功能很全,具體可以參考下圖。

          使用

          在SpringBoot中使用Sa-Token是非常簡(jiǎn)單的,接下來(lái)我們使用它來(lái)實(shí)現(xiàn)最常用的認(rèn)證授權(quán)功能,包括登錄認(rèn)證、角色認(rèn)證和權(quán)限認(rèn)證。

          集成及配置

          Sa-Token的集成和配置都非常簡(jiǎn)單,不愧為開(kāi)箱即用。

          • 首先我們需要在項(xiàng)目的pom.xml中添加Sa-Token的相關(guān)依賴;
          <!-- Sa-Token 權(quán)限認(rèn)證 -->
          <dependency>
              <groupId>cn.dev33</groupId>
              <artifactId>sa-token-spring-boot-starter</artifactId>
              <version>1.24.0</version>
          </dependency>
          • 然后在application.yml中添加Sa-Token的相關(guān)配置,考慮到要支持前后端分離項(xiàng)目,我們關(guān)閉從cookie中讀取token,改為從head中讀取token。
          # Sa-Token配置
          sa-token:
            # token名稱 (同時(shí)也是cookie名稱)
            token-name: Authorization
            # token有效期,單位秒,-1代表永不過(guò)期
            timeout: 2592000
            # token臨時(shí)有效期 (指定時(shí)間內(nèi)無(wú)操作就視為token過(guò)期),單位秒
            activity-timeout: -1
            # 是否允許同一賬號(hào)并發(fā)登錄 (為false時(shí)新登錄擠掉舊登錄)
            is-concurrent: true
            # 在多人登錄同一賬號(hào)時(shí),是否共用一個(gè)token (為false時(shí)每次登錄新建一個(gè)token)
            is-share: false
            # token風(fēng)格
            token-style: uuid
            # 是否輸出操作日志
            is-log: false
            # 是否從cookie中讀取token
            is-read-cookie: false
            # 是否從head中讀取token
            is-read-head: true

          登錄認(rèn)證

          在管理系統(tǒng)中,除了登錄接口,基本都需要登錄認(rèn)證,在Sa-Token中使用路由攔截鑒權(quán)是最方便的,下面我們來(lái)實(shí)現(xiàn)下。

          • 實(shí)現(xiàn)登錄認(rèn)證非常簡(jiǎn)單,首先在UmsAdminController中添加一個(gè)登錄接口;
          /**
           * 后臺(tái)用戶管理
           * Created by macro on 2018/4/26.
           */

          @Controller
          @Api(tags = "UmsAdminController", description = "后臺(tái)用戶管理")
          @RequestMapping("/admin")
          public class UmsAdminController {
              @Autowired
              private UmsAdminService adminService;

              @ApiOperation(value = "登錄以后返回token")
              @RequestMapping(value = "/login", method = RequestMethod.POST)
              @ResponseBody
              public CommonResult login(@RequestParam String username, @RequestParam String password) {
                  SaTokenInfo saTokenInfo = adminService.login(username, password);
                  if (saTokenInfo == null) {
                      return CommonResult.validateFailed("用戶名或密碼錯(cuò)誤");
                  }
                  Map<String, String> tokenMap = new HashMap<>();
                  tokenMap.put("token", saTokenInfo.getTokenValue());
                  tokenMap.put("tokenHead", saTokenInfo.getTokenName());
                  return CommonResult.success(tokenMap);
              }
          }
          • 然后在UmsAdminServiceImpl添加登錄的具體邏輯,先驗(yàn)證密碼,然后調(diào)用StpUtil.login(adminUser.getId())即可實(shí)現(xiàn)登錄,調(diào)用API一行搞定;
          /**
           * Created by macro on 2020/10/15.
           */

          @Slf4j
          @Service
          public class UmsAdminServiceImpl implements UmsAdminService {

              @Override
              public SaTokenInfo login(String username, String password) {
                  SaTokenInfo saTokenInfo = null;
                  AdminUser adminUser = getAdminByUsername(username);
                  if (adminUser == null) {
                      return null;
                  }
                  if (!SaSecureUtil.md5(password).equals(adminUser.getPassword())) {
                      return null;
                  }
                  // 密碼校驗(yàn)成功后登錄,一行代碼實(shí)現(xiàn)登錄
                  StpUtil.login(adminUser.getId());
                  // 獲取當(dāng)前登錄用戶Token信息
                  saTokenInfo = StpUtil.getTokenInfo();
                  return saTokenInfo;
              }
          }
          • 我們?cè)偬砑右粋€(gè)測(cè)試接口用于查詢當(dāng)前登錄狀態(tài),返回true表示已經(jīng)登錄;
          /**
           * Created by macro on 2020/10/15.
           */

          @Slf4j
          @Service
          public class UmsAdminServiceImpl implements UmsAdminService {
              @ApiOperation(value = "查詢當(dāng)前登錄狀態(tài)")
              @RequestMapping(value = "/isLogin", method = RequestMethod.GET)
              @ResponseBody
              public CommonResult isLogin() {
                  return CommonResult.success(StpUtil.isLogin());
              }
          }
          • 之后可以通過(guò)Swagger訪問(wèn)登錄接口來(lái)獲取Token了,使用賬號(hào)為admin:123456,訪問(wèn)地址:http://localhost:8088/swagger-ui/
          • 然后在Authorization請(qǐng)求頭中添加獲取到的token;
          • 訪問(wèn)/admin/isLogin接口,data屬性就會(huì)返回true了,表示你已經(jīng)是登錄狀態(tài)了;
          • 接下來(lái)我們需要把除登錄接口以外的接口都添加登錄認(rèn)證,添加Sa-Token的Java配置類(lèi)SaTokenConfig,注冊(cè)一個(gè)路由攔截器SaRouteInterceptor,這里我們的IgnoreUrlsConfig配置會(huì)從配置文件中讀取白名單配置;
          /**
           * Sa-Token相關(guān)配置
           */

          @Configuration
          public class SaTokenConfig implements WebMvcConfigurer {

              @Autowired
              private IgnoreUrlsConfig ignoreUrlsConfig;

              /**
               * 注冊(cè)sa-token攔截器
               */

              @Override
              public void addInterceptors(InterceptorRegistry registry) {
                  registry.addInterceptor(new SaRouteInterceptor((req, resp, handler) -> {
                      // 獲取配置文件中的白名單路徑
                      List<String> ignoreUrls = ignoreUrlsConfig.getUrls();
                      // 登錄認(rèn)證:除白名單路徑外均需要登錄認(rèn)證
                      SaRouter.match(Collections.singletonList("/**"), ignoreUrls, StpUtil::checkLogin);
                  })).addPathPatterns("/**");
              }
          }
          • application.yml文件中的白名單配置如下,注意開(kāi)放Swagger的訪問(wèn)路徑和靜態(tài)資源路徑;
          # 訪問(wèn)白名單路徑
          secure:
            ignored:
              urls:
                - /
                - /swagger-ui/
                - /*.html
                - /favicon.ico
                - /**/*.html
                - /**/*.css
                - /**/*.js
                - /swagger-resources/**
                - /v2/api-docs/**
                - /actuator/**
                - /admin/login
                - /admin/isLogin
          • 由于未登錄狀態(tài)下訪問(wèn)接口,Sa-Token會(huì)拋出NotLoginException異常,所以我們需要全局處理下;
          /**
           * 全局異常處理
           * Created by macro on 2020/2/27.
           */

          @ControllerAdvice
          public class GlobalExceptionHandler {

              /**
               * 處理未登錄的異常
               */

              @ResponseBody
              @ExceptionHandler(value = NotLoginException.class)
              public CommonResult handleNotLoginException(NotLoginException e
          {
                  return CommonResult.unauthorized(e.getMessage());
              }
          }
          • 之后當(dāng)我們?cè)诘卿洜顟B(tài)下訪問(wèn)接口時(shí),可以獲取到數(shù)據(jù);
          • 當(dāng)我們未登錄狀態(tài)(不帶token)時(shí)無(wú)法正常訪問(wèn)接口,返回code401

          角色認(rèn)證

          角色認(rèn)證也就是我們定義好一套規(guī)則,比如ROLE-ADMIN角色可以訪問(wèn)/brand下的所有資源,而ROLE_USER角色只能訪問(wèn)/brand/listAll,接下來(lái)我們來(lái)實(shí)現(xiàn)下角色認(rèn)證。

          • 首先我們需要擴(kuò)展Sa-Token的StpInterface接口,通過(guò)實(shí)現(xiàn)方法來(lái)返回用戶的角色碼和權(quán)限碼;
          /**
           * 自定義權(quán)限驗(yàn)證接口擴(kuò)展
           */

          @Component
          public class StpInterfaceImpl implements StpInterface {
              @Autowired
              private UmsAdminService adminService;
              @Override
              public List<String> getPermissionList(Object loginId, String loginType) {
                  AdminUser adminUser = adminService.getAdminById(Convert.toLong(loginId));
                  return adminUser.getRole().getPermissionList();
              }

              @Override
              public List<String> getRoleList(Object loginId, String loginType) {
                  AdminUser adminUser = adminService.getAdminById(Convert.toLong(loginId));
                  return Collections.singletonList(adminUser.getRole().getName());
              }
          }
          • 然后在Sa-Token的攔截器中配置路由規(guī)則,ROLE_ADMIN角色可以訪問(wèn)所有路徑,而ROLE_USER只能訪問(wèn)/brand/listAll路徑;
          /**
           * Sa-Token相關(guān)配置
           */

          @Configuration
          public class SaTokenConfig implements WebMvcConfigurer {

              @Autowired
              private IgnoreUrlsConfig ignoreUrlsConfig;

              /**
               * 注冊(cè)sa-token攔截器
               */

              @Override
              public void addInterceptors(InterceptorRegistry registry) {
                  registry.addInterceptor(new SaRouteInterceptor((req, resp, handler) -> {
                      // 獲取配置文件中的白名單路徑
                      List<String> ignoreUrls = ignoreUrlsConfig.getUrls();
                      // 登錄認(rèn)證:除白名單路徑外均需要登錄認(rèn)證
                      SaRouter.match(Collections.singletonList("/**"), ignoreUrls, StpUtil::checkLogin);
                      // 角色認(rèn)證:ROLE_ADMIN可以訪問(wèn)所有接口,ROLE_USER只能訪問(wèn)查詢?nèi)拷涌?/span>
                      SaRouter.match("/brand/listAll", () -> {
                          StpUtil.checkRoleOr("ROLE_ADMIN","ROLE_USER");
                          //強(qiáng)制退出匹配鏈
                          SaRouter.stop();
                      });
                      SaRouter.match("/brand/**", () -> StpUtil.checkRole("ROLE_ADMIN"));
                  })).addPathPatterns("/**");
              }
          }
          • 當(dāng)用戶不是被允許的角色訪問(wèn)時(shí),Sa-Token會(huì)拋出NotRoleException異常,我們可以全局處理下;
          /**
           * 全局異常處理
           * Created by macro on 2020/2/27.
           */

          @ControllerAdvice
          public class GlobalExceptionHandler {

              /**
               * 處理沒(méi)有角色的異常
               */

              @ResponseBody
              @ExceptionHandler(value = NotRoleException.class)
              public CommonResult handleNotRoleException(NotRoleException e
          {
                  return CommonResult.forbidden(e.getMessage());
              }
          }
          • 我們現(xiàn)在有兩個(gè)用戶,admin用戶具有ROLE_ADMIN角色,macro用戶具有ROLE_USER角色;
          • 使用admin賬號(hào)訪問(wèn)/brand/list接口可以正常訪問(wèn);
          • 使用macro賬號(hào)訪問(wèn)/brand/list接口無(wú)法正常訪問(wèn),返回code403。

          權(quán)限認(rèn)證

          當(dāng)我們給角色分配好權(quán)限,然后給用戶分配好角色后,用戶就擁有了這些權(quán)限。我們可以為每個(gè)接口分配不同的權(quán)限,擁有該權(quán)限的用戶就可以訪問(wèn)該接口。這就是權(quán)限認(rèn)證,接下來(lái)我們來(lái)實(shí)現(xiàn)下它。

          • 我們可以在Sa-Token的攔截器中配置路由規(guī)則,admin用戶可以訪問(wèn)所有路徑,而macro用戶只有讀取的權(quán)限,沒(méi)有寫(xiě)、改、刪的權(quán)限;
          /**
           * Sa-Token相關(guān)配置
           */

          @Configuration
          public class SaTokenConfig implements WebMvcConfigurer {

              @Autowired
              private IgnoreUrlsConfig ignoreUrlsConfig;

              /**
               * 注冊(cè)sa-token攔截器
               */

              @Override
              public void addInterceptors(InterceptorRegistry registry) {
                  registry.addInterceptor(new SaRouteInterceptor((req, resp, handler) -> {
                      // 獲取配置文件中的白名單路徑
                      List<String> ignoreUrls = ignoreUrlsConfig.getUrls();
                      // 登錄認(rèn)證:除白名單路徑外均需要登錄認(rèn)證
                      SaRouter.match(Collections.singletonList("/**"), ignoreUrls, StpUtil::checkLogin);
                      // 權(quán)限認(rèn)證:不同接口, 校驗(yàn)不同權(quán)限
                      SaRouter.match("/brand/listAll", () -> StpUtil.checkPermission("brand:read"));
                      SaRouter.match("/brand/create", () -> StpUtil.checkPermission("brand:create"));
                      SaRouter.match("/brand/update/{id}", () -> StpUtil.checkPermission("brand:update"));
                      SaRouter.match("/brand/delete/{id}", () -> StpUtil.checkPermission("brand:delete"));
                      SaRouter.match("/brand/list", () -> StpUtil.checkPermission("brand:read"));
                      SaRouter.match("/brand/{id}", () -> StpUtil.checkPermission("brand:read"));
                  })).addPathPatterns("/**");
              }
          }
          • 當(dāng)用戶無(wú)權(quán)限訪問(wèn)時(shí),Sa-Token會(huì)拋出NotPermissionException異常,我們可以全局處理下;
          /**
           * 全局異常處理
           * Created by macro on 2020/2/27.
           */

          @ControllerAdvice
          public class GlobalExceptionHandler {

              /**
               * 處理沒(méi)有權(quán)限的異常
               */

              @ResponseBody
              @ExceptionHandler(value = NotPermissionException.class)
              public CommonResult handleNotPermissionException(NotPermissionException e
          {
                  return CommonResult.forbidden(e.getMessage());
              }
          }
          • 使用admin賬號(hào)訪問(wèn)/brand/delete接口可以正常訪問(wèn);
          • 使用macro賬號(hào)訪問(wèn)/brand/delete無(wú)法正常訪問(wèn),返回code403。

          總結(jié)

          通過(guò)對(duì)Sa-Token的一波實(shí)踐,我們可以發(fā)現(xiàn)它的API設(shè)計(jì)非常優(yōu)雅,比起Shiro和Spring Security來(lái)說(shuō)確實(shí)順手多了。Sa-Token不僅提供了一系列強(qiáng)大的權(quán)限相關(guān)功能,還提供了很多標(biāo)準(zhǔn)的解決方案,比如Oauth2、分布式Session會(huì)話等,大家感興趣的話可以研究下。

          參考資料

          Sa-Token的官方文檔很全,也很良心,不僅提供了解決方式,還提供了解決思路,強(qiáng)烈建議大家去看下。

          官方文檔:http://sa-token.dev33.cn/

          項(xiàng)目源碼地址

          https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-sa-token

          < END >

          也許你還想看
            | 我在 B 站淘了 2 個(gè) Java 實(shí)戰(zhàn)項(xiàng)目! 小破站,YYDS!
            | 我常用的20+個(gè)學(xué)習(xí)編程的網(wǎng)站!蕪湖起飛!
            | 1w+字的 Dubbo 面試題/知識(shí)點(diǎn)總結(jié)?。?021 最新版)
            | 7年前,24歲,出版了一本 Redis 神書(shū)
            | 京東二面:為什么需要分布式ID?你項(xiàng)目中是怎么做的?
            | 和 Github 在一起 5 年多了......
            | 一鍵生成數(shù)據(jù)庫(kù)文檔,堪稱數(shù)據(jù)庫(kù)界的Swagger
            | 來(lái)看看這個(gè)超好用的項(xiàng)目腳手架吧!5分鐘搭建一個(gè)Spring Boot 前后端分離系統(tǒng)!
            | Spring 官宣,干掉 Spring 5.3.x!

          我是 Guide哥,一個(gè)工作2年有余,接觸編程已經(jīng)6年有余的程序員。大三開(kāi)源 JavaGuide,目前已經(jīng) 100k+ Star。未來(lái)幾年,希望持續(xù)完善 JavaGuide,爭(zhēng)取能夠幫助更多學(xué)習(xí) Java 的小伙伴!共勉!凎!點(diǎn)擊即可了解我的個(gè)人經(jīng)歷

          歡迎點(diǎn)贊分享。咱們下期再會(huì)!

          瀏覽 130
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  AV大奶网 | 亚洲精品乱码久久久久久久久久久久 | AⅤ视频在线观看 | 色色一区二区 | 欧美三级视频在线播放 |