主要解决:登录认证、权限认证、单点登录、OAuth2.0、分布式Session会话、微服务网关鉴权
1.添加依赖
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.31.0</version>
</dependency>
设置配置文件(可做可不做)
server:
# 端口
port: 8081
# 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.创建启动类
@SpringBootApplication
public class SaTokenDemoApplication {
public static void main(String[] args) throws JsonProcessingException {
SpringApplication.run(SaTokenDemoApplication.class, args);
System.out.println("启动成功:Sa-Token配置如下:" + SaManager.getConfig());
}
}
登录认证
登录
// 会话登录:参数填写要登录的账号id,建议的数据类型:long | int | String, 不可以传入复杂类型,如:User、Admin 等等
StpUtil.login(Object id);
关键: Sa-Token 为这个账号创建了一个Token凭证,且通过 Cookie 上下文返回给了前端
一般的会话登录接口
// 会话登录接口
@RequestMapping("doLogin")
public SaResult doLogin(String name, String pwd) {
// 第一步:比对前端提交的账号名称、密码
if("zhang".equals(name) && "123456".equals(pwd)) {
// 第二步:根据账号id,进行登录
StpUtil.login(10001);
return SaResult.ok("登录成功");
}
return SaResult.error("登录失败");
}
cookie功能的基本两点
// 当前会话注销登录
StpUtil.logout();
// 获取当前会话是否已经登录,返回true=已登录,false=未登录
StpUtil.isLogin();
// 检验当前会话是否已经登录, 如果未登录,则抛出异常:`NotLoginException`
StpUtil.checkLogin();
包含的异常: 异常 NotLoginException
代表当前会话暂未登录,可能的原因有很多: 前端没有提交 Token、前端提交的 Token 是无效的、前端提交的 Token 已经过期 …… 等等,
会话查询
// 获取当前会话账号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值
StpUtil.getTokenValue();
// 获取当前`StpLogic`的token名称
StpUtil.getTokenName();
// 获取指定token对应的账号id,如果未登录,则返回 null
StpUtil.getLoginIdByToken(String tokenValue);
// 获取当前会话剩余有效期(单位:s,返回-1代表永久有效)
StpUtil.getTokenTimeout();
// 获取当前会话的token信息参数
StpUtil.getTokenInfo();
测试
/**
* 登录测试
*/
@RestController
@RequestMapping("/acc/")
public class LoginController {
// 测试登录 ---- http://localhost:8081/acc/doLogin?name=zhang&pwd=123456
@RequestMapping("doLogin")
public SaResult doLogin(String name, String pwd) {
// 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对
if("zhang".equals(name) && "123456".equals(pwd)) {
StpUtil.login(10001);
return SaResult.ok("登录成功");
}
return SaResult.error("登录失败");
}
// 查询登录状态 ---- http://localhost:8081/acc/isLogin
@RequestMapping("isLogin")
public SaResult isLogin() {
return SaResult.ok("是否登录:" + StpUtil.isLogin());
}
// 查询 Token 信息 ---- http://localhost:8081/acc/tokenInfo
@RequestMapping("tokenInfo")
public SaResult tokenInfo() {
return SaResult.data(StpUtil.getTokenInfo());
}
// 测试注销 ---- http://localhost:8081/acc/logout
@RequestMapping("logout")
public SaResult logout() {
StpUtil.logout();
return SaResult.ok();
}
}
会自动生成一个token存在浏览器的cookies里面
使用: 新建一个类,实现StpInterface接口
/**
* 自定义权限验证接口扩展
*/
@Component // 保证此类被SpringBoot扫描, 完成自定义权限扩展
public class StpInterfaceImpl implements StpInterface {
@Override
public List<String> getPermissionList(Object o, String s) {
ArrayList<String> list = new ArrayList<>();
list.add("101");
list.add("user:add");
list.add("user:delete");
list.add("user:update");
return list;
}
/**
* 返回一个账号所拥有的角色标识的集合(权限与角色可以分开校验)
* @param o
* @param s
* @return
*/
@Override
public List<String> getRoleList(Object o, String s) {
ArrayList<String> list = new ArrayList<>();
list.add("admin");
list.add("super-admin");
return list;
}
}
拦截全局异常
@RestControllerAdvice
public class GlobalExceptionHandler {
// 全局异常拦截
@ExceptionHandler
public SaResult handlerException(Exception e) {
e.printStackTrace();
return SaResult.error(e.getMessage());
}
}
上帝权限: 当一个账号拥有 "*"
权限时,他可以验证通过任何权限码 (角色认证同理)
踢人下线
1.强制注销
StpUtil.logout(10001); // 强制指定账号注销下线
StpUtil.logout(10001, "PC"); // 强制指定账号指定端注销下线
StpUtil.logoutByTokenValue("token"); // 强制指定 Token 注销下线
注解鉴权
// 将指定账号踢下线
StpUtil.kickout(10001);
// 将指定账号指定端踢下线
StpUtil.kickout(10001, "PC");
// 将指定 Token 踢下线
StpUtil.kickoutByTokenValue("token");
```
强制注销 和 踢人下线 的区别在于:
强制注销等价于对方主动调用了注销方法,再次访问会提示:Token无效。
踢人下线不会清除Token信息,而是将其打上特定标记,再次访问会提示:Token已被踢下线。
注解鉴权 —— 优雅的将鉴权与业务代码分离!
@SaCheckLogin
: 登录认证 —— 只有登录之后才能进入该方法。
@SaCheckRole("admin")
: 角色认证 —— 必须具有指定角色标识才能进入该方法。
@SaCheckPermission("user:add")
: 权限认证 —— 必须具有指定权限才能进入该方法。
@SaCheckSafe
: 二级认证校验 —— 必须二级认证之后才能进入该方法。
@SaCheckBasic
: HttpBasic认证 —— 只有通过 Basic 认证后才能进入该方法。
@SaIgnore
:忽略认证 —— 表示被修饰的方法或类无需进行注解认证和路由拦截认证。
@SaCheckDisable("comment")
:账号服务封禁校验 —— 校验当前账号指定服务是否被封禁
a-Token 使用全局拦截器完成注解鉴权功能,为了不为项目带来不必要的性能负担,拦截器默认处于关闭状态因此,为了使用注解鉴权,你必须手动将 Sa-Token 的全局拦截器注册到你项目中
/**
* @BelongsProject: saToken
* @BelongsPackage: com.example.config
* @Author: Mr.Hua
* @CreateTime: 2022-10-09 17:22
* @Description: TODO
* @Version: 1.0
*/
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
// 注册 sa token 拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器 打开注解鉴权
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
}
}
未完待续.....