AccessToken.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Net;
  4. using Newtonsoft.Json;
  5. using Common;
  6. using System.Threading.Tasks;
  7. namespace ZcPeng.weixin.PublicAccount
  8. {
  9. /// <summary>
  10. /// 微信许可令牌
  11. /// </summary>
  12. public class AccessToken
  13. {
  14. /// <summary>
  15. /// 保存已获取到的许可令牌,键为公众号,值为公众号最后一次获取到的令牌
  16. /// </summary>
  17. private static ConcurrentDictionary<string, Tuple<AccessToken, DateTime>> accessTokens = new ConcurrentDictionary<string, Tuple<AccessToken, DateTime>>();
  18. /// <summary>
  19. /// 获取access token的地址
  20. /// </summary>
  21. private const string urlForGettingAccessToken = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";
  22. /// <summary>
  23. /// 获取access token的http方法
  24. /// </summary>
  25. private const string httpMethodForGettingAccessToken = WebRequestMethods.Http.Get;
  26. /// <summary>
  27. /// 保存access token的最长时间(单位:秒),超过时间之后,需要重新获取
  28. /// </summary>
  29. private const int accessTokenSavingSeconds = 7000;
  30. /// <summary>
  31. /// access token
  32. /// </summary>
  33. public string access_token { get; set; }
  34. /// <summary>
  35. /// 有效时间,单位:秒
  36. /// </summary>
  37. public int expires_in { get; set; }
  38. /// <summary>
  39. /// 构造函数
  40. /// </summary>
  41. /// <param name="_access_token">access token</param>
  42. /// <param name="_expires_in">有效时间</param>
  43. internal AccessToken(string _access_token, int _expires_in)
  44. {
  45. access_token = _access_token;
  46. expires_in = _expires_in;
  47. }
  48. /// <summary>
  49. /// 返回AccessToken字符串
  50. /// </summary>
  51. /// <returns></returns>
  52. public override string ToString()
  53. {
  54. return string.Format("AccessToken:{0}\r\n有效时间:{1}秒", access_token, expires_in);
  55. }
  56. /// <summary>
  57. /// 从JSON字符串解析AccessToken
  58. /// </summary>
  59. /// <param name="json">JSON字符串</param>
  60. /// <returns>返回AccessToken</returns>
  61. internal static AccessToken ParseFromJson(string json)
  62. {
  63. var at = JsonConvert.DeserializeAnonymousType(json, new { access_token = "", expires_in = 1 });
  64. return new AccessToken(at.access_token, at.expires_in);
  65. }
  66. /// <summary>
  67. /// 尝试从JSON字符串解析AccessToken
  68. /// </summary>
  69. /// <param name="json">JSON字符串</param>
  70. /// <param name="msg">如果解析成功,返回AccessToken;否则,返回null。</param>
  71. /// <returns>返回是否解析成功</returns>
  72. internal static bool TryParseFromJson(string json, out AccessToken token)
  73. {
  74. bool success = false;
  75. token = null;
  76. try
  77. {
  78. token = ParseFromJson(json);
  79. success = true;
  80. }
  81. catch { }
  82. return success;
  83. }
  84. /// <summary>
  85. /// 得到access token
  86. /// </summary>
  87. /// <param name="userName">公众号</param>
  88. /// <returns>返回access token</returns>
  89. public static AccessToken Get(string userName)
  90. {
  91. Tuple<AccessToken, DateTime> lastToken = accessTokens.ContainsKey(userName) ? accessTokens[userName] : null;
  92. AccessToken token = lastToken == null ? null : lastToken.Item1;
  93. DateTime refreshTime = lastToken == null ? DateTime.MinValue : lastToken.Item2;
  94. if ((DateTime.Now - refreshTime).TotalSeconds > accessTokenSavingSeconds || token == null)
  95. {
  96. //尝试从微信服务器获取2次
  97. ErrorMessage msg;
  98. AccessToken newToken = GetFromWeixinServer(userName, out msg);
  99. if (newToken == null)
  100. newToken = GetFromWeixinServer(userName, out msg);
  101. if (newToken != null)
  102. {
  103. lastToken = new Tuple<AccessToken, DateTime>(newToken, DateTime.Now);
  104. accessTokens[userName] = lastToken;
  105. token = newToken;
  106. Message msg1 = new Message(Guid.NewGuid(), MessageType.Request, token.ToString(),
  107. DateTime.Now, "GetToken", msg.errmsg);
  108. Message.Insert(msg1);
  109. }
  110. else
  111. {
  112. Message msg1 = new Message(Guid.NewGuid(), MessageType.Request, msg.errcode == 0 ? "" : msg.errcode.ToString(),
  113. DateTime.Now, "GetToken", msg.errcode+":"+msg.errmsg);
  114. Message.Insert(msg1);
  115. return null;
  116. }
  117. }
  118. return token;
  119. }
  120. /// <summary>
  121. /// 从微信服务器获取access token
  122. /// </summary>
  123. /// <param name="userName">公众号</param>
  124. /// <param name="msg">从服务器返回的错误信息。</param>
  125. /// <returns>返回许可令牌;如果获取失败,返回null。</returns>
  126. private static AccessToken GetFromWeixinServer(string userName, out ErrorMessage msg)
  127. {
  128. AccessToken token = null;
  129. msg = new ErrorMessage(ErrorMessage.ExceptionCode, "");
  130. AccountInfo account = AccountInfoCollection.GetAccountInfo(userName);
  131. if (account == null)
  132. {
  133. msg.errmsg = "获取公众号参数失败。";
  134. return token;
  135. }
  136. string url = string.Format(urlForGettingAccessToken, account.AppId, account.AppSecret);
  137. string result = HttpHelper.HttpPost(url, httpMethodForGettingAccessToken, "application/text").Result;
  138. if(result =="")
  139. {
  140. msg.errmsg = "从微信服务器获取响应失败。";
  141. return token;
  142. }
  143. if (ErrorMessage.IsErrorMessage(result))
  144. msg = ErrorMessage.Parse(result);
  145. else
  146. {
  147. try
  148. {
  149. token = AccessToken.ParseFromJson(result);
  150. }
  151. catch (Exception e)
  152. {
  153. msg = new ErrorMessage(e);
  154. }
  155. }
  156. return token;
  157. }
  158. }
  159. }