应用客户端在加入 IM 群聊,登录以收发实时消息进行即时通讯时,都需要传入 Token 参数,完成鉴权。此 Token 来自你的应用服务端,由应用服务端在加入房间时,根据 IM AppID、IM AppKey、 UserID、时间戳等参数实时生成。

IM 服务端在收到群聊操作请求时,会校验请求中携带的 Token 信息,如果校验不成功,进房会失败。

鉴权全流程

鉴权全流程如下。

  1. 客户端根据需要,向应用服务端申请 Token。

  2. 应用服务端生成 Token。

  3. 应用服务端将 Token 下发到客户端。

  4. 客户端使用获取到的 Token 进行群聊操作。

  5. IM 服务端验证 Token。

  6. 应用客户端收到来自 IM SDK 的回调,获取操作结果(成功/失败)。

alt

生成 Token

你可以参考 IM 提供的示例代码,在应用服务端实现 Token 的生成。

以 Golang 为例,参考以下代码:

package main

import (
   "fmt"
   "time"
)

func main() {
   var appID int32 = 666666    // 火山控制台分配的appID
   var userID int64 = 88888888 // 用户的UserID
   var tokenExpireTime int64 = 
      time.Now().UnixNano()/int64(time.Millisecond) + 1000*60*30 // 30分钟
   appKey := "Your_appKey" 
   token, err := GetToken(appID, userID, tokenExpireTime, appKey)
   if err != nil {
      fmt.Printf("get token error , err:%+v", err)
      return
   }

   fmt.Printf("token:%s", token)
}
go
package main

import (
   "bytes"
   "crypto/aes"
   "crypto/cipher"
   "encoding/base64"
   "encoding/binary"
   "hash/crc32"
   "math"
   
    "github.com/pkg/errors"
)

const (
   standardBase  = 256
   targetBase      = 62
)

var alphabet = []byte{
   '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
}

// Token token信息
type Token struct {
   ID        int32 
   Type      int16 
   AppID     int32 
   UserID    int64 
   Timestamp int64 
}

func GetToken(appID int32, userID, tokenExpireTime int64, appKey string) (string, error) {
   token := &Token{
      ID:        appID,
      Type:      1,
      AppID:     appID,
      UserID:    userID,
      Timestamp: tokenExpireTime,
   }

   encrypted := make([]byte, 24)
   binary.BigEndian.PutUint16(encrypted[:2], uint16(1))
   binary.BigEndian.PutUint16(encrypted[2:4], uint16(token.Type))
   binary.BigEndian.PutUint32(encrypted[4:8], uint32(token.AppID))
   binary.BigEndian.PutUint64(encrypted[8:16], uint64(token.UserID))
   binary.BigEndian.PutUint64(encrypted[16:24], uint64(token.Timestamp))

   decodeKey, err := base64.StdEncoding.DecodeString(appKey)
   if err != nil {
      return "", errors.Wrapf(err, "[GetToken] decode appkey error , appkey:%s", appKey)
   }
   c, err := aes.NewCipher(decodeKey)
   if err != nil {
      return "", errors.Wrapf(err, "[GetToken] new cipher error , appkey:%s,decodekey:%s", appKey, decodeKey)
   }
   iv := decodeKey[:16]
   encrypted = pkcs5Padding(encrypted, c.BlockSize())
   cipher.NewCBCEncrypter(c, iv).CryptBlocks(encrypted, encrypted)
   crc := crc32.ChecksumIEEE(encrypted)
   output := make([]byte, 8+len(encrypted))
   binary.BigEndian.PutUint32(output[:4], crc)
   binary.BigEndian.PutUint32(output[4:8], uint32(token.ID))
   copy(output[8:], encrypted)
   return string(Encode(output)), nil
}

func pkcs5Padding(ciphertext []byte, blockSize int) []byte {
   padding := blockSize - len(ciphertext)%blockSize
   padtext := bytes.Repeat([]byte{byte(padding)}, padding)
   return append(ciphertext, padtext...)
}

func Encode(input []byte) []byte {
   output := convert(input, standardBase, targetBase)
   translate(output, alphabet)
   return output
}

func convert(input []byte, sourceBase, targetBase int) []byte {
   estimatedLength := estimateOutputLength(len(input), sourceBase, targetBase)
   output := make([]byte, 0, estimatedLength)
   zeroCount := 0
   for _, b := range input {
      if b == 0 {
         zeroCount++
      } else {
         break
      }
   }

   for len(input) > 0 {
      quotient := make([]byte, 0, len(input))
      remainder := 0
      for _, b := range input {
         accumulator := int(b&0xFF) + remainder*sourceBase
         digit := (accumulator - (accumulator % targetBase)) / targetBase
         remainder = accumulator % targetBase
         if len(quotient) > 0 || digit > 0 {
            quotient = append(quotient, byte(digit))
         }
      }
      output = append(output, byte(remainder))
      input = quotient
   }

   if zeroCount != 0 {
      for i := 0; i < zeroCount; i++ {
         output = append(output, 0)
      }
   }

   for i, j := 0, len(output)-1; i < j; i, j = i+1, j-1 {
      output[i], output[j] = output[j], output[i]
   }
   return output

}

func estimateOutputLength(inputLength, sourceBase, targetBase int) int {
   return int(math.Ceil((math.Log(float64(sourceBase)) / math.Log(float64(targetBase))) * float64(inputLength)))
}

func translate(indices, dict []byte) {
   for i, b := range indices {
      indices[i] = dict[b]
   }
}
go