百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

再见Spring Security!一款功能强大的权限认证框架,用起来够优雅

ccwgpt 2024-10-07 06:50 27 浏览 0 评论

在我们做SpringBoot项目的时候,认证授权是必不可少的功能!我们经常会选择Shiro、Spring Security这类权限认证框架来实现,但这些框架使用起来有点繁琐,而且功能也不够强大。最近发现一款功能强大的权限认证框架Sa-Token,它使用简单、API设计优雅,推荐给大家!

Sa-Token简介

Sa-Token是一款轻量级的Java权限认证框架,可以用来解决登录认证、权限认证、Session会话、单点登录、OAuth2.0、微服务网关鉴权等一系列权限相关问题。

框架集成简单、开箱即用、API设计优雅,通过Sa-Token,你将以一种极其简单的方式实现系统的权限认证部分,有时候往往只需一行代码就能实现功能。

Sa-Token功能很全,具体可以参考下图。

使用

在SpringBoot中使用Sa-Token是非常简单的,接下来我们使用它来实现最常用的认证授权功能,包括登录认证、角色认证和权限认证。

集成及配置

Sa-Token的集成和配置都非常简单,不愧为开箱即用。

  • 首先我们需要在项目的pom.xml中添加Sa-Token的相关依赖;
<!-- Sa-Token 权限认证 -->
<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临时有效期 (指定时间内无操作就视为token过期),单位秒
  activity-timeout: -1
  # 是否允许同一账号并发登录 (为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

登录认证

在管理系统中,除了登录接口,基本都需要登录认证,在Sa-Token中使用路由拦截鉴权是最方便的,下面我们来实现下。

  • 实现登录认证非常简单,首先在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添加登录的具体逻辑,先验证密码,然后调用StpUtil.login(adminUser.getId())即可实现登录,调用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;
        }
        // 密码校验成功后登录,一行代码实现登录
        StpUtil.login(adminUser.getId());
        // 获取当前登录用户Token信息
        saTokenInfo = StpUtil.getTokenInfo();
        return saTokenInfo;
    }
}
  • 我们再添加一个测试接口用于查询当前登录状态,返回true表示已经登录;
/**
 * Created by macro on 2020/10/15.
 */
@Slf4j
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
    @ApiOperation(value = "查询当前登录状态")
    @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了,表示你已经是登录状态了;
  • 接下来我们需要把除登录接口以外的接口都添加登录认证,添加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的访问路径和静态资源路径;
# 访问白名单路径
secure:
  ignored:
    urls:
      - /
      - /swagger-ui/
      - /*.html
      - /favicon.ico
      - /**/*.html
      - /**/*.css
      - /**/*.js
      - /swagger-resources/**
      - /v2/api-docs/**
      - /actuator/**
      - /admin/login
      - /admin/isLogin
  • 由于未登录状态下访问接口,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());
    }
}
  • 之后当我们在登录状态下访问接口时,可以获取到数据;
  • 当我们未登录状态(不带token)时无法正常访问接口,返回code401

角色认证

角色认证也就是我们定义好一套规则,比如ROLE-ADMIN角色可以访问/brand下的所有资源,而ROLE_USER角色只能访问/brand/listAll,接下来我们来实现下角色认证。

  • 首先我们需要扩展Sa-Token的StpInterface接口,通过实现方法来返回用户的角色码和权限码;
/**
 * 自定义权限验证接口扩展
 */
@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的拦截器中配置路由规则,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只能访问查询全部接口
            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());
    }
}
  • 我们现在有两个用户,admin用户具有ROLE_ADMIN角色,macro用户具有ROLE_USER角色;
  • 使用admin账号访问/brand/list接口可以正常访问;
  • 使用macro账号访问/brand/list接口无法正常访问,返回code403

权限认证

当我们给角色分配好权限,然后给用户分配好角色后,用户就拥有了这些权限。我们可以为每个接口分配不同的权限,拥有该权限的用户就可以访问该接口。这就是权限认证,接下来我们来实现下它。

  • 我们可以在Sa-Token的拦截器中配置路由规则,admin用户可以访问所有路径,而macro用户只有读取的权限,没有写、改、删的权限;
/**
 * 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);
            // 权限认证:不同接口, 校验不同权限
            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("/**");
    }
}
  • 当用户无权限访问时,Sa-Token会抛出NotPermissionException异常,我们可以全局处理下;
/**
 * 全局异常处理
 * Created by macro on 2020/2/27.
 */
@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理没有权限的异常
     */
    @ResponseBody
    @ExceptionHandler(value = NotPermissionException.class)
    public CommonResult handleNotPermissionException(NotPermissionException e) {
        return CommonResult.forbidden(e.getMessage());
    }
}
  • 使用admin账号访问/brand/delete接口可以正常访问;
  • 使用macro账号访问/brand/delete无法正常访问,返回code403

总结

通过对Sa-Token的一波实践,我们可以发现它的API设计非常优雅,比起Shiro和Spring Security来说确实顺手多了。Sa-Token不仅提供了一系列强大的权限相关功能,还提供了很多标准的解决方案,比如Oauth2、分布式Session会话等,大家感兴趣的话可以研究下。

