开箱即用!看看人家的微服务权限解决方案,那叫一个优雅

前置知识

我们将采用Nacos作为注册中心,Gateway作为网关,使用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服务,启动顺序无所谓;

添加图片注释,不超过 140 字(可选)

  • 直接通过网关访问登录接口获取Token,访问地址:http://localhost:9201/auth/user/login

添加图片注释,不超过 140 字(可选)

  • 通过网关访问API服务,不带Token调用获取用户信息的接口,无法正常访问,访问地址:http://localhost:9201/api/user/info

添加图片注释,不超过 140 字(可选)

  • 通过网关访问API服务,带Token调用获取用户信息的接口,可以正常访问;

添加图片注释,不超过 140 字(可选)

  • 通过网关访问API服务,使用macro用户访问需api:test:hello权限的测试接口,无法正常访问,访问地址:http://localhost:9201/api/test/hello

添加图片注释,不超过 140 字(可选)

  • 登录切换为admin用户,该用户具有api:test:hello权限;

添加图片注释,不超过 140 字(可选)

  • 通过网关访问API服务,使用admin用户访问测试接口,可以正常访问。

添加图片注释,不超过 140 字(可选)

总结

对比之前使用Spring Security的微服务权限解决方案,Sa-Token的解决方案更简单、更优雅。使用Security我们需要定义鉴权管理器、分别处理未认证和未授权的情况、还要自己定义认证和资源服务器配置,使用非常繁琐。而使用Sa-Token,只要在网关上配置过滤器实现认证和授权,然后调用API实现登录及权限分配即可。具体区别可以参考下图。

添加图片注释,不超过 140 字(可选)

项目源码地址

https://github.com/macrozheng/springcloud-learning/tree/master/micro-sa-token

资料获取:
大家点赞、收藏、关注、评论啦 、查看👇🏻👇🏻👇🏻微信公众号获取联系方式👇🏻👇🏻👇🏻
精彩专栏推荐订阅:下方专栏👇🏻👇🏻👇🏻👇🏻

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值