在微服务架构中,如果忽略服务的安全性,任由接口暴露在网络中,一旦遭受攻击后果是不可想象的、

保护微服务键安全的常见方案有:1.JWT令牌(token) 2.双向SSL 3.OAuth 2.0 等

本文主要介绍使用Token的实现方式

源码地址:https://github.com/Mike-Zrw/TokenApiAuth

基本流程:

上图中有两个服务,服务A和服务B,我们模拟的是服务A来调用服务B的过程,也可以反过来让服务B来调用服务A。

整个流程简单来说只有两步

  • 获取token
  • 携带token请求数据

详细流程为

  1. 客户端请求服务B
  2. 客户端检测本地缓存是否有服务B的token缓存
  3. 客户端检测本地不存在对应的token缓存或者token缓存已超时,则调用接口重新获取token
  4. 客户端调用接口获取token,参数为:时间戳+请求身份标识10位+guid,用rsa非对称加密
  5. 服务B获取客户端获取token的请求,用ras密钥解密客户端的参数,验证请求是否超时以及标识是否有效
  6. 服务端验证客户端参数有效则生成token,返回给客户端,token为一个包含了用户名称,过期时间等字段的加密字符串
  7. 客户端接收到token将token存入本地缓存
  8. 客户端将加密的token放入HTTP header中像服务端请求获取数据
  9. 服务端验证客户端的HTTP header中的token信息是否有效,如果有效,则成功返回数据

获取token

服务端会提供一个产生token的接口供客户端来调用,而对于调用该接口的请求同样需要认证,否则岂不是所有人都可以随意调用该接口来生成token了。

我的思路是每个客户端会有一个权限标识,可以是一样的。然后将权限,时间戳和一个随机数组成一个字符串,然后将该字符串以非对称加密。加密后的字符就是调用接口的参数了

在token生成的服务端,会解密客户端传来的数据,并进行权限及时间的校验,验证通过就会生成一个token,该token包含了用户信息及过期时间等数据,然后用HA256加密返回给客户端

一个token包含的结构如下

  1. public class TokenClaims
  2. {
  3. /// <summary>
  4. /// token的发行者
  5. /// </summary>
  6. public string Iss { get; set; }
  7. /// <summary>
  8. /// 用户权限
  9. /// </summary>
  10. public string Role { get; set; }
  11. /// <summary>
  12. /// 用户名
  13. /// </summary>
  14. public string Usr { get; set; }
  15. /// <summary>
  16. /// 签发时间 秒,时间点
  17. /// </summary>
  18. public long Iat { get; set; }
  19. /// <summary>
  20. /// 到期时间 秒,时间点
  21. /// </summary>
  22. public long Exp { get; set; }
  23. /// <summary>
  24. /// 唯一标识
  25. /// </summary>
  26. public string SingleStr { get; set; }
  27. }

其中用户名是服务端生成的,服务端会将该用户名作为键,将该token存储到缓存中。 所以对于每一个请求都会生成一个唯一的用户名

  1. public static TokenResult MakeToken(string RequestParam, string PrimaryKey = null)
  2. {
  3. try
  4. {
  5. dynamic p = JsonConvert.DeserializeObject(RequestParam);
  6. string RequestAuth = p.RequestAuth;//请求人信息
  7. string DesAuth;//解密后的author
  8. if (PrimaryKey == null)
  9. DesAuth = RSAHelper.Decrypt(RequestAuth, Config_PrimaryKey);
  10. else
  11. DesAuth = RSAHelper.Decrypt(RequestAuth, PrimaryKey);
  12. #region 请求历史是否有重复
  13. if (MakeTokenParamHistory.Contains(DesAuth))
  14. {
  15. ToolFactory.LogHelper.Info("生成token身份验证失败:该请求的字符串与之前重复:" + DesAuth);
  16. return new TokenResult() { Success = false, Error_Message = "请求数据非法" };
  17. }
  18. MakeTokenParamHistory.Insert(0, DesAuth);
  19. if (MakeTokenParamHistory.Count > 1000)
  20. MakeTokenParamHistory.RemoveRange(1000, MakeTokenParamHistory.Count - 1000);
  21. #endregion
  22. string ReqAuthId = DesAuth.Substring(DesAuth.Length - 46, 10);//请求人身份标识
  23. long reqTimespan = long.Parse(DesAuth.Substring(0, DesAuth.Length - 46)); //客户端请求时间秒数
  24. if (!ValidTokenAuth(ReqAuthId))
  25. {
  26. ToolFactory.LogHelper.Info("生成token身份验证失败:DesAuth" + DesAuth);
  27. return new TokenResult() { Success = false, Error_Message = "身份验证失败" };
  28. }
  29. if ((TimeHelper.GetTimeSecond() - reqTimespan) > ReqToken_OverTime)
  30. {
  31. ToolFactory.LogHelper.Info("生成token请求时间超时:DesAuth" + DesAuth);
  32. return new TokenResult() { Success = false, Error_Message = "请求时间超时" };
  33. }
  34. string uname = TokenBuilder.CreateUserName(ReqAuthId);
  35. long TokenOverTime = Token_OverTime;
  36. if (AuthMapOverTime != null && AuthMapOverTime.ContainsKey(ReqAuthId))
  37. TokenOverTime = AuthMapOverTime[ReqAuthId];
  38. string tokenStr = TokenBuilder.MakeToken(uname, ReqAuthId, TokenOverTime);
  39. ToolFactory.LogHelper.Notice("生成token:" + tokenStr);
  40. ToolFactory.CacheHelper.SetCache("ServiceTokenCacheKey_" + uname, tokenStr, TimeSpan.FromSeconds(TokenOverTime + 30)); //多存30秒,用于判断token的错误类型
  41. return new TokenResult() { Success = true, Token = tokenStr }; ;
  42. }
  43. catch (Exception ex)
  44. {
  45. ToolFactory.LogHelper.Error("生成token出现异常", ex);
  46. return new TokenResult() { Success = false, Error_Message = "错误的请求:" + ex.Message };
  47. }
  48. }

