Server/go/apptoken/apptoken.go (186 lines of code) (raw):
package apptoken
import (
"apptoken/utils"
"bytes"
"encoding/base64"
"encoding/binary"
"errors"
"io"
"time"
)
const VENSION_LENGTH = 3
const BUFFER_CAPACITY_BASE = 256
const VERSION_0 = "000"
type AppToken struct {
AppId string
AppKey string
IssueTimestamp int32
Salt int32
Timestamp int32
Service *Service
Options *AppTokenOptions
Signature []byte
}
func nextMultiple(n, base int) int {
if base <= 0 || n <= 0 {
return 0
}
result := base
for result < n {
result *= 2
}
return result
}
func CreateAppToken(appId string, appKey string, timestamp int32) AppToken {
return AppToken{
AppId: appId,
AppKey: appKey,
Salt: utils.RandomInt32(),
IssueTimestamp: int32(time.Now().Unix()),
Timestamp: timestamp,
}
}
func (token *AppToken) SetService(service *Service) {
token.Service = service
}
func (token *AppToken) SetOptions(options *AppTokenOptions) {
token.Options = options
}
func (token *AppToken) buildSignBody() ([]byte, error) {
buf := new(bytes.Buffer)
// appId
appId := []byte(token.AppId)
if err := binary.Write(buf, binary.BigEndian, int32(len(appId))); err != nil {
return nil, errors.New("illegal AppId")
}
if _, err := buf.Write(appId); err != nil {
return nil, errors.New("illegal AppId")
}
// issueTimestamp
if err := binary.Write(buf, binary.BigEndian, token.IssueTimestamp); err != nil {
return nil, errors.New("illegal IssueTimestamp")
}
// salt
if err := binary.Write(buf, binary.BigEndian, token.Salt); err != nil {
return nil, errors.New("illegal Salt")
}
// timestamp
if err := binary.Write(buf, binary.BigEndian, token.Timestamp); err != nil {
return nil, errors.New("illegal Timestamp")
}
// service
service, err := token.Service.Pack()
if err != nil {
return nil, errors.New("illegal Service")
}
if err := binary.Write(buf, binary.BigEndian, service); err != nil {
return nil, errors.New("illegal Service")
}
// options
if token.Options == nil {
token.Options = &AppTokenOptions{
EngineOptions: make(map[string]string),
}
}
options, err := token.Options.Pack()
if err != nil {
return nil, errors.New("illegal AppTokenOptions")
}
if err := binary.Write(buf, binary.BigEndian, options); err != nil {
return nil, errors.New("illegal AppTokenOptions")
}
//FIXME
len := nextMultiple(buf.Len(), BUFFER_CAPACITY_BASE)
result := make([]byte, len)
copy(result, buf.Bytes())
return result, nil
}
func (token *AppToken) Build() (string, error) {
if token.AppKey == "" {
return "", errors.New("illegal AppKey")
}
if token.Service == nil {
return "", errors.New("illegal Service")
}
token.Service.Validate()
generatedSign, err := utils.GenerateSign(token.AppKey, token.IssueTimestamp, token.Salt)
if err != nil {
return "", errors.New("generate sign failed")
}
buf, err := token.buildSignBody()
if buf == nil || err != nil {
return "", errors.New("build sign body failed")
}
// sign
sign, err := utils.Sign(generatedSign, buf)
if err != nil {
return "", errors.New("sign failed")
}
tokenBuf := new(bytes.Buffer)
// signLength
if err := binary.Write(tokenBuf, binary.BigEndian, int32(len(sign))); err != nil {
return "", errors.New("illegal sign")
}
// signBody
if err := binary.Write(tokenBuf, binary.BigEndian, sign); err != nil {
return "", errors.New("illegal sign")
}
// buf
if err := binary.Write(tokenBuf, binary.BigEndian, buf); err != nil {
return "", errors.New("illegal buf")
}
tokenCompress, err := utils.Compress(tokenBuf.Bytes())
if err != nil {
return "", errors.New("token compress failed")
}
return VERSION_0 + base64.StdEncoding.EncodeToString(tokenCompress), nil
}
func Parse(tokenStr string) (AppToken, error) {
appToken := AppToken{}
if len(tokenStr) <= VENSION_LENGTH || tokenStr[0:VENSION_LENGTH] != VERSION_0 {
return appToken, errors.New("illegal appToken length")
}
tokenOri := tokenStr[VENSION_LENGTH:]
token, err := base64.StdEncoding.DecodeString(tokenOri)
if err != nil {
return appToken, errors.New("base64.decode appToken failed")
}
tokenDecompress, err := utils.Decompress(token)
if err != nil {
return appToken, errors.New("token decompress failed")
}
// sign
tokenBuf := bytes.NewReader(tokenDecompress)
// signLegth
var signLegth int32
if err := binary.Read(tokenBuf, binary.BigEndian, &signLegth); err != nil {
return appToken, err
}
// signBody
signature := make([]byte, signLegth)
if _, err := io.ReadFull(tokenBuf, signature); err != nil {
return appToken, errors.New("parse sign failed")
}
appToken.Signature = signature
// appId
var appIdLength int32
if err := binary.Read(tokenBuf, binary.BigEndian, &appIdLength); err != nil {
return appToken, err
}
appId := make([]byte, appIdLength)
if _, err := io.ReadFull(tokenBuf, appId); err != nil {
return appToken, errors.New("parse appId failed")
}
appToken.AppId = string(appId)
// issueTimestamp
var issueTimestamp int32
if err := binary.Read(tokenBuf, binary.BigEndian, &issueTimestamp); err != nil {
return appToken, errors.New("parse issueTimestamp failed")
}
appToken.IssueTimestamp = issueTimestamp
// salt
var salt int32
if err := binary.Read(tokenBuf, binary.BigEndian, &salt); err != nil {
return appToken, errors.New("parse salt failed")
}
appToken.Salt = salt
// timestamp
var timestamp int32
if err := binary.Read(tokenBuf, binary.BigEndian, ×tamp); err != nil {
return appToken, errors.New("parse timestamp failed")
}
appToken.Timestamp = timestamp
// service
service, err := UnpackService(tokenBuf)
if err != nil {
return appToken, errors.New("parse service failed")
}
appToken.Service = service
// options
options, err := UnpackAppTokenOptions(tokenBuf)
if err != nil {
return appToken, errors.New("parse appTokenOptions failed")
}
appToken.Options = options
return appToken, nil
}