spring boot 配置sa-token
前言
- sa-token一个国产的,权限认证框架。
- 功能上类似Apache Shiro
、
Spring Security等。但是更加强大、容易上手 - 主要解决: 登录认证、权限认证、Session会话 等一系列权限相关问题。大部分api调用都是一行代码就能解决
- sa-token功能很强大。以下简单记录登录认证、权限认证、路由拦截鉴权等功能
- 在
sa-token
中,登录授权就是如此的简单,不需要什么全局过滤器,不需要各种乱七八糟的配置!只需要一行简单的API调用,即可完成会话的登录授权! - 只要完成登录认证、权限认证、路由拦截鉴权就可以简单的为项目增加一个鉴权系统。
- 如果只需要登录认证,权限认证都不需要,就更简单了。。写一个路由拦截配置文件、用户登录调用login方法就搞定一切了。
1.spring boot
引入sa-token
<!-- Sa-Token 权限认证, 在线文档: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文件增加如下配置项目,当然也可以不增加:
# Sa-Token配置 sa-token: # token名称 (同时也是cookie名称) token-name: satoken # token有效期,单位s 默认30天, -1代表永不过期 timeout: 2592000 # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒 activity-timeout: -1 # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) is-concurrent: true # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) is-share: false # token风格 token-style: uuid # 是否输出操作日志 is-log: false
3.登录认证
所谓登录认证,说白了就是限制某些API接口必须登录后才能访问。
如何判断一个会话是否已经登录?用户登录后,服务器端调用
login方法
。sa-token会帮我们对会话进行做个标记。每次需要登录认证的时候校验这些标记。有标记者视为已登录,无标记者视为未登录。默认,不特殊处理的情况下,sa-token是通过cookie来标记的。如果清掉了cookie,则需要重新登录。
// 标记当前会话登录的账号id // 建议的参数类型:long | int | String, 不可以传入复杂类型,如:User、Admin等等 StpUtil.login(Object id); // 当前会话注销登录 StpUtil.logout(); // 获取当前会话是否已经登录,返回true=已登录,false=未登录 StpUtil.isLogin(); // 检验当前会话是否已经登录, 如果未登录,则抛出异常:`NotLoginException` StpUtil.checkLogin() // 获取当前会话账号id, 如果未登录,则抛出异常:`NotLoginException` StpUtil.getLoginId(); // 类似查询API还有: StpUtil.getLoginIdAsString(); // 获取当前会话账号id, 并转化为`String`类型 StpUtil.getLoginIdAsInt(); // 获取当前会话账号id, 并转化为`int`类型 StpUtil.getLoginIdAsLong(); // 获取当前会话账号id, 并转化为`long`类型 // ---------- 指定未登录情形下返回的默认值 ---------- // 获取当前会话账号id, 如果未登录,则返回null StpUtil.getLoginIdDefaultNull(); // 获取当前会话账号id, 如果未登录,则返回默认值 (`defaultValue`可以为任意类型) StpUtil.getLoginId(T defaultValue); // 获取指定token对应的账号id,如果未登录,则返回 null StpUtil.getLoginIdByToken(String tokenValue); // 获取当前`StpLogic`的token名称 StpUtil.getTokenName(); // 获取当前会话的token值 StpUtil.getTokenValue(); // 获取当前会话的token信息参数 StpUtil.getTokenInfo();
4.权限认证
所谓权限认证,认证的核心就是一个账号是否拥有一个权限码。有,就让你通过。没有?那么禁止访问!
再往底了说,就是每个账号都会拥有一个权限码集合,我来校验这个集合中是否包含指定的权限码
例如:当前账号拥有权限码集合:
["user-add", "user-delete", "user-get"]
,这时候我来校验权限"user-update"
,则其结果就是:验证失败,禁止访问所以核心问题如下:
4.1 获取当前账号的权限码集合
你需要做的就是新建一个类,实现
StpInterface
接口添加
@Component
保证此类被SpringBoot扫描,完成Sa-Token的自定义权限验证扩展注意方法传入的loginID。实际项目需要更加loginID实际返回该ID对于的权限。
/** * 自定义权限验证接口扩展 */ @Component // 保证此类被SpringBoot扫描,完成Sa-Token的自定义权限验证扩展 public class StpInterfaceImpl implements StpInterface { /** * 返回一个账号所拥有的权限码集合 */ @Override public List<String> getPermissionList(Object loginId, String loginType) { // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限 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; } /** * 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验) */ @Override public List<String> getRoleList(Object loginId, String loginType) { // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色 List<String> list = new ArrayList<String>(); list.add("admin"); list.add("super-admin"); return list; } }
4.2权限认证
告诉了sa-token用户所拥有的权限或角色后。就可以用以下api来鉴权了
// 判断:当前账号是否含有指定权限, 返回true或false StpUtil.hasPermission("user-update"); // 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException StpUtil.checkPermission("user-update"); // 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过] StpUtil.checkPermissionAnd("user-update", "user-delete"); // 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可] StpUtil.checkPermissionOr("user-update", "user-delete"); // 判断:当前账号是否拥有指定角色, 返回true或false StpUtil.hasRole("super-admin"); // 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException StpUtil.checkRole("super-admin"); // 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过] StpUtil.checkRoleAnd("super-admin", "shop-admin"); // 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可] StpUtil.checkRoleOr("super-admin", "shop-admin");
5.路由拦截鉴权
为什么需要路由拦截鉴权?
方法1:每个方法手动调用如上的鉴权api。如果没有权限就不执行。。这个办法侵入性太高
方法2:注解鉴权。。。这个办法同样不太方便。。。需要修改api接口的注解。
方法3:自己动手书写全局拦截器
方法4:sa-token提供的路由拦截鉴权。。这个是比较方便的。。改动比较少。只需要配置相关配置类。基本上所有事情就搞定了。。。
增加如下配置类即可。如下代码注册了一个登录认证拦截器,并且排除了
/user/doLogin
接口用来开放登录(除了/user/doLogin
以外的所有接口都需要登录才能访问)@Configuration public class SaTokenConfigure implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // 注册路由拦截器,自定义认证规则 registry.addInterceptor(new SaRouteInterceptor((req, res, handler)->{ // 根据路由划分模块,不同模块不同鉴权 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"); } }
在校验函数内不只可以使用
StpUtil.checkPermission("xxx")
进行权限校验,你还可以写任意代码最重要的是日记记录。。
// 登录认证 -- 拦截所有路由,并排除/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 路由匹配,还可以根据很多其它特征进行匹配,以下是所有可匹配的特征:
// 基础写法样例:匹配一个path,执行一个校验函数 SaRouter.match("/user/**").check(r -> StpUtil.checkLogin()); // 根据 path 路由匹配 ——— 支持写多个path,支持写 restful 风格路由 SaRouter.match("/user/**", "/goods/**", "/art/get/{id}").check( /* 要执行的校验函数 */ ); // 根据 path 路由排除匹配 SaRouter.match("/**").notMatch("*.html", "*.css", "*.js").check( /* 要执行的校验函数 */ ); // 根据请求类型匹配 SaRouter.match(SaHttpMethod.GET).check( /* 要执行的校验函数 */ ); // 根据一个 boolean 条件进行匹配 SaRouter.match( StpUtil.isLogin() ).check( /* 要执行的校验函数 */ ); // 根据一个返回 boolean 结果的lambda表达式匹配 SaRouter.match( r -> StpUtil.isLogin() ).check( /* 要执行的校验函数 */ ); // 多个条件一起使用 SaRouter.match(SaHttpMethod.GET).match("/**").check( /* 要执行的校验函数 */ ); // 可以无限连缀下去 SaRouter .match(SaHttpMethod.GET) .match("/admin/**") .match("/user/**") .notMatch("/**/*.js") .notMatch("/**/*.css") // .... .check( /* 只有上述所有条件都匹配成功,才会执行最后的check校验函数 */ );