您的位置 首页 > 腾讯云社区

「玩转腾讯云」API网关+云函数SCF开启OAuth2.0认证实战---shwinpiocess的技术之路

如果在按照教程测试的时候遇到服务调用失败的情况,可以直接跳到第五步,查看以下避坑指北呦!

一、创建生成Json Web Token的云函数

按照https://cloud.tencent.com/document/product/628/38393文章的指导,在开启API网关的OAuth2.0认证之前必须新建OAuth认证服务器,下面我们用SCF云函数实现一个Golang版本的认证服务器

云函数代码如下

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地址做API调试时的结果用postman测试时结果有可能是这样的

如果你在测试的时候不幸遇到了以上两种情况,可以先对域名执行dig命令,看下IP地址是否是同一个,然后重新新建一个服务重试下!

---来自腾讯云社区的---shwinpiocess的技术之路

关于作者: 瞎采新闻

这里可以显示个人介绍!这里可以显示个人介绍!

热门文章

留言与评论(共有 0 条评论)
   
验证码: