AccessToken.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <?php
  2. namespace app\common\library\agora\token;
  3. class Message
  4. {
  5. public $salt;
  6. public $ts;
  7. public $privileges;
  8. public function __construct()
  9. {
  10. $this->salt = rand(0, 100000);
  11. $this->ts = time() + 24 * 3600;
  12. $this->privileges = array();
  13. }
  14. public function packContent()
  15. {
  16. $buffer = unpack("C*", pack("V", $this->salt));
  17. $buffer = array_merge($buffer, unpack("C*", pack("V", $this->ts)));
  18. $buffer = array_merge($buffer, unpack("C*", pack("v", sizeof($this->privileges))));
  19. foreach ($this->privileges as $key => $value) {
  20. $buffer = array_merge($buffer, unpack("C*", pack("v", $key)));
  21. $buffer = array_merge($buffer, unpack("C*", pack("V", $value)));
  22. }
  23. return $buffer;
  24. }
  25. public function unpackContent($msg)
  26. {
  27. $pos = 0;
  28. $salt = unpack("V", substr($msg, $pos, 4))[1];
  29. $pos += 4;
  30. $ts = unpack("V", substr($msg, $pos, 4))[1];
  31. $pos += 4;
  32. $size = unpack("v", substr($msg, $pos, 2))[1];
  33. $pos += 2;
  34. $privileges = array();
  35. for ($i = 0; $i < $size; $i++) {
  36. $key = unpack("v", substr($msg, $pos, 2));
  37. $pos += 2;
  38. $value = unpack("V", substr($msg, $pos, 4));
  39. $pos += 4;
  40. $privileges[$key[1]] = $value[1];
  41. }
  42. $this->salt = $salt;
  43. $this->ts = $ts;
  44. $this->privileges = $privileges;
  45. }
  46. }
  47. class AccessToken
  48. {
  49. const Privileges = array(
  50. "kJoinChannel" => 1,
  51. "kPublishAudioStream" => 2,
  52. "kPublishVideoStream" => 3,
  53. "kPublishDataStream" => 4,
  54. "kPublishAudioCdn" => 5,
  55. "kPublishVideoCdn" => 6,
  56. "kRequestPublishAudioStream" => 7,
  57. "kRequestPublishVideoStream" => 8,
  58. "kRequestPublishDataStream" => 9,
  59. "kInvitePublishAudioStream" => 10,
  60. "kInvitePublishVideoStream" => 11,
  61. "kInvitePublishDataStream" => 12,
  62. "kAdministrateChannel" => 101,
  63. "kRtmLogin" => 1000,
  64. );
  65. public $appID, $appCertificate, $channelName, $uid;
  66. public $message;
  67. function __construct()
  68. {
  69. $this->message = new Message();
  70. }
  71. function setUid($uid)
  72. {
  73. if ($uid === 0) {
  74. $this->uid = "";
  75. } else {
  76. $this->uid = $uid . '';
  77. }
  78. }
  79. function is_nonempty_string($name, $str)
  80. {
  81. if (is_string($str) && $str !== "") {
  82. return true;
  83. }
  84. echo $name . " check failed, should be a non-empty string";
  85. return false;
  86. }
  87. static function init($appID, $appCertificate, $channelName, $uid)
  88. {
  89. $accessToken = new AccessToken();
  90. if (!$accessToken->is_nonempty_string("appID", $appID) ||
  91. !$accessToken->is_nonempty_string("appCertificate", $appCertificate) ||
  92. !$accessToken->is_nonempty_string("channelName", $channelName)) {
  93. return null;
  94. }
  95. $accessToken->appID = $appID;
  96. $accessToken->appCertificate = $appCertificate;
  97. $accessToken->channelName = $channelName;
  98. $accessToken->setUid($uid);
  99. $accessToken->message = new Message();
  100. return $accessToken;
  101. }
  102. static function initWithToken($token, $appCertificate, $channel, $uid)
  103. {
  104. $accessToken = new AccessToken();
  105. if (!$accessToken->extract($token, $appCertificate, $channel, $uid)) {
  106. return null;
  107. }
  108. return $accessToken;
  109. }
  110. function addPrivilege($key, $expireTimestamp)
  111. {
  112. $this->message->privileges[$key] = $expireTimestamp;
  113. return $this;
  114. }
  115. function extract($token, $appCertificate, $channelName, $uid)
  116. {
  117. $ver_len = 3;
  118. $appid_len = 32;
  119. $version = substr($token, 0, $ver_len);
  120. if ($version !== "006") {
  121. echo 'invalid version ' . $version;
  122. return false;
  123. }
  124. if (!$this->is_nonempty_string("token", $token) ||
  125. !$this->is_nonempty_string("appCertificate", $appCertificate) ||
  126. !$this->is_nonempty_string("channelName", $channelName)) {
  127. return false;
  128. }
  129. $appid = substr($token, $ver_len, $appid_len);
  130. $content = (base64_decode(substr($token, $ver_len + $appid_len, strlen($token) - ($ver_len + $appid_len))));
  131. $pos = 0;
  132. $len = unpack("v", $content . substr($pos, 2))[1];
  133. $pos += 2;
  134. $sig = substr($content, $pos, $len);
  135. $pos += $len;
  136. $crc_channel = unpack("V", substr($content, $pos, 4))[1];
  137. $pos += 4;
  138. $crc_uid = unpack("V", substr($content, $pos, 4))[1];
  139. $pos += 4;
  140. $msgLen = unpack("v", substr($content, $pos, 2))[1];
  141. $pos += 2;
  142. $msg = substr($content, $pos, $msgLen);
  143. $this->appID = $appid;
  144. $message = new Message();
  145. $message->unpackContent($msg);
  146. $this->message = $message;
  147. //non reversable values
  148. $this->appCertificate = $appCertificate;
  149. $this->channelName = $channelName;
  150. $this->setUid($uid);
  151. return true;
  152. }
  153. function build()
  154. {
  155. $msg = $this->message->packContent();
  156. $val = array_merge(unpack("C*", $this->appID), unpack("C*", $this->channelName), unpack("C*", $this->uid), $msg);
  157. $sig = hash_hmac('sha256', implode(array_map("chr", $val)), $this->appCertificate, true);
  158. $crc_channel_name = crc32($this->channelName) & 0xffffffff;
  159. $crc_uid = crc32($this->uid) & 0xffffffff;
  160. $content = array_merge(unpack("C*", packString($sig)), unpack("C*", pack("V", $crc_channel_name)), unpack("C*", pack("V", $crc_uid)), unpack("C*", pack("v", count($msg))), $msg);
  161. $version = "006";
  162. $ret = $version . $this->appID . base64_encode(implode(array_map("chr", $content)));
  163. return $ret;
  164. }
  165. }
  166. function packString($value)
  167. {
  168. return pack("v", strlen($value)) . $value;
  169. }