参考资料

Sa-Token的官方文档很全,也很良心,不仅提供了解决方式,还提供了解决思路,强烈建议大家去看下。

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

项目源码地址

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


来源:https://mp.weixin.qq.com/s/v4b7b-r1V8lpd_oV7qoH8g

相关推荐

用Deepseek扩写土木工程毕业论文实操指南

用Deepseek扩写毕业论文实操指南一、前期准备整理现有论文初稿/提纲列清楚论文核心框架(背景、现状、意义、方法、数据、结论等)梳理好关键文献,明确核心技术路线二、Deepseek扩写核心思路...

985学霸亲授,DeepSeek也能绘6大科研图表,5分钟就出图

在实验数据处理中,高效可视化是每个科研人的必修课。传统绘图软件操作复杂、耗时费力,而智能工具DeepSeek的出现彻底改变了这一现状。本文将详解如何用DeepSeek一键生成六大科研常用图表,从思维导...

AI写论文刷屏?大学生正在丢掉的思考力

一、宿舍深夜:当论文变成"Ctrl+C+V"凌晨两点的大学宿舍,小王对着电脑屏幕叹气。本该三天前开始写的近代史论文,此刻还一片空白。他熟练打开某AI写作网站,输入"论五四运动的...

Grok在辅助论文写作上能不能既“聪明”又“可怕”?!

AcademicIdeas-学境思源AI初稿写作随着人工智能技术的飞速发展,论文写作这一学术任务正迎来新的助力。2025年2月18日,美国xAI公司推出了备受瞩目的Grok3模型,其创始人埃隆·...

大四论文沟通场景!音频转文字难题听脑AI来化解

大四学生都知道,写论文时和导师沟通修改意见,简直是“过关斩将”。电话、语音沟通完,想把导师说的修改方向、重点要求记下来,麻烦事儿可不少。手写记不全,用普通录音转文字工具,转完还得自己慢慢找重点,稍不注...

论文写作 | 技术路线图怎么画?(提供经典优秀模板参考)

技术路线图是一种图表或文字说明,用于描述研究目标、方法和实施计划。它展示了研究的整体框架和步骤,有助于读者理解研究的逻辑和进展。在课题及论文中,技术路线图是常见的一部分,甚至是一个类似心脏一样的中枢器...

25年信息系统项目管理师考试第2批论文题目写作建议思路框架

25年信息系统项目管理师考试第2批论文题目写作建议思路框架--马军老师

微信购物应尽快纳入法律框架(微信购物管辖)

符向军近日,甘肃省工商行政管理局发布《2016年上半年信息分析报告》。报告显示,微信网购纠纷迅猛增长,网络购物投诉呈上升趋势。投诉的主要问题有出售的商品质量不过关、消费者通过微信付款后对方不发货、购买...

泛珠三角区域网络媒体与腾讯微信签署《战略合作框架协议》

新海南客户端、南海网7月14日消息(记者任桐)7月14日上午,参加第四届泛珠三角区域合作网络媒体论坛的区域网络媒体负责人及嘉宾一行到腾讯微信总部座谈交流,并签署《战略合作框架协议》(以下简称《框架协...

离线使用、植入微信-看乐心Mambo手环如何打破框架

从2014年开始智能手环就成功进入人们的生活,至今已经演变出数据监测、信息推送、心率监测等诸多五花八门的功能,人们选择智能手环并不指望其能够改变身体健康情况,更多的是通过数据来正视自身运动情况和身体健...

微信私域电商运营策略与框架(微信私域怎么做)

...

华专网络:如何零基础制作一个网站出来?

#如何零基础制作一个网站出来?#你是不是觉得网站建设很复杂,觉得自己是小白,需求不明确、流程搞不懂、怕被外包公司坑……这些问题我都懂!今天华专网络就用大白话给你捋清楚建站的全流程,让你轻松get网站制...

WAIC2024丨明日上午9点,不见不散!共同探讨智能社会与全球治理框架

大咖云集,硕果闪耀WAIC2024世界人工智能大会智能社会论坛将于7月5日9:00-12:00与你相约直播间WAIC2024上海杨浦同济大学哔哩哔哩多平台同步直播探讨智能社会与全球治理框架WAIC...

约基奇:森林狼换来戈贝尔时大家都在嘲笑 他们的阵容框架很不错

直播吧5月4日讯西部季后赛半决赛,掘金将迎战森林狼,约基奇赛前接受采访。约基奇说道:“当蒂姆-康纳利(森林狼总经理、前掘金总经理&曾选中约基奇)做了那笔交易(换来戈贝尔)时,每个人都在嘲笑他...

视频号带货为什么一个流量都没有?顶级分析框架送给你

视频号带货为什么一个流量都没有?遇到问题,一定是步步来分析内容,视频号带货一个流量都没有,用另外一个意思来讲,就可以说是零播放。为什么视频号带货一个流量都没有?跟你说再多,都不如来个分析框架。1、是否...

取消回复欢迎 发表评论: