- 一、OAuth2
- 二、授权流程(grant_type)
- 1、授权码(authorization_code)
- 2、隐藏式(implicit)
- 3、密码式(password)
- 4、客户端凭证(client_credentials)
- 三、基于oauth2的简单授权流程
- 1、模拟第三方服务
- 2、模拟授权验证
一、OAuth2参考文档:https://github.com/go-oauth2/oauth2
参考博客:https://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html
二、授权流程(grant_type) 1、授权码(authorization_code)拉起三方,获取用户信息,验证,授权,获取用户信息,颁发token
- 用户登陆第三方应用授权, 先拿code(第三方授权码)
- 用code换token
- 用户登陆授权,不拿code,直接获取token
用户提供给客户端 用户名和密码- 验证客户端,验证通过,发放token
- 验证客户端,直接发放token
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// 测试定死了code、token,真实开发应该持久化
var (
AuthCode = "CODE_CODE"
AccessTokenCode = "AccessTokenCode"
)
func main() {
r := gin.Default()
// 前端获取 code
r.POST("/code", Code)
// 后端通过 code 获取令牌 token
r.POST("/access_token", AccessToken)
// 后端通过 token 获取用户信息
r.POST("/user_info", UserInfo)
r.Run(":9090")
}
type UserInfoParams struct {
Token string `json:"token"`
}
func UserInfo(c *gin.Context) {
params := &UserInfoParams{}
if err := c.BindJSON(params); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if params.Token != AccessTokenCode {
c.JSON(http.StatusNoContent, nil)
return
}
c.JSON(200, gin.H{"user_name": "zhangsan", "Age": 20})
}
type TokenParams struct {
Code string `json:"code"`
}
func AccessToken(c *gin.Context) {
params := &TokenParams{}
if err := c.BindJSON(params); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if params.Code != AuthCode {
c.JSON(200, gin.H{"access_token": ""})
return
}
c.JSON(200, gin.H{"access_token": AccessTokenCode})
}
type CodeParams struct {
ClientId string `json:"client_id"`
Code string `json:"code"`
Scope string `json:"scope"`
State string `json:"state"`
RedirectUri string `json:"redirect_uri"`
}
func Code(c *gin.Context) {
params := &CodeParams{}
if err := c.BindJSON(params); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
params.Code = AuthCode
c.JSON(200, gin.H{"msg": params})
}
2、模拟授权验证
package main
import (
"bytes"
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"github.com/gin-gonic/gin"
)
var (
postTokenUrl = "http://localhost:9090/access_token"
postUserInfoUrl = "http://localhost:9090/user_info"
)
func main() {
r := gin.Default()
// 获取 token
r.POST("/token", Token)
r.Run(":9091")
}
type TokenParams struct {
Code string `json:"code"` // 授权码 是前端拉起第三方页面获取到的code
// ==== 以下本次测试的用不到 ====
GrantType string `json:"grant_type"` // 授权流程
RedirectUri string `json:"redirect_uri"` // 重定向uri
}
func Token(c *gin.Context) {
params := &TokenParams{}
if err := c.BindJSON(params); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 1.请求第三方颁发 token
assToken := &AccToken{}
Post(assToken, postTokenUrl, "code", params.Code)
if assToken.AccessToken == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "get assecc_token error"})
return
}
// 2.获取用户信息
userInfo := &UserInfo{}
err := Post(userInfo, postUserInfoUrl, "token", assToken.AccessToken)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "user not found"})
return
}
// ------------- 这里做一些判断操作,比如验证用户是否存在,创建/更新/登陆...... ---------------
c.JSON(200, gin.H{
"name": userInfo.UserName,
"age": userInfo.Age,
})
}
type UserInfo struct {
UserName string `json:"user_name"`
Age int `json:"age"`
}
type AccToken struct {
AccessToken string `json:"access_token"`
}
// 请求工具类
func Post(params interface{}, url string, key string, val string) error {
client := &http.Client{}
data := map[string]interface{}{
key: val,
}
reqData, _ := json.Marshal(data)
req, _ := http.NewRequest("POST", url, bytes.NewReader(reqData))
resp, _ := client.Do(req)
respBody, _ := ioutil.ReadAll(resp.Body)
if len(respBody) == 0 {
return errors.New("body nil")
}
json.Unmarshal(respBody, params)
return nil
}



