facebook登录服务端校验,Facebook oidc,Meta oidc服务端校验

本文档介绍了如何使用Facebook的有限登录,并在服务端进行校验。详细阐述了验证流程,包括获取appId、公钥列表,解析并校验token。同时提供了Go语言的实现示例,展示了如何解析token、获取公钥以及校验用户信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

相对应的客户端文档可以先阅读这里:
https://developers.facebook.com/docs/facebook-login/limited-login/ios
如果你选择的是
https://developers.facebook.com/docs/facebook-login/ios?locale=zh_CN
这个登录,是没有接入服务端逻辑的,赶快停止code,请选择上面的有限登录。

接入后,客户端会获得facebook返回的一个token和userId,接下来就该服务端进行校验了。


一、验证流程

Facebook的服务端校验逻辑与apple几乎一样

  1. 获取appId(注册应用时拿到的);
  2. 获取公钥列表(可以直接缓存起来,然后等解析token的时候拿出来使用);
  3. 使用公钥解析token;
  4. 把userId 、 appId等信息与解析出的token参数做对比,校验完成。

服务端文档见:https://developers.facebook.com/docs/facebook-login/limited-login/token/validating

二、go实现

package third_party_jwt_util

import (
	"crypto/rsa"
	"encoding/base64"
	"encoding/json"
	"errors"
	"github.com/dgrijalva/jwt-go"
	log "github.com/sirupsen/logrus"
	"io/ioutil"
	"math/big"
	// 这里的三个包可以进行替换使用
	e "错误处理的包"
	"缓存的包"
	httpPool "http的包"
	"strings"
)

const (
	PublicKeyReqUrl     = "https://appleid.apple.com/auth/keys"
	MetaPublicKeyReqUrl = "https://www.facebook.com/.well-known/oauth/openid/jwks"
)

type JwtClaims struct {
	jwt.StandardClaims
}

type JwtHeader struct {
	Kid string `json:"kid"`
	Alg string `json:"alg"`
}

// VerifyIdentityToken /  认证客户端传递过来的token是否有效  ///
// AppleIssuer = "https://appleid.apple.com"
// MetaIssuer = "https://www.facebook.com"
// clientId = 申请到的appId
// token = 前端获取的token
// cliUserId = 前端传过来的userId
func VerifyIdentityToken(issuer string, clientId string, cliToken string, cliUserID string) error {
	// 数据由 头部、载荷、签名 三部分组成
	log.Debug(clientId, cliToken, cliUserID)
	cliTokenArr := strings.Split(cliToken, ".")
	if len(cliTokenArr) < 3 {
		return errors.New("cliToken Split err")
	}

	// 解析cliToken的header获取kid
	cliHeader, err := jwt.DecodeSegment(cliTokenArr[0])
	if err != nil {
		log.Error(err)
		return err
	}

	var jHeader JwtHeader
	err = json.Unmarshal(cliHeader, &jHeader)
	if err != nil {
		log.Error(err)
		return err
	}
	println(jHeader.Kid)
	// 效验pubKey 及 token
	token, err := jwt.ParseWithClaims(cliToken, &JwtClaims{}, func(token *jwt.Token) (interface{}, error) {
		return GetPublicKey(jHeader.Kid), nil
	})

	if err != nil {
		log.Error(err)
		return err
	}

	// 信息验证
	if claims, ok := token.Claims.(*JwtClaims); ok && token.Valid {
		log.Debug("Audience:" + claims.Audience)
		log.Debug("Issuer:" + claims.Issuer)
		log.Debug("Subject:" + claims.Subject)
		log.Debug(claims.ExpiresAt)
		if claims.Issuer != issuer || claims.Audience != clientId || claims.Subject != cliUserID {
			log.Error("校验不通过")
			return errors.New("verify token info fail, info is not match")
		}
		// here is verified ok !
	} else {
		return errors.New("token claims parse fail")
	}

	return nil
}

/  以下⬇️获取key  /

type JwtKeys struct {
	Kty string `json:"kty"`
	Kid string `json:"kid"`
	Use string `json:"use"`
	Alg string `json:"alg"`
	N   string `json:"n"`
	E   string `json:"e"`
}

func GetPublicKey(keyId string) *rsa.PublicKey {
	log.Debug("get public key,  key id:" + keyId)
	// 获取验证所需的公钥
	var pubKey rsa.PublicKey
	var keys JwtKeys
	if key, err := cache.BigCache.Get(keyId); err == nil {
		keys = key.(JwtKeys)
		nBin, _ := base64.RawURLEncoding.DecodeString(keys.N)
		nData := new(big.Int).SetBytes(nBin)

		eBin, _ := base64.RawURLEncoding.DecodeString(keys.E)
		eData := new(big.Int).SetBytes(eBin)

		pubKey.N = nData
		pubKey.E = int(eData.Uint64())
		return &pubKey
	} else {
		panic(e.NewApiError(e.WrongAppId))
	}
}
// 项目启动执行
func AppleKeyInit() {
	log.Info("Apple Key Init...")
	// 把key 按keyId+对象的形式 放在缓存里
	response, err := httpPool.Client.Get(PublicKeyReqUrl)

	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	data, err := ioutil.ReadAll(response.Body)
	var jKeys map[string][]JwtKeys
	err = json.Unmarshal(data, &jKeys)
	if err != nil {
		panic(err)
	}

	for _, keys := range jKeys {
		for _, key := range keys {
			cache.BigCache.Set(key.Kid, key)
		}
	}
}
// 项目启动执行
func MetaKeyInit() {
	log.Info("Meta Key Init...")
	// 把key 按keyId+对象的形式 放在缓存里
	response, err := httpPool.Client.Get(MetaPublicKeyReqUrl)

	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	data, err := ioutil.ReadAll(response.Body)
	var jKeys map[string][]JwtKeys
	err = json.Unmarshal(data, &jKeys)
	if err != nil {
		panic(err)
	}

	for _, keys := range jKeys {
		for _, key := range keys {
			println(key.Kid)
			cache.BigCache.Set(key.Kid, key)
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

了迹奇有没

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值