feature: 新增oauth2

This commit is contained in:
梁真铭 2025-01-02 15:13:41 +08:00
parent 9cd127e708
commit 7d6068a864
5 changed files with 105 additions and 1 deletions

View File

@ -15,7 +15,7 @@ confirm:
## run/api: run the cmd/api application ## run/api: run the cmd/api application
.PHONY: run/api .PHONY: run/api
run/api: run/api:
go run ./cmd/api go run ./cmd/api -port=4002
# ==================================================================================== # # ==================================================================================== #
# QUALITY CONTROL # QUALITY CONTROL

95
cmd/api/auth.go Normal file
View File

@ -0,0 +1,95 @@
package main
import (
"context"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"net/http"
"time"
"golang.org/x/oauth2"
)
const (
h5Host = "http://localhost:5173"
clientID = "client_id"
clientSecret = "client_secret"
clientHost = "http://localhost:4002"
clientState = "client_state"
authHost = "http://localhost:4001"
code_challenge = "code_challenge"
)
var (
oauth2Config = oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
Scopes: []string{"all"},
RedirectURL: clientHost + "/v1/oauth2",
Endpoint: oauth2.Endpoint{
AuthURL: authHost + "/v1/oauth2/authorize",
TokenURL: authHost + "/v1/oauth2/token",
},
}
)
// 登录 重定向到认证服务器
func (app *application) loginHandler(w http.ResponseWriter, r *http.Request) {
u := oauth2Config.AuthCodeURL(clientState, oauth2.SetAuthURLParam("code_challenge", genCodeChallengeS256(code_challenge)), oauth2.SetAuthURLParam("code_challenge_method", "S256"))
http.Redirect(w, r, u, http.StatusFound)
}
// 授权回调
func (app *application) oauth2Handler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
state := r.Form.Get("state")
if state != clientState {
app.serverErrorResponse(w, r, errors.New("state is not valid"))
return
}
code := r.Form.Get("code")
if code == "" {
app.serverErrorResponse(w, r, errors.New("code is not found"))
return
}
token, err := oauth2Config.Exchange(r.Context(), code, oauth2.SetAuthURLParam("code_verifier", code_challenge))
if err != nil {
app.serverErrorResponse(w, r, err)
return
}
// TODO 检查资源服务器上是否有该用户信息, 没有则通过authHost/v1/oauth2/get-user-info获取用户信息并保存到资源服务器
http.Redirect(w, r, fmt.Sprintf("%s/authorize?access_token=%s&refresh_token=%s&expiry=%d", h5Host, token.AccessToken, token.RefreshToken, token.Expiry.Unix()), http.StatusFound)
}
// 刷新token
// POST /v1/refresh-token
func (app *application) refreshTokenHandler(w http.ResponseWriter, r *http.Request) {
var input struct {
RefreshToken string `json:"refresh_token"`
}
err := app.readJSON(w, r, &input)
if err != nil {
app.serverErrorResponse(w, r, err)
return
}
token, err := oauth2Config.TokenSource(context.Background(), &oauth2.Token{
RefreshToken: input.RefreshToken,
Expiry: time.Now(),
}).Token()
if err != nil {
app.serverErrorResponse(w, r, err)
return
}
err = app.writeJSON(w, http.StatusOK, envelope{"token": token}, nil)
if err != nil {
app.serverErrorResponse(w, r, err)
return
}
}
func genCodeChallengeS256(s string) string {
s256 := sha256.Sum256([]byte(s))
return base64.URLEncoding.EncodeToString(s256[:])
}

View File

@ -13,6 +13,10 @@ func (app *application) routes() http.Handler {
router.NotFound = http.HandlerFunc(app.notFoundResponse) router.NotFound = http.HandlerFunc(app.notFoundResponse)
router.MethodNotAllowed = http.HandlerFunc(app.methodNotAllowResponse) router.MethodNotAllowed = http.HandlerFunc(app.methodNotAllowResponse)
router.HandlerFunc(http.MethodGet, "/v1/healthcheck", app.healthcheckHandler) router.HandlerFunc(http.MethodGet, "/v1/healthcheck", app.healthcheckHandler)
// 授权
router.HandlerFunc(http.MethodGet, "/v1/login", app.loginHandler)
router.HandlerFunc(http.MethodGet, "/v1/oauth2", app.oauth2Handler)
router.HandlerFunc(http.MethodPost, "/v1/refresh-token", app.refreshTokenHandler)
router.Handler(http.MethodGet, "/debug/vars", expvar.Handler()) router.Handler(http.MethodGet, "/debug/vars", expvar.Handler())
return app.metrics( return app.metrics(
app.recoverPanic( app.recoverPanic(

1
go.mod
View File

@ -7,5 +7,6 @@ require (
github.com/julienschmidt/httprouter v1.3.0 github.com/julienschmidt/httprouter v1.3.0
github.com/lib/pq v1.10.2 github.com/lib/pq v1.10.2
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
golang.org/x/oauth2 v0.24.0
golang.org/x/time v0.5.0 golang.org/x/time v0.5.0
) )

4
go.sum
View File

@ -1,10 +1,14 @@
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=