请求数据

对于携带token的请求,我将token放在http的header中,尽量减少验证对于业务代码的侵入性。

服务端将token取出,并或得token中存储的用户名,然后将服务端缓存的数据取出来判断该token是否有效

  1. /// <summary>
  2. /// 验证客户端发来的token是否有效
  3. /// </summary>
  4. /// <param name="header"></param>
  5. /// <returns></returns>
  6. public static ValidTokenResult ValidClientToken(HttpRequestHeaders header)
  7. {
  8. if (header.Authorization == null || header.Authorization.Parameter == null)
  9. {
  10. return new ValidTokenResult() { Success = false, Message = "not exit token" };
  11. }
  12. string tokenStr = header.Authorization.Parameter;
  13. //ToolFactory.LogHelper.Notice("接收到带token的请求:" + tokenStr);
  14. TokenClaims tcParam = TokenBuilder.DecodeToken(tokenStr);
  15. TokenClaims tcCache = TokenBuilder.DecodeToken(ToolFactory.CacheHelper.GetCache<string>("ServiceTokenCacheKey_" + tcParam.Usr));
  16. if (tcCache != null)
  17. {
  18. if (TokenIsTimeLoss(tcCache.Exp))
  19. {
  20. ToolFactory.LogHelper.Info("token过时,token:" + tokenStr);
  21. return new ValidTokenResult() { Success = false, Message = "token过时" };
  22. }
  23. else if (tcCache.SingleStr != tcParam.SingleStr)
  24. {
  25. ToolFactory.LogHelper.Info("token不正确,token:" + tokenStr);
  26. return new ValidTokenResult() { Success = false, Message = "token不正确" };
  27. }
  28. else
  29. {
  30. return new ValidTokenResult() { Success = true };
  31. }
  32. }
  33. else
  34. {
  35. ToolFactory.LogHelper.Info("ValidClientToken未授权的用户,token:" + tokenStr);
  36. return new ValidTokenResult() { Success = false, Message = "未授权的用户" };
  37. }
  38. }

整个验证框架的主要流程大概就是这样,当然还有很多细节,比如缓存的刷新,请求超时配置等等,有兴趣的可以到github下载具体代码~~~

