<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>

          再見Spring Security!推薦一款功能強大的權(quán)限認證框架,用起來夠優(yōu)雅!

          共 16373字,需瀏覽 33分鐘

           ·

          2021-08-24 17:51

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

           

          Sa-Token簡介

          Sa-Token是一款輕量級的Java權(quán)限認證框架,可以用來解決登錄認證、權(quán)限認證、Session會話、單點登錄、OAuth2.0、微服務網(wǎng)關鑒權(quán)等一系列權(quán)限相關問題。
          框架集成簡單、開箱即用、API設計優(yōu)雅,通過Sa-Token,你將以一種極其簡單的方式實現(xiàn)系統(tǒng)的權(quán)限認證部分,有時候往往只需一行代碼就能實現(xiàn)功能。
          Sa-Token功能很全,具體可以參考下圖。

           

          使用

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

          集成及配置

          Sa-Token的集成和配置都非常簡單,不愧為開箱即用。
          • 首先我們需要在項目的pom.xml中添加Sa-Token的相關依賴;

          <!-- Sa-Token 權(quán)限認證 -->
          <dependency>
              <groupId>cn.dev33</groupId>
              <artifactId>sa-token-spring-boot-starter</artifactId>
              <version>1.24.0</version>
          </dependency>

          • 然后在application.yml中添加Sa-Token的相關配置,考慮到要支持前后端分離項目,我們關閉從cookie中讀取token,改為從head中讀取token。

          # Sa-Token配置
          sa-token:
            # token名稱 (同時也是cookie名稱)
            token-name: Authorization
            # token有效期,單位秒,-1代表永不過期
            timeout: 2592000
            # token臨時有效期 (指定時間內(nèi)無操作就視為token過期),單位秒
            activity-timeout: -1
            # 是否允許同一賬號并發(fā)登錄 (為false時新登錄擠掉舊登錄)
            is-concurrent: true
            # 在多人登錄同一賬號時,是否共用一個token (為false時每次登錄新建一個token)
            is-share: false
            # token風格
            token-style: uuid
            # 是否輸出操作日志
            is-log: false
            # 是否從cookie中讀取token
            is-read-cookie: false
            # 是否從head中讀取token
            is-read-head: true

          登錄認證

          在管理系統(tǒng)中,除了登錄接口,基本都需要登錄認證,在Sa-Token中使用路由攔截鑒權(quán)是最方便的,下面我們來實現(xiàn)下。
          • 實現(xiàn)登錄認證非常簡單,首先在UmsAdminController中添加一個登錄接口;
          /**
           * 后臺用戶管理
           * Created by macro on 2018/4/26.
           */

          @Controller
          @Api(tags = "UmsAdminController", description = "后臺用戶管理")
          @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("用戶名或密碼錯誤");
                  }
                  Map<String, String> tokenMap = new HashMap<>();
                  tokenMap.put("token", saTokenInfo.getTokenValue());
                  tokenMap.put("tokenHead", saTokenInfo.getTokenName());
                  return CommonResult.success(tokenMap);
              }
          }
          • 然后在UmsAdminServiceImpl添加登錄的具體邏輯,先驗證密碼,然后調(diào)用StpUtil.login(adminUser.getId())即可實現(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;
                  }
                  // 密碼校驗成功后登錄,一行代碼實現(xiàn)登錄
                  StpUtil.login(adminUser.getId());
                  // 獲取當前登錄用戶Token信息
                  saTokenInfo = StpUtil.getTokenInfo();
                  return saTokenInfo;
              }
          }
          • 我們再添加一個測試接口用于查詢當前登錄狀態(tài),返回true表示已經(jīng)登錄;
          /**
           * Created by macro on 2020/10/15.
           */

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

          @Configuration
          public class SaTokenConfig implements WebMvcConfigurer {

              @Autowired
              private IgnoreUrlsConfig ignoreUrlsConfig;

              /**
               * 注冊sa-token攔截器
               */

              @Override
              public void addInterceptors(InterceptorRegistry registry) {
                  registry.addInterceptor(new SaRouteInterceptor((req, resp, handler) -> {
                      // 獲取配置文件中的白名單路徑
                      List<String> ignoreUrls = ignoreUrlsConfig.getUrls();
                      // 登錄認證:除白名單路徑外均需要登錄認證
                      SaRouter.match(Collections.singletonList("/**"), ignoreUrls, StpUtil::checkLogin);
                  })).addPathPatterns("/**");
              }
          }
          • application.yml文件中的白名單配置如下,注意開放Swagger的訪問路徑和靜態(tài)資源路徑;
          # 訪問白名單路徑
          secure:
            ignored:
              urls:
                - /
                - /swagger-ui/
                - /*.html
                - /favicon.ico
                - /**/*.html
                - /**/*.css
                - /**/*.js
                - /swagger-resources/**
                - /v2/api-docs/**
                - /actuator/**
                - /admin/login
                - /admin/isLogin
          • 由于未登錄狀態(tài)下訪問接口,Sa-Token會拋出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());
              }
          }
          • 之后當我們在登錄狀態(tài)下訪問接口時,可以獲取到數(shù)據(jù);
          • 當我們未登錄狀態(tài)(不帶token)時無法正常訪問接口,返回code401。

          角色認證

          角色認證也就是我們定義好一套規(guī)則,比如ROLE-ADMIN角色可以訪問/brand下的所有資源,而ROLE_USER角色只能訪問/brand/listAll,接下來我們來實現(xiàn)下角色認證。
          • 首先我們需要擴展Sa-Token的StpInterface接口,通過實現(xiàn)方法來返回用戶的角色碼和權(quán)限碼;
          /**
           * 自定義權(quán)限驗證接口擴展
           */

          @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角色可以訪問所有路徑,而ROLE_USER只能訪問/brand/listAll路徑;
          /**
           * Sa-Token相關配置
           */

          @Configuration
          public class SaTokenConfig implements WebMvcConfigurer {

              @Autowired
              private IgnoreUrlsConfig ignoreUrlsConfig;

              /**
               * 注冊sa-token攔截器
               */

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

          @ControllerAdvice
          public class GlobalExceptionHandler {

              /**
               * 處理沒有角色的異常
               */

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

          權(quán)限認證

          當我們給角色分配好權(quán)限,然后給用戶分配好角色后,用戶就擁有了這些權(quán)限。我們可以為每個接口分配不同的權(quán)限,擁有該權(quán)限的用戶就可以訪問該接口。這就是權(quán)限認證,接下來我們來實現(xiàn)下它。
          • 我們可以在Sa-Token的攔截器中配置路由規(guī)則,admin用戶可以訪問所有路徑,而macro用戶只有讀取的權(quán)限,沒有寫、改、刪的權(quán)限;
          /**
           * Sa-Token相關配置
           */

          @Configuration
          public class SaTokenConfig implements WebMvcConfigurer {

              @Autowired
              private IgnoreUrlsConfig ignoreUrlsConfig;

              /**
               * 注冊sa-token攔截器
               */

              @Override
              public void addInterceptors(InterceptorRegistry registry) {
                  registry.addInterceptor(new SaRouteInterceptor((req, resp, handler) -> {
                      // 獲取配置文件中的白名單路徑
                      List<String> ignoreUrls = ignoreUrlsConfig.getUrls();
                      // 登錄認證:除白名單路徑外均需要登錄認證
                      SaRouter.match(Collections.singletonList("/**"), ignoreUrls, StpUtil::checkLogin);
                      // 權(quá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("/**");
              }
          }
          • 當用戶無權(quán)限訪問時,Sa-Token會拋出NotPermissionException異常,我們可以全局處理下;
          /**
           * 全局異常處理
           * Created by macro on 2020/2/27.
           */

          @ControllerAdvice
          public class GlobalExceptionHandler {

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

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

           

          總結(jié)

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

           

          參考資料

          Sa-Token的官方文檔很全,也很良心,不僅提供了解決方式,還提供了解決思路,強烈建議大家去看下。
          官方文檔:http://sa-token.dev33.cn/

          有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)

          歡迎大家關注Java之道公眾號


          好文章,我在看??

          ?
          ?
          瀏覽 35
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  中文字幕一区二区久久人妻网站 | 久久人妻视频 | 国产做受 高潮游戏视频 | 在线三级日韩 | 国产免费污污 |