-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
10dd1fc
commit e9018fd
Showing
4 changed files
with
200 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,45 @@ | ||
# appleTools | ||
苹果登陆相关工具 | ||
|
||
开发APP苹果登陆功能时参考各种渠道的代码然后顺手封装一下。 | ||
|
||
主要参考:https://github.com/tptpp/sign-in-with-apple | ||
|
||
在此基础上增加了解密苹果最终返回数据的id_token,用于验证客户端post过来的userIdentifier | ||
|
||
其中id_token解密后有个sub字段,该字段一般和userIdentifier一致 | ||
|
||
通过对比这两个字段是否相等来处理后续登陆业务 | ||
|
||
比如如下使用方式 | ||
|
||
```go | ||
// @Summary 苹果登陆 | ||
// @Produce application/json | ||
// @Param data body request.AppleLoginCode true "苹果登陆" | ||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"登陆成功"}" | ||
// @Router /appLogin/AppleLoginCode [post] | ||
func AppleLogin(c *gin.Context) { | ||
//一个请求信息的结构体,接收authorizationCode和userIdentifier | ||
var A request.AppleLoginCodeAndId | ||
_ = c.ShouldBindJSON(&A) | ||
|
||
//生成client_secret,参数:苹果账户的KeyId,TeamId, ClientID, KeySecret | ||
clientSecret := appleTools.GetAppleSecret(KeyId,TeamId, ClientID, KeySecret) | ||
|
||
//获取用户信息,参数:ClientID, 上面的clientSecret, authorizationCode | ||
data, err := appleTools.GetAppleLoginData(ClientID, clientSecret, A.Code) | ||
|
||
//检查用户信息,参数:上面的data,客户端传过来的userIdentifier,上面的err | ||
check := appleTools.CheckAppleID(data, A.ID, err) | ||
if check == false { | ||
response.FailWithMessage("APPLE登陆失败[error 1],请重试或使用其他方式登陆", c) | ||
return | ||
} | ||
|
||
fmt.Println("苹果校验结果OK") | ||
|
||
//你的登陆业务 | ||
|
||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package appleTools | ||
|
||
import ( | ||
"crypto/ecdsa" | ||
"crypto/x509" | ||
"encoding/json" | ||
"encoding/pem" | ||
"github.com/dgrijalva/jwt-go" | ||
"github.com/pkg/errors" | ||
"io/ioutil" | ||
"net/http" | ||
"net/url" | ||
"strings" | ||
"time" | ||
) | ||
|
||
//请求苹果用户信息 | ||
func GetAppleLoginData(clientId string, clientSecret string, code string) ([]byte, error) { | ||
params := map[string]string{ | ||
"client_id": clientId, | ||
"client_secret": clientSecret, | ||
"code": code, | ||
"grant_type": "authorization_code", | ||
"redirect_uri": "", | ||
} | ||
form := url.Values{} | ||
for k, v := range params { | ||
form.Set(k, v) | ||
} | ||
|
||
var request *http.Request | ||
var err error | ||
if request, err = http.NewRequest("POST", "https://appleid.apple.com/auth/token", | ||
strings.NewReader(form.Encode())); err != nil { | ||
return nil, err | ||
} | ||
|
||
request.Header.Set("Content-Type", "application/x-www-form-urlencoded") | ||
|
||
var response *http.Response | ||
if response, err = http.DefaultClient.Do(request); nil != err { | ||
return nil, err | ||
} | ||
defer response.Body.Close() | ||
|
||
data, err := ioutil.ReadAll(response.Body) | ||
|
||
return data, err | ||
} | ||
|
||
//生成client_secret | ||
func GetAppleSecret(keyId string, teamId string, clientId string, keySecret string) string { | ||
token := &jwt.Token{ | ||
Header: map[string]interface{}{ | ||
"alg": "ES256", | ||
"kid": keyId, | ||
}, | ||
Claims: jwt.MapClaims{ | ||
"iss": teamId, | ||
"iat": time.Now().Unix(), | ||
// constraint: exp - iat <= 180 days | ||
"exp": time.Now().Add(24 * time.Hour).Unix(), | ||
"aud": "https://appleid.apple.com", | ||
"sub": clientId, | ||
}, | ||
Method: jwt.SigningMethodES256, | ||
} | ||
|
||
ecdsaKey, _ := AuthKeyFromBytes([]byte(keySecret)) | ||
ss, _ := token.SignedString(ecdsaKey) | ||
return ss | ||
} | ||
|
||
//JWT加密 | ||
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 | ||
} | ||
|
||
//解密苹果返回的data中的id_token中的用户id和客户端post的id否一致 | ||
func CheckAppleID(data []byte, id string, err error) bool { | ||
//fmt.Println("苹果服务器返回信息为:", string(data)) | ||
if err != nil || strings.Contains(string(data), "error") { | ||
//fmt.Println("APPLE登陆失败[error 1],请重试或使用其他方式登陆") | ||
return false | ||
} | ||
//fmt.Println("苹果服务器返回信息OK") | ||
Au := AppleAuth{} | ||
err = json.Unmarshal(data, &Au) | ||
if err != nil { | ||
//fmt.Println("APPLE登陆失败[error 2],请重试或使用其他方式登陆") | ||
return false | ||
} | ||
//fmt.Println("结构体赋值OK", Au) | ||
var userDecode []byte | ||
parts := strings.Split(Au.IdToken, ".") | ||
userDecode, err = jwt.DecodeSegment(parts[1]) | ||
if err != nil { | ||
//fmt.Println("APPLE登陆失败[error 3],请重试或使用其他方式登陆") | ||
return false | ||
} | ||
//fmt.Println("Au.IdToken解码OK", userDecode) | ||
It := AppleIdToken{} | ||
err = json.Unmarshal(userDecode, &It) | ||
if err != nil { | ||
//fmt.Println("APPLE登陆失败[error 4],请重试或使用其他方式登陆") | ||
return false | ||
} | ||
//fmt.Println("结构体赋值OK", It) | ||
if It.Sub != id { | ||
//fmt.Println("APPLE登陆失败[error 5],请重试或使用其他方式登陆") | ||
return false | ||
} | ||
return true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package appleTools | ||
|
||
type AppleIdToken struct { | ||
Iss string `json:"iss,omitempty"` | ||
Aud string `json:"aud,omitempty"` | ||
Exp int `json:"exp,omitempty"` | ||
Iat int `json:"iat,omitempty"` | ||
Sub string `json:"sub,omitempty"` | ||
AtHash string `json:"at_hash,omitempty"` | ||
Email string `json:"email,omitempty"` | ||
EmailVerified string `json:"email_verified,omitempty"` | ||
IsPrivateEmail string `json:"is_private_email,omitempty"` | ||
AuthTime int `json:"auth_time,omitempty"` | ||
NonceSupported bool `json:"nonce_supported,omitempty"` | ||
} | ||
|
||
type AppleAuth struct { | ||
AccessToken string `json:"access_token,omitempty"` | ||
TokenType string `json:"token_type,omitempty"` | ||
ExpiresIn int `json:"expires_in,omitempty"` | ||
RefreshToken string `json:"refresh_token,omitempty"` | ||
IdToken string `json:"id_token,omitempty"` | ||
} |