用Token令牌维护微服务之间的通信安全的实现的更多相关文章

  1. JHipster技术栈定制 - 基于UAA的微服务之间安全调用

    本文通过代码实例演示如何通过UAA实现微服务之间的安全调用. uaa: 身份认证服务,同时也作为被调用的资源服务.服务端口9999. microservice1: 调用uaa的消费者服务,服务端口80 ...

  2. spring cloud实战与思考(二) 微服务之间通过fiegn上传一组文件(上)

    需求场景: 微服务之间调用接口一次性上传多个文件. 上传文件的同时附带其他参数. 多个文件能有效的区分开,以便进行不同处理. Spring cloud的微服务之间接口调用使用Feign.原装的Feig ...

  3. spring cloud实战与思考(三) 微服务之间通过fiegn上传一组文件(下)

    需求场景: 用户调用微服务1的接口上传一组图片和对应的描述信息.微服务1处理后,再将这组图片上传给微服务2进行处理.各个微服务能区分开不同的图片进行不同处理. 上一篇博客已经讨论了在微服务之间传递一组 ...

  4. SOA和微服务之间的区别

    近几年,我们有很多文章对SOA和微服务之间的不同点和相似点进行了分析.有些人认为SOA有很多地方是值得微服务学习的,而有些人则认为区别对待微服务和SOA会更好.而Neal Ford认为,将单体迁移到面 ...

  5. SpringCloud实战 | 第五篇:SpringCloud整合OpenFeign实现微服务之间的调用

    一. 前言 微服务实战系列是基于开源微服务项目 有来商城youlai-mall 版本升级为背景来开展的,本篇则是讲述SpringCloud整合OpenFeign实现微服务之间的相互调用,有兴趣的朋友可 ...

  6. 微服务之间如何共享DTO?

    1. 概述 近些年来,微服务变得越来越流行.微服务基本特征是模块化.独立.易于扩展的.它们之间需要协同工作并交换数据.为了实现这一点,我们创建了名为 DTO 的共享数据传输对象.在本文中,我们将介绍在 ...

  7. Restful、SOAP、RPC、SOA、微服务之间的区别

    什么是Restful Restful是一种架构设计风格,提供了设计原则和约束条件,而不是架构,而满足这些约束条件和原则的应用程序或设计就是 Restful架构或服务. 主要的设计原则: 资源与URI ...

  8. 服务注册中心之ZooKeeper系列(二) 实现一个简单微服务之间调用的例子

    上一篇文章简单介绍了ZooKeeper,讲了分布式中,每个微服务都会部署到多台服务器上,那服务之间的调用是怎么样的呢?如图: 1.集群A中的服务调用者如何发现集群B中的服务提供者呢? 2.集群A中的服 ...

  9. SpringBoot+SpringCloud实现登录用户信息在微服务之间的传递

    实现思路: 1:准备一个ThreadLocal变量,供线程之间共享. 2:每个微服务对所有过来的Feign调用进行过滤,然后从请求头中获取User用户信息,并存在ThreadLocal变量中. 3:每 ...

随机推荐

  1. mysql+mybatis递归调用

    递归调用的应用场景常常出现在多级嵌套的情况,比如树形的菜单.下面通过一个简单的例子来实现mysql+mybatis的递归. 数据模型 private Integer categoryId; priva ...

  2. objective-c 开发最简单的UITableView时数据进不去的问题

    今天在使用UITableView时遇到的问题,我在plist文件配置的数据进不去列表,以下是解决方案 问题原因:plist文件root的type配置错误 如上图所示,博主是使用plist文件作为我的沙 ...

  3. 《认知与设计:理解UI设计准则》【PDF】下载

    <认知与设计:理解UI设计准则>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382276 内容介绍 <图灵交互设计丛书·认 ...

  4. 58、js扩展

    作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理. 一.js的作用域 任何程序设计语言都有作用域的概念,简单的说,作用 ...

  5. JMeter循环控制器循环次数使用变量控制注意事项

    1.进入循环控制器之前变量要有值: 2.BeanShell处理文件,读取行数,赋值给变量,要有相应的Sampler,不然脚本不会运行. 对于单个线程来说,假如设置了循环2次,线程启动后,运行结束,此时 ...

  6. JavaWeb之数据源连接池(3)---Tomcat

    此文续 <JavaWeb之数据源连接池(2)---C3P0>. Apache Tomcat作为一款JavaWeb服务器,内置了DBCP数据源连接池.在使用中,只要进行相应配置即可. 首先, ...

  7. 开发过程遇到的bug

    1.transition和display的冲突: 解决方法:1)使用visibility:2)使用js的setTimeout使动画延迟:3)可以改变高度: 2.form表单提交和js中的提交冲突 解决 ...

  8. Golang 网络爬虫框架gocolly/colly 二 jQuery selector

    Golang 网络爬虫框架gocolly/colly 二 jQuery selector colly框架依赖goquery库,goquery将jQuery的语法和特性引入到了go语言中.如果要灵活自如 ...

  9. centos7 安装solr

    1 下载solr安装包 下载6.4.1版本 2.创建 存放数据的文件夹 solr_data 和 安装目录 solr_installation 3.进入solr的bin目录执行 /install_sol ...

  10. 微信小程序开发模板消息的时候 出现 errcode: 41028, errmsg: "invalid form id hint:

    小程序开发模板消息的时候  出现 errcode: 41028, errmsg: "invalid form id hint: 我是使用的微信支付发送模板消息,提示的formid无效的 大家 ...