做网站商家,简单的销售网站怎么做,陕西西安网站建设,海东市公司网站建设#x1f388; 1 参考文档 网管关统一鉴权 | sa-token.cc #x1f969;2 微服务中使用Sa-Token依赖引入说明
2.1 Sa-Token依赖
对于网关服务#xff0c;大体来讲分为两种#xff1a;
一种是基于Servlet模型的#xff0c;如#xff1a;Zuul#xff0c;我们需要引入的是… 1 参考文档 网管关统一鉴权 | sa-token.cc 2 微服务中使用Sa-Token依赖引入说明
2.1 Sa-Token依赖
对于网关服务大体来讲分为两种
一种是基于Servlet模型的如Zuul我们需要引入的是sa-token-spring-boot-starter。一种是基于Reactor模型的如SpringCloud Gateway、ShenYu 等等我们需要引入的是sa-token-reactor-spring-boot-starter并且注册全局过滤器 注意切不可直接在一个项目里同时引入这两个依赖否则会造成项目无法启动 2.1.1 基于Servlet模型
!-- Sa-Token 权限认证在线文档https://sa-token.cc --
dependencygroupIdcn.dev33/groupIdartifactIdsa-token-spring-boot-starter/artifactIdversion1.35.0.RC/version
/dependency注意如果你使用的是 SpringBoot 3.x只需要将 sa-token-spring-boot-starter 修改为 sa-token-spring-boot3-starter 即可。 2.1.2 基于Reactor模型
!-- Sa-Token 权限认证Reactor响应式集成在线文档https://sa-token.cc --
dependencygroupIdcn.dev33/groupIdartifactIdsa-token-reactor-spring-boot-starter/artifactIdversion1.35.0.RC/version
/dependency注意如果你使用的是 SpringBoot 3.x只需要将 sa-token-reactor-spring-boot-starter 修改为 sa-token-reactor-spring-boot3-starter 即可。 2.2 Redis依赖
无论使用哪种序列化方式你都必须为项目提供一个 Redis 实例化方案因为我们需要和各个服务通过Redis来同步数据。
2.2.1 jdk 默认序列化方式
优点兼容性好缺点Session 序列化后基本不可读对开发者来讲等同于乱码。
!-- Sa-Token 整合 Redis 使用 jdk 默认序列化方式 --
dependencygroupIdcn.dev33/groupIdartifactIdsa-token-redis/artifactIdversion1.35.0.RC/version
/dependency
!-- 提供Redis连接池 --
dependencygroupIdorg.apache.commons/groupIdartifactIdcommons-pool2/artifactId
/dependency2.2.2 jackson 序列化方式
优点Session 序列化后可读性强可灵活手动修改缺点兼容性稍差。
!-- Sa-Token 整合 Redis 使用 jackson 序列化方式 --
dependencygroupIdcn.dev33/groupIdartifactIdsa-token-redis-jackson/artifactIdversion1.35.0.RC/version
/dependency
!-- 提供Redis连接池 --
dependencygroupIdorg.apache.commons/groupIdartifactIdcommons-pool2/artifactId
/dependency3 和jwt集成
3.1 引入依赖
!-- Sa-Token 整合 jwt --
dependencygroupIdcn.dev33/groupIdartifactIdsa-token-jwt/artifactIdversion1.35.0.RC/version
/dependency注意: sa-token-jwt 显式依赖 hutool-jwt 5.7.14 版本保险起见你的项目中要么不引入 hutool要么引入版本 5.7.14 的 hutool 版本。hutool 5.8.13 和 5.8.14 版本下会出现类型转换问题关联issue。 3.2 配置秘钥
在 application.yml 配置文件中配置 jwt 生成秘钥
sa-token:# jwt秘钥 jwt-secret-key: asdasdasifhueuiwyurfewbfjsdafjk3.3 注入jwt实现
根据不同的整合规则插件提供了三种不同的模式你需要选择其中一种注入到你的项目中
Configuration
public class SaTokenConfigure {// Sa-Token 整合 jwt (Simple 简单模式)Beanpublic StpLogic getStpLogicJwt() {return new StpLogicJwtForSimple();}// Sa-Token 整合 jwt (Mixin 混入模式)Beanpublic StpLogic getStpLogicJwt() {return new StpLogicJwtForMixin();}// Sa-Token 整合 jwt (Stateless 无状态模式)Beanpublic StpLogic getStpLogicJwt() {return new StpLogicJwtForStateless();}
}4 实现鉴权接口
关于数据的获取建议以下方案三选一
在网关处集成ORM框架直接从数据库查询数据。先从Redis中获取数据获取不到时走ORM框架查询数据库。先从Redis中获取缓存数据获取不到时走RPC调用子服务 (专门的权限数据提供服务) 获取。
放到登录服务中
/*** 自定义权限验证接口扩展*/
Component
public class StpInterfaceImpl implements StpInterface {Autowiredprivate MenuService menuService;Autowiredprivate RoleService roleService;Autowiredprivate ThreadPoolConfig threadPoolConfig;/*** 返回一个账号所拥有的权限码集合** param loginId* param loginType* return*/Overridepublic ListString getPermissionList(Object loginId, String loginType) {ListString res (ListString) StpUtil.getTokenSession().get(PERMISSION-LIST);if (res null) {CompletableFutureListString permissionFuture CompletableFuture.supplyAsync(() - {ListMenuVO menuVOList menuService.getPermissionList(Convert.toLong(loginId),null);return menuVOList.stream().map(MenuVO::getPermission).collect(Collectors.toList());}, threadPoolConfig.USER_ROLE_PERM_THREAD_POOL);try {return permissionFuture.get();} catch (InterruptedException | ExecutionException e) {throw new RuntimeException(e);}}return res;}/*** 返回一个账号所拥有的角色标识集合** param loginId* param loginType* return*/Overridepublic ListString getRoleList(Object loginId, String loginType) {ListString res (ListString) StpUtil.getTokenSession().get(ROLE-LIST);if (res null) {CompletableFutureListString roleFuture CompletableFuture.supplyAsync(() - {// 返回此 loginId 拥有的权限列表ListRoleVO roles roleService.getRoleByUserId(Convert.toLong(loginId));return roles.stream().map(RoleVO::getRoleKey).collect(Collectors.toList());}, threadPoolConfig.USER_ROLE_PERM_THREAD_POOL);try {return roleFuture.get();} catch (InterruptedException | ExecutionException e) {throw new RuntimeException(e);}}return res;}
}其他子服务
/*** 自定义权限验证接口扩展*/
Component
public class StpInterfaceImpl implements StpInterface {/*** 返回一个账号所拥有的权限码集合** param loginId* param loginType* return*/Overridepublic ListString getPermissionList(Object loginId, String loginType) {return (ListString) StpUtil.getTokenSession().get(PERMISSION-LIST);}/*** 返回一个账号所拥有的角色标识集合** param loginId* param loginType* return*/Overridepublic ListString getRoleList(Object loginId, String loginType) {return (ListString) StpUtil.getTokenSession().get(ROLE-LIST);}
}5 注册全局过滤器
然后在网关处注册全局过滤器进行鉴权操作
Configuration
public class SaTokenConfigure{/*** Sa-Token 整合 jwt (Simple 简单模式)** return*/Beanpublic StpLogic getStpLogicJwt() {return new StpLogicJwtForSimple();}/*** 注册 [Sa-Token全局过滤器]** return*/Beanpublic SaReactorFilter getSaReactorFilter() {return new SaReactorFilter()// 拦截地址 - 拦截全部path.addInclude(/**)// 开放地址.addExclude(/favicon.png)// 鉴权方法每次访问进入// 全局认证函数.setAuth(obj - {SaRouter// 拦截的所有接口.match(/**)// 忽略所有登陆相关接口.notMatch(/account/**)// 忽略所有个人信息相关接口.notMatch(/manage/profile/**)// 忽略获得文件下载链接相关接口.notMatch(/file/fileTransfer/getFileUrl/**)// 忽略获取站点信息列表相关接口.notMatch(/manage/config/get)// 忽略获取所有的公告相关接口.notMatch(/manage/notice/list)// 忽略所有接口文档相关接口.notMatch(/doc.html,/doc.html*,/doc.html/*,/webjars/**,/img.icons/**,/swagger-resources/**,/**/v2/api-docs)// 要执行的校验动作可以写完整的 lambda 表达式.check(r - StpUtil.checkLogin());})// 异常处理函数.setError(e - {return ResultResponse.fail().message(e.getMessage());})// 前置函数在每次认证函数之前执行.setBeforeAuth(obj - {// ---------- 设置跨域响应头 ----------SaHolder.getResponse()// 允许指定域访问跨域资源.setHeader(Access-Control-Allow-Origin, *)// 允许所有请求方式.setHeader(Access-Control-Allow-Methods, POST, GET, PUT, DELETE, OPTIONS)// 有效时间.setHeader(Access-Control-Max-Age, 3600)// 允许的header参数.setHeader(Access-Control-Allow-Headers, *);// 如果是预检请求则立即返回到前端SaRouter.match(SaHttpMethod.OPTIONS).free(r - System.out.println(--------OPTIONS预检请求不做处理--------)).back();});}
}6 使用Same-Token模块提供的身份校验能力完成服务间的权限认证
6.1 网关处添加Same-Token
为网关添加全局过滤器此过滤器会为Request请求头追加 Same-Token 参数这个参数会被转发到子服务。
/*** 全局过滤器为请求添加 Same-Token */
Component
public class ForwardAuthFilter implements GlobalFilter {Overridepublic MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest newRequest exchange.getRequest().mutate()// 为请求追加 Same-Token 参数 .header(SaSameUtil.SAME_TOKEN, SaSameUtil.getToken()).build();ServerWebExchange newExchange exchange.mutate().request(newRequest).build();return chain.filter(newExchange);}
}6.2 在子服务里校验参数
在子服务添加过滤器校验参数。
/*** Sa-Token 权限认证 配置类 */
Configuration
public class SaTokenConfigure implements WebMvcConfigurer {// 注册 Sa-Token 全局过滤器 Beanpublic SaServletFilter getSaServletFilter() {return new SaServletFilter().addInclude(/**).addExclude(/favicon.ico).setAuth(obj - {// 校验 Same-Token 身份凭证 —— 以下两句代码可简化为SaSameUtil.checkCurrentRequestToken(); String token SaHolder.getRequest().getHeader(SaSameUtil.SAME_TOKEN);SaSameUtil.checkToken(token);}).setError(e - {return SaResult.error(e.getMessage());});}
}启动网关与子服务访问测试 如果通过网关转发可以正常访问直接访问子服务会提示无效Same-Tokenxxx 6.3 服务间内部调用鉴权
有时候我们需要在一个服务调用另一个服务的接口这也是需要添加Same-Token作为身份凭证的
在服务里添加 Same-Token 流程与网关类似我们以RPC框架 Feign 为例
6.3.1 首先在调用方添加 FeignInterceptor
/*** feign拦截器, 在feign请求发出之前加入一些操作 */
Component
public class FeignInterceptor implements RequestInterceptor {// 为 Feign 的 RCP调用 添加请求头Same-Token Overridepublic void apply(RequestTemplate requestTemplate) {requestTemplate.header(SaSameUtil.SAME_TOKEN, SaSameUtil.getToken());// 如果希望被调用方有会话状态此处就还需要将 satoken 添加到请求头中// requestTemplate.header(StpUtil.getTokenName(), StpUtil.getTokenValue());}
}6.3.2 在调用接口里使用此Interceptor
/*** 在调用接口里使用 Interceptor*/
FeignClient(name netdisk-account, configuration FeignInterceptor.class)
public interface AccountFeignService {/*** 返回一个账号所拥有的权限码集合** return*/RequestMapping(/menu/getPermissionList/)ResultResponseListMenuVO getPermissionList();/*** 根据用户ID获取详细信息** param userId* return*/RequestMapping(/role/getRoleByUserId/{userId})ResultResponseListRoleVO getRoleByUserId(PathVariable Long userId);
}