记得之前写过一篇文章微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现对立认证和鉴权! ,提供了Spring Cloud中的权限解决方案,其实一开始整合的时候我始终玩不转,又是查资料又是看源码,最终才胜利了。最近尝试了下Sa-Token提供的微服务权限解决方案,用起来感觉很优雅,举荐给大家!
SpringBoot实战电商我的项目mall(50k+star)地址:https://github.com/macrozheng/mall
前置常识
咱们将采纳Nacos作为注册核心,Gateway作为网关,应用Sa-Token提供的微服务权限解决方案,此计划是基于之前的解决方案革新的,对这些技术不理解的敌人能够看下上面的文章。
- Spring Cloud Gateway:新一代API网关服务
- Spring Cloud Alibaba:Nacos 作为注册核心和配置核心应用
- 微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现对立认证和鉴权!
- Sa-Token应用教程
利用架构
还是和之前计划差不多的思路,认证服务负责登录解决,网关负责登录认证和权限认证,其余API服务负责解决本人的业务逻辑。为了能在多个服务中共享Sa-Token的Session,所有服务都须要集成Sa-Token和Redis。
- micro-sa-token-common:通用工具包,其余服务专用的用户类
UserDTO
和通用返回后果类CommonResult
被抽取到了这里。 - micro-sa-token-gateway:网关服务,负责申请转发、登录认证和权限认证。
- micro-sa-token-auth:认证服务,仅蕴含一个登录接口,调用Sa-Token的API实现。
- micro-sa-token-api:受爱护的API服务,用户通过网关鉴权通过后能够拜访该服务。
计划实现
接下来实现下这套解决方案,顺次搭建网关服务、认证服务和API服务。
micro-sa-token-gateway
咱们首先来搭建下网关服务,它将负责整个微服务的登录认证和权限认证。
- 除了通用的Gateway依赖,咱们还须要在
pom.xml
中增加如下依赖,包含Sa-Token的Reactor响应式依赖,整合Redis实现分布式Session的依赖以及咱们的micro-sa-token-common
依赖;
<dependencies>
<!-- Sa-Token 权限认证(Reactor响应式集成) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-reactor-spring-boot-starter</artifactId>
<version>1.24.0</version>
</dependency>
<!-- Sa-Token 整合 Redis (应用jackson序列化形式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.24.0</version>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- micro-sa-token通用依赖 -->
<dependency>
<groupId>com.macro.cloud</groupId>
<artifactId>micro-sa-token-common</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
- 接下来批改配置文件
application.yml
,增加Redis配置和Sa-Token的配置,如果你看过之前那篇Sa-Token应用教程的话,根本就晓得这些配置的作用了;
spring:
redis:
database: 0
port: 6379
host: localhost
password:
# 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的配置类
SaTokenConfig
,注入一个过滤器用于登录认证和权限认证,在setAuth
办法中增加路由规定,在setError
办法中增加鉴权失败的回调解决;
@Configuration
public class SaTokenConfig {
/**
* 注册Sa-Token全局过滤器
*/
@Bean
public SaReactorFilter getSaReactorFilter() {
return new SaReactorFilter()
// 拦挡地址
.addInclude("/**")
// 凋谢地址
.addExclude("/favicon.ico")
// 鉴权办法:每次拜访进入
.setAuth(r -> {
// 登录认证:除登录接口都须要认证
SaRouter.match("/**", "/auth/user/login", StpUtil::checkLogin);
// 权限认证:不同接口拜访权限不同
SaRouter.match("/api/test/hello", () -> StpUtil.checkPermission("api:test:hello"));
SaRouter.match("/api/user/info", () -> StpUtil.checkPermission("api:user:info"));
})
// setAuth办法异样解决
.setError(e -> {
// 设置谬误返回格局为JSON
ServerWebExchange exchange = SaReactorSyncHolder.getContent();
exchange.getResponse().getHeaders().set("Content-Type", "application/json; charset=utf-8");
return SaResult.error(e.getMessage());
});
}
}
- 扩大下Sa-Token提供的
StpInterface
接口,用于获取用户的权限,咱们在用户登录当前会把用户信息存到Session中去,权限信息也会在外面,所以权限码只有从Session中获取即可。
/**
* 自定义权限验证接口扩大
*/
@Component
public class StpInterfaceImpl implements StpInterface {
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
// 返回此 loginId 领有的权限码列表
UserDTO userDTO = (UserDTO) StpUtil.getSession().get("userInfo");
return userDTO.getPermissionList();
}
@Override
public List<String> getRoleList(Object loginId, String loginType) {
// 返回此 loginId 领有的角色码列表
return null;
}
}
micro-sa-token-auth
接下来咱们来搭建下认证服务,只有集成Sa-Token并实现登录接口即可,非常简单。
- 首先在
pom.xml
中增加相干依赖,包含Sa-Token的SpringBoot依赖、整合Redis实现分布式Session的依赖以及咱们的micro-sa-token-common
依赖;
<dependencies>
<!-- Sa-Token 权限认证 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.24.0</version>
</dependency>
<!-- Sa-Token 整合 Redis (应用jackson序列化形式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.24.0</version>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- micro-sa-token通用依赖 -->
<dependency>
<groupId>com.macro.cloud</groupId>
<artifactId>micro-sa-token-common</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
- 接下来批改配置文件
application.yml
,照抄之前网关的配置即可;
spring:
redis:
database: 0
port: 6379
host: localhost
password:
# 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
- 在
UserController
中定义好登录接口,登录胜利后返回Token,具体实现在UserServiceImpl
类中;
/**
* 自定义Oauth2获取令牌接口
* Created by macro on 2020/7/17.
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserServiceImpl userService;
@RequestMapping(value = "/login", method = RequestMethod.POST)
public CommonResult login(@RequestParam String username, @RequestParam String password) {
SaTokenInfo saTokenInfo = userService.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);
}
}
- 在
UserServiceImpl
中增加登录的具体逻辑,首先验证明码,明码校验胜利后,告诉下Sa-Token登录的用户ID,而后把用户信息间接存储到Session中去;
/**
* 用户治理业务类
* Created by macro on 2020/6/19.
*/
@Service
public class UserServiceImpl{
private List<UserDTO> userList;
public SaTokenInfo login(String username, String password) {
SaTokenInfo saTokenInfo = null;
UserDTO userDTO = loadUserByUsername(username);
if (userDTO == null) {
return null;
}
if (!SaSecureUtil.md5(password).equals(userDTO.getPassword())) {
return null;
}
// 明码校验胜利后登录,一行代码实现登录
StpUtil.login(userDTO.getId());
// 将用户信息存储到Session中
StpUtil.getSession().set("userInfo",userDTO);
// 获取以后登录用户Token信息
saTokenInfo = StpUtil.getTokenInfo();
return saTokenInfo;
}
}
- 这里有一点须要揭示下,Sa-Token的Session并不是咱们平时了解的HttpSession,而是它本人实现的相似Session的机制。
micro-sa-token-api
接下来咱们来搭建一个受爱护的API服务,实现获取登录用户信息的接口和须要非凡权限能力拜访的测试接口。
- 首先在
pom.xml
中增加相干依赖,和下面的micro-sa-token-auth
一样;
<dependencies>
<!-- Sa-Token 权限认证 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.24.0</version>
</dependency>
<!-- Sa-Token 整合 Redis (应用jackson序列化形式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.24.0</version>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- micro-sa-token通用依赖 -->
<dependency>
<groupId>com.macro.cloud</groupId>
<artifactId>micro-sa-token-common</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
- 接下来批改配置文件
application.yml
,照抄之前网关的配置即可;
spring:
redis:
database: 0
port: 6379
host: localhost
password:
# 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
- 增加获取用户信息的接口,因为应用了Redis实现分布式Session,间接从Session中获取即可,是不是非常简单!
/**
* 获取登录用户信息接口
* Created by macro on 2020/6/19.
*/
@RestController
@RequestMapping("/user")
public class UserController{
@GetMapping("/info")
public CommonResult<UserDTO> userInfo() {
UserDTO userDTO = (UserDTO) StpUtil.getSession().get("userInfo");
return CommonResult.success(userDTO);
}
}
- 增加须要
api:test:hello
权限拜访的测试接口,预置的admin
用户领有该权限,而macro
用户是没有的。
/**
* 测试接口
* Created by macro on 2020/6/19.
*/
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/hello")
public CommonResult hello() {
return CommonResult.success("Hello World.");
}
}
性能演示
三个服务搭建实现后,咱们用Postman来演示下微服务的认证受权性能。
- 首先启动Nacos和Redis服务,而后再启动
micro-sa-token-gateway
、micro-sa-token-auth
和micro-sa-token-api
服务,启动程序无所谓;
- 间接通过网关拜访登录接口获取Token,拜访地址:http://localhost:9201/auth/us…
- 通过网关拜访API服务,
不带Token
调用获取用户信息的接口,无奈失常拜访,拜访地址:http://localhost:9201/api/use…
- 通过网关拜访API服务,
带Token
调用获取用户信息的接口,能够失常拜访;
- 通过网关拜访API服务,应用
macro
用户拜访需api:test:hello
权限的测试接口,无奈失常拜访,拜访地址:http://localhost:9201/api/tes…
- 登录切换为
admin
用户,该用户具备api:test:hello
权限;
- 通过网关拜访API服务,应用
admin
用户拜访测试接口,能够失常拜访。
总结
比照之前应用Spring Security的微服务权限解决方案,Sa-Token的解决方案更简略、更优雅。应用Security咱们须要定义鉴权管理器、别离解决未认证和未受权的状况、还要本人定义认证和资源服务器配置,应用十分繁琐。而应用Sa-Token,只有在网关上配置过滤器实现认证和受权,而后调用API实现登录及权限调配即可。具体区别能够参考下图。
参考资料
官网文档:http://sa-token.dev33.cn/
我的项目源码地址
https://github.com/macrozheng/springcloud-learning
发表回复