generated from example/golang-server-template
feature: 新增oauth2
This commit is contained in:
parent
9cd127e708
commit
7d6068a864
2
Makefile
2
Makefile
|
|
@ -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
95
cmd/api/auth.go
Normal 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[:])
|
||||||
|
}
|
||||||
|
|
@ -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
1
go.mod
|
|
@ -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
4
go.sum
|
|
@ -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=
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user