之前进行鉴权、授权都要写一大堆代码。如果使用像Spring Security这样的框架,又要花好多时间学习,拿过来一用,好多配置项也不知道是干嘛用的,又不想了解。要是不用Spring Security,token的生成、校验、刷新,权限的验证分配,又全要自己写,想想都头大。
Spring Security太重而且配置繁琐。自己实现所有的点必须又要顾及到,更是麻烦。
最近看到一个权限认证框架,真是够简单高效。这里分享一个使用Sa-Token的gateway鉴权demo。
Sa-Token
我们首先编写sa-token模块进行token生成和权限分配。
在sa-token的session模式下生成token非常方便,只需要调用
StpUtil.login(Object id);
复制代码
就可以为账号生成 Token
凭证与 Session
会话了。
配置信息
server:
# 端口
port: 8081
spring:
application:
name: weishuang-account
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/weishuang_account?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL
username: root
password: root
# redis配置
redis:
# Redis数据库索引(默认为0)
database: 0
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
# password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
sa-token:
# token名称 (同时也是cookie名称)
token-name: weishuang-token
# token有效期,单位s 默认30天, -1代表永不过期
timeout: 2592000
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
activity-timeout: -1
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
is-share: true
# token风格
token-style: uuid
# 是否输出操作日志
is-log: false
# token前缀
token-prefix: Bearer
复制代码
在sa-token的配置中,我使用了token-name
来指定token
的名称,如果不指定那么就是默认的satoken。
使用token-prefix
来指定token的前缀,这样前端在header
里传入token的时候就要加上Bearer
了(注意有个空格),建议和前端商量一下需不需要这个前缀,如果不使用,直接传token就好了。
现在调用接口时传入的格式就是
weishuang-token = Bearer token123456
复制代码
sa-token的session模式需要redis来存储session,在微服务中,各个服务的session也需要redis来同步。
当然sa-token也支持jwt来生成无状态的token,这样就不需要在服务中引入redis了。本文使用session模式(jwt的刷新token等机制还要自己实现,session的刷新sa-token都帮我们做好了,使用默认的模式更加方便,而且功能更多)
我们来编写一个登录接口
User
@Data
public class User {
/**
* id
*/
private String id;
/**
* 账号
*/
private String userName;
/**
* 密码
*/
private String password;
}
复制代码
UserController
@RestController
@RequestMapping("/account/user/")
public class UserController {
@Autowired
private UserManager userManager;
@PostMapping("doLogin")
public SaResult doLogin(@RequestBody AccountUserLoginDTO req) {
userManager.login(req);
return SaResult.ok("登录成功");
}
}
复制代码
UserManager
@Component
public class UserManagerImpl implements UserManager {
@Autowired
private UserService userService;
@Override
public void login(AccountUserLoginDTO req) {
//生成密码
String password = PasswordUtil.generatePassword(req.getPassword());
//调用数据库校验是否存在用户
User user = userService.getOne(req.getUserName(), password);
if (user == null) {
throw new RuntimeException("账号或密码错误");
}
//为账号生成Token凭证与Session会话
StpUtil.login(user.getId());
//为该用户的session存储更多信息
//这里为了方便直接把user实体存进去了,也包括了密码,自己实现时不建议这样做。
StpUtil.getSession().set("USER_DATA", user);
}
}
复制代码
UserService
@Service
public class UserService