如果在按照教程测试的时候遇到服务调用失败的情况,可以直接跳到第五步,查看以下避坑指北呦!
一、创建生成Json Web Token的云函数
按照https://cloud.tencent.com/document/product/628/38393文章的指导,在开启API网关的OAuth2.0认证之前必须新建OAuth认证服务器,下面我们用SCF云函数实现一个Golang版本的认证服务器
云函数代码如下
代码语言:go复制package main
import (
"context"
"encoding/json"
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/google/uuid"
"github.com/lestrrat-go/jwx/jwk"
"github.com/tencentyun/scf-go-lib/cloudfunction"
"log"
"time"
)
// ssh-keygen -t rsa生成的私钥文件,可以根据需求替换成自己的私钥
var keyData = []byte("-----BEGIN RSA PRIVATE KEY-----nMIIEowIBAAKCAQEA4f5wg5l2hKsTeNem/V41fGnJm6gOdrj8ym3rFkEU/wT8RDtnnSgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7mCpz9Er5qLaMXJwZxzHzAahlfA0incqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBpHssPnpYGIn20ZZuNlX2BrClciHhCnPUIIZOQn/MmqTD31jSyjoQoV7MhhMTATKJx2XrHhR 1DcKJzQBSTAGnpYVaqpsARnap nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3bODIRe1AuTyHceAbewn8b462yEWKAnRdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy7wIDAQABAoIBAQCwia1k7 2oZ2d3nn6agCAbqIE1QXfCmh41ZqJHbOY3oRQG3X1wpcGH4Gk O zDVTV2JszdcOt7E5dAynMaomETAhRxB7hlIOnEN7WKm dGNrKRvV0wDU5ReFMRHg31/Lnu8c 5BvGjZX ky9nPOIhFFYJqwCRlopGSUIxmVj5rSgtzk3iWOQXr ah1bjEXvlxDOWkHN6YfpV5ThdEnKdBIPGEVqa63r9n2h qazKrtiRqJqGnOrHzOECYbRFYhexsNFz7YT02xdfSHn7gMnIvabDDP/Qp0PjE1jdouiMaFHYnLBbgvlnZW9yuVf/rpXTUq/njxIXMmvmEyyvSDnnFcFikB8pAoGBAPF77hK4m3/rdGT7X8a/gwvZ2R121aBcdPwEaUhvj/36dx596zvYnmEOjrWfZhF083/nYWE2kVquj2wjs otCLfifEEgXcVPTnEOPO9Zg3uNSL0nNQghjnFuD3iGLTUBCtM66oTe0jLSslHe8gLGEQqyMzHOzYxNqibxcOZIe8Qt0NAoGBAO UnI5 XWjWEgDmvyC3TrOSf/KCGjtu0TSv30ipv27bDLMrpvPmD/5lpptTFwcxvVhCsn2b chCjlghFSWFbBULBrfci2FtliClOVMYrlNBdUSJhf3aYSG2Doe6Bgt1n2CpNnn/iu37Y3NfemZBJA7hNl4dYe f uzM87cdQ214 jrAoGAXA0XxX8ll2 ToOLJsaNTnOvNB9h9Uc5qK5X5w 7G7O998BN2PC/MWp8H 2fVqpXgNENpNXttkRm1hk1dych86nEunfdPuqsX as44oCyJGFHVBnWpm33eWQw9YqANRI pCJzP08I5WK3osnPiwshd nhR54yjgfYhBFNI7B95PmEQkCgYBzFSz7h1 s34Ycr8SvxsOBWxymG5zaCsUbPsL0n4aCgLScCHb9J E86aVbbVFdglYa5Id7DPTL61ixhl7WZjujspeXZGSbmq0KcnckbnmDgqkLECiOJW2NHP/j0McAkDLL4tysF8TLDO8gvuvzNC WQ6drO2ThrypLVZQ ryneBIPmwKBgEZxhqa0gVvHQG/7Od69KWj4eJP28kq13RhKay8JOoN0vPmspXJo1HY3nCKuHRG AP579dncdUnOMvfXOtkdM4vk0 hWASBQzM9xzVcztCa koAugjVaLS9A n9uQoqEeVNTckxx0S2bYevRy7hGQmUJTyQm3j1zEUR5jpdbL83Fbqn-----END RSA PRIVATE KEY-----")
func init() {
parsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(keyData)
if err != nil {
log.Println(err)
}
publicKey, err := jwk.New(&parsedKey.PublicKey)
if err != nil {
log.Println(err)
}
jsonbuf, err := json.MarshalIndent(publicKey, "", " ")
if err != nil {
log.Println(err)
}
fmt.Println("公钥:n", string(jsonbuf))
}
type Event struct {
RequestContext struct {
ServiceID string `json:"serviceId"`
Path string `json:"path"`
HTTPMethod string `json:"httpMethod"`
RequestID string `json:"requestId"`
Identity struct {
SecretID string `json:"secretId"`
} `json:"identity"`
SourceIP string `json:"sourceIp"`
Stage string `json:"stage"`
} `json:"requestContext"`
Headers struct {
AcceptLanguage string `json:"Accept-Language"`
Accept string `json:"Accept"`
Host string `json:"Host"`
UserAgent string `json:"User-Agent"`
OAuthClientId string `json:"OAuth_Client_ID"`
OAuthClientSecret string `json:"OAuth_Client_Secret"`
} `json:"headers"`
Body string `json:"body"`
PathParameters struct {
Path string `json:"path"`
} `json:"pathParameters"`
QueryStringParameters struct {
Code string `json:"code"`
GrantType string `json:"grant_type"`
} `json:"queryStringParameters"`
HeaderParameters struct {
Refer string `json:"Refer"`
} `json:"headerParameters"`
StageVariables struct {
Stage string `json:"stage"`
} `json:"stageVariables"`
Path string `json:"path"`
QueryString struct {
Foo string `json:"foo"`
Bob string `json:"bob"`
} `json:"queryString"`
HTTPMethod string `json:"httpMethod"`
}
type Claims struct {
Username string `json:"username"`
jwt.StandardClaims
}
func integratedResponse(body map[string]interface{}) (map[string]interface{}, error) {
response := make(map[string]interface{})
response["isBase64Encoded"] = false
response["statusCode"] = 200
headers := make(map[string]string)
headers["Content-Type"] = "application/json"
response["headers"] = headers
js, err := json.Marshal(body)
if err != nil {
log.Println("3333", err)
}
response["body"] = fmt.Sprintf("%s", js)
return response, nil
}
func getToken(ctx context.Context, event Event) (map[string]interface{}, error) {
result := make(map[string]interface{})
if event.QueryStringParameters.Code != "cloud.tencent.com" || event.Headers.OAuthClientId != "cloud.tencent.com" || event.Headers.OAuthClientSecret != "cloud.tencent.com" {
result["error"] = "error param!"
result["error_code"] = 10021
return integratedResponse(result)
}
expirationTime := time.Now().Add(5 * time.Minute)
claims := &Claims{
Username: "admin",
StandardClaims: jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(),
},
}
parsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(keyData)
if err != nil {
log.Println("1111", err)
result["error"] = "secret key parse error!"
result["error_code"] = 10022
return integratedResponse(result)
}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
tokenString, err := token.SignedString(parsedKey)
if err != nil {
log.Println("2222", err)
result["error"] = "generate token error"
return integratedResponse(result)
}
result["refresh_token"], _ = uuid.NewUUID()
result["id_token"] = tokenString
result["token_type"] = "Bearer"
result["expires_in"] = 300
return integratedResponse(result)
}
func main() {
// Make the handler available for Remote Procedure Call by Cloud Function
cloudfunction.Start(getToken)
}
1、执行以下命令将如上golang代码编译
GOOS=linux GOARCH=amd64 go build -o main main.go |
---|
2、将编译成功后的代码执行以下命令打包
zip main.zip main |
---|
3、在上海地区新建函数,函数名称为getToken,运行环境选择Golang 1,创建方式选择空白函数,然后点击下一步按钮
4、在函数配置页面,提交方法选择本地上传zip包,函数代码,选择刚刚编译并打包的main.zip,然后点击完成按钮
二、创建OAuth认证服务器
1、打开API网关服务页面,在上海地区,点击新建按钮,在弹出框中填写服务名为AuthorizationService,前端
类型选择http和https,访问方式勾选内网VPC和公网,然后点击提交按钮
2、点击提交按钮创建成功后,点击服务ID进入服务详情页面
3、进入服务详情页面后,点击管理API标签页
4、进入管理API标签页后,点击新建按钮
5、点击新建按钮后,API名称填写为请求token,路径为/oauth/token,鉴权类型为免鉴权,参数配置新增4个参数
- 参数名code,参数位置选择Query
- 参数名grant_type,参数位置选择Query
- 参数名OAuth_Client_ID,取消必填勾选,默认值填写为cloud.tencent.com,这是因为在云函数代码中写死了
- 参数名OAuth_Client_Secret,取消必填勾选,默认值填写为cloud.tencent.com,这是因为在云函数代码中写死了
然后点击下一步
6、在后端配置页面,后端类型选择cloud function,云函数名称选择getToken,勾选是否启用响应集成,然后点击下一步
7、在响应结果配置页面,选择返回类型为JSON,然后点击完成
8、在点击完成后弹出的页面中点击前往发布服务
9、在点击前往发布服务后出现的页面点击发布按钮,在弹出框填写备注,然后点击提交按钮,生成token服务就发布成功了
10、本地测试生成token服务是否正常
三、创建授权API
1、在上海区域新建服务,服务名为OAuthService,前段类型为http和https,访问方式勾选内网VPC和公网,然后点击提交
2、点击提交完成后,进去OAuthServic的详情页面,点击管理API标签页,在管理API页面点击新建按钮
3、点击新建按钮后出现的前段配置页面中,鉴权模式选择OAuth 2.0,OAuth模式选择授权API,新增两个参数
- 参数名code,参数位置选择Query
- 参数名grant_type,参数位置选择Query
然后点击下一步
4、点击下一步在出现的后端配置页面中,认证服务器填写我们刚刚创建的AuthorizationService的公网域名,后端路径为/oauth/token,Token携带位置选择Header,公钥填写
{ "kty": "RSA", "e": "AQAB", "n": "4f5wg5l2hKsTeNem_V41fGnJm6gOdrj8ym3rFkEU_wT8RDtnSgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7mCpz9Er5qLaMXJwZxzHzAahlfA0icqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBpHssPnpYGIn20ZZuNlX2BrClciHhCPUIIZOQn_MmqTD31jSyjoQoV7MhhMTATKJx2XrHhR-1DcKJzQBSTAGnpYVaqpsARap-nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3bODIRe1AuTyHceAbewn8b462yEWKARdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy7w"} |
---|
这里的公钥是和笔者提供的Golang云函数的私钥相对应的,如果需要更换私钥,则公钥也要跟着替换。
参数名OAuth_Client_ID,参数值填写为cloud.tencent.com,这是因为在云函数代码中写死了
参数名OAuth_Client_Secret,参数值填写为cloud.tencent.com,这是因为在云函数代码中写死了,然后点击下一步
5、在点击下一步出现的响应结果配置页面中,返回类型选择JSON,然后点击完成
6、在点击完成后弹出的页面中点击前往发布服务,然后点击发布按钮,填写发布备注信息,点击提交
四、创建业务API
1、在OAuthService的管理API页面,点击新建按钮
2、在点击新建按钮出现的前端配置页面中,鉴权类型选择OAuth 2.0,OAuth模式选择业务API,关联授权API选择刚刚创建的名称为token的API,然后点击下一步
3、在点击下一步出现的后端配置页面中,后端类型选择mock,返回数据填写helloworld,然后点击完成按钮
4、在点击完成按钮出现的页面中选择前往发布服务选项,然后点击发布,填写备注信息后,点击提交按钮
5、测试
我们直接访问刚刚发布的helloworld业务API出现如下图所示
接下来我们先调用授权API获取一个访问token,然后把token放到请求Header中再访问helloworld业务API
可以看到把token放到请求Header后再次请求业务API,正常返回了helloworld
五、避坑指北
1、API网关 云函数SCF创建的认证服务,如果是在同地域的话,可能会遇到不通的情况
笔者在整理这片文章的时候本来是打算用北京地区测试的,结果在测试的时候遇到了一个大坑,后面又改用了上海区域。
这是笔者在北京测试的时候创建的3个服务的域名做dig以后的结果,如果两个服务dig出来的IP地址是相同的,那这两个服务之间调用的时候会有问题
如果你在测试的时候不幸遇到了以上两种情况,可以先对域名执行dig命令,看下IP地址是否是同一个,然后重新新建一个服务重试下!