first commit
This commit is contained in:
166
pkg/partner/Apple/AppleClient.go
Normal file
166
pkg/partner/Apple/AppleClient.go
Normal file
@ -0,0 +1,166 @@
|
||||
package Apple
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"servicebase/pkg/common/HyTools"
|
||||
"time"
|
||||
|
||||
"github.com/anxpp/beego/logs"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const (
|
||||
AppleServerApiUrl = "https://appleid.apple.com/auth/token"
|
||||
)
|
||||
|
||||
// 苹果登录
|
||||
type AppleClient struct {
|
||||
}
|
||||
|
||||
func NewAppleClient() *AppleClient {
|
||||
return &AppleClient{}
|
||||
}
|
||||
|
||||
type Claims struct {
|
||||
jwt.StandardClaims
|
||||
Iss string `json:"iss"`
|
||||
Aud string `json:"aud"`
|
||||
Exp int64 `json:"exp"`
|
||||
Iat int64 `json:"iat"`
|
||||
Sub string `json:"sub"`
|
||||
AtHash string `json:"at_hash"`
|
||||
Email string `json:"email"`
|
||||
EmailVerified string `json:"email_verified"`
|
||||
AuthTime int64 `json:"auth_time"`
|
||||
NonceSupported bool `json:"nonce_supported"`
|
||||
}
|
||||
|
||||
type ResJwt struct {
|
||||
Token string `json:"access_token"`
|
||||
Type string `json:"token_type"`
|
||||
ExpiresIn int64 `json:"expires_in"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
IdToken string `json:"id_token"`
|
||||
}
|
||||
|
||||
// 验证code
|
||||
func (client *AppleClient) VerifyAppleAuthCode(authCode string) (appleUserId string, resultErr error) {
|
||||
|
||||
apiUrl := AppleServerApiUrl
|
||||
|
||||
clientId := viper.GetString("apple.IOSBundleId")
|
||||
|
||||
param := make(map[string]string, 0)
|
||||
param["client_id"] = clientId
|
||||
param["code"] = authCode
|
||||
param["grant_type"] = viper.GetString("apple.GrantType")
|
||||
|
||||
// 生成密钥
|
||||
secret := client.GenerateClientSecret()
|
||||
|
||||
if len(secret) == 0 {
|
||||
resultErr = errors.New("生成密钥出错")
|
||||
return
|
||||
}
|
||||
param["client_secret"] = secret
|
||||
|
||||
paramString := ""
|
||||
for k, v := range param {
|
||||
kv := k + "=" + v + "&"
|
||||
paramString += kv
|
||||
}
|
||||
|
||||
if len(paramString) > 0 {
|
||||
paramString = HyTools.Substr(paramString, 0, len(paramString)-1)
|
||||
}
|
||||
|
||||
logs.Info("signWithApple params:" + paramString)
|
||||
|
||||
headerMap := make(map[string]string, 0)
|
||||
headerMap["Content-Type"] = "application/x-www-form-urlencoded;charset=utf-8"
|
||||
|
||||
result, err := HyTools.HttpDo(http.MethodPost, apiUrl, headerMap, paramString)
|
||||
if err != nil {
|
||||
logs.Error("苹果登录失败,请求API失败:" + err.Error())
|
||||
resultErr = err
|
||||
return
|
||||
}
|
||||
logs.Info("signWithApple result:" + result)
|
||||
var resultDTO ResJwt
|
||||
e := json.Unmarshal([]byte(result), &resultDTO)
|
||||
if e != nil {
|
||||
logs.Error("苹果登录失败,JSON:" + e.Error())
|
||||
resultErr = err
|
||||
return
|
||||
}
|
||||
token, _ := jwt.ParseWithClaims(resultDTO.IdToken, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
// Don't forget to validate the alg is what you expect:
|
||||
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
||||
return nil, fmt.Errorf("Unexpected signing method: %v ", token.Header["alg"])
|
||||
}
|
||||
return token, nil
|
||||
})
|
||||
if claims, ok := token.Claims.(*Claims); ok {
|
||||
appleUserId = claims.Sub
|
||||
} else {
|
||||
resultErr = errors.New("jwt error, token not correct")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 生成jwt密钥
|
||||
func (client *AppleClient) GenerateClientSecret() (secret string) {
|
||||
|
||||
token := &jwt.Token{
|
||||
Header: map[string]interface{}{
|
||||
"alg": "ES256",
|
||||
"kid": viper.GetString("apple.KeyId"),
|
||||
},
|
||||
Claims: jwt.MapClaims{
|
||||
"iss": viper.GetString("apple.TeamId"),
|
||||
"iat": time.Now().Unix(),
|
||||
// constraint: exp - iat <= 180 days
|
||||
"exp": time.Now().Add(24 * time.Hour).Unix(),
|
||||
"aud": "https://appleid.apple.com",
|
||||
"sub": viper.GetString("apple.IOSBundleId"),
|
||||
},
|
||||
Method: jwt.SigningMethodES256,
|
||||
}
|
||||
|
||||
ecdsaKey, _ := AuthKeyFromBytes([]byte(viper.GetString("apple.PrivateKey")))
|
||||
ss, _ := token.SignedString(ecdsaKey)
|
||||
|
||||
secret = ss
|
||||
return
|
||||
}
|
||||
|
||||
func AuthKeyFromBytes(key []byte) (*ecdsa.PrivateKey, error) {
|
||||
var err error
|
||||
|
||||
// Parse PEM block
|
||||
var block *pem.Block
|
||||
if block, _ = pem.Decode(key); block == nil {
|
||||
return nil, errors.New("token: AuthKey must be a valid .p8 PEM file")
|
||||
}
|
||||
|
||||
// Parse the key
|
||||
var parsedKey interface{}
|
||||
if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var pkey *ecdsa.PrivateKey
|
||||
var ok bool
|
||||
if pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok {
|
||||
return nil, errors.New("token: AuthKey must be of type ecdsa.PrivateKey")
|
||||
}
|
||||
|
||||
return pkey, nil
|
||||
}
|
||||
32
pkg/partner/Apple/AppleClient_test.go
Normal file
32
pkg/partner/Apple/AppleClient_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
package Apple
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/anxpp/beego/logs"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAppleClient_VerifyAppleAuthCode(t *testing.T) {
|
||||
result := `{"access_token":"a43aca909b09c47e7ab17cf65f2f87d5a.0.mrwxs.NGHQpX4Dp1mHUeGiC7UF7A","token_type":"Bearer","expires_in":3600,"refresh_token":"r472107c8150844c0995604692008d3c5.0.mrwxs.WE3wQnwhQpVVX77-U6169A","id_token":"eyJraWQiOiI4NkQ4OEtmIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY24ubWVldGFsay5lbmVuIiwiZXhwIjoxNTk4MTA1NDE2LCJpYXQiOjE1OTgxMDQ4MTYsInN1YiI6IjAwMTY3Mi43NTQyNGFiMGRjYWM0YTM2YTkxMmI5NDNmMzg5MmQ4ZC4xMDUzIiwiYXRfaGFzaCI6IlUwVUM5QTdnRWc4cnFfOHVORWxocVEiLCJlbWFpbCI6IjgzNDA3NzA2NUBxcS5jb20iLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJhdXRoX3RpbWUiOjE1OTgxMDQ4MTMsIm5vbmNlX3N1cHBvcnRlZCI6dHJ1ZX0.G1GwHu5qWW1Hj8-E2BObM3BIwi0ntDeoX0ymHR2EufgXxWcBI7rePadO2vGqJIRFpzsoN7ixRT1-ChpvUT7X37izDqCMHRTsBgEEKqh15gvgB-siYfssBmojcJx4W_DcKx24-4Z_mi4_BejPU6_kstJrlOPzh33dOPiUZJcY0pAWpynaTPFR3WkdbLd7RB9N-s5Z9jY2VF2SQLA8kkTcdRZQsfagErGcawJK6zttkR8EwK3D0wo_cfXoJ9Q49fayzAwkyhqfMrAWZZVwzDDtdNq4dmXfnPxNG1pootRF4PZE1IkJKteN0262xp4XZEBuWsMBeUwwjHlpu4gr77Re9w"}`
|
||||
|
||||
var resultDTO ResJwt
|
||||
e := json.Unmarshal([]byte(result), &resultDTO)
|
||||
if e != nil {
|
||||
logs.Error("JSON:" + e.Error())
|
||||
return
|
||||
}
|
||||
secret := NewAppleClient().GenerateClientSecret()
|
||||
token, jwtError := jwt.ParseWithClaims(resultDTO.IdToken, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
// Don't forget to validate the alg is what you expect:
|
||||
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
||||
return nil, fmt.Errorf("Unexpected signing method: %v ", token.Header["alg"])
|
||||
}
|
||||
return []byte(secret), nil
|
||||
})
|
||||
logs.Info(jwtError)
|
||||
logs.Info(token.Valid)
|
||||
logs.Info(token.SigningString())
|
||||
logs.Info(token.Claims.(*Claims).Sub)
|
||||
}
|
||||
Reference in New Issue
Block a user