🎉 完成基本的演示和样例
This commit is contained in:
89
internal/client/client.go
Normal file
89
internal/client/client.go
Normal file
@ -0,0 +1,89 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"github.com/google/uuid"
|
||||
"github.com/loveuer/nf"
|
||||
"github.com/loveuer/nf/nft/log"
|
||||
"github.com/loveuer/nf/nft/resp"
|
||||
"golang.org/x/oauth2"
|
||||
"net/http"
|
||||
"uauth/internal/tool"
|
||||
)
|
||||
|
||||
//go:embed login.html
|
||||
var page string
|
||||
|
||||
var (
|
||||
config = oauth2.Config{
|
||||
ClientID: "test",
|
||||
ClientSecret: "Foobar123",
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "http://localhost:8080/oauth/v2/authorize",
|
||||
TokenURL: "http://localhost:8080/oauth/v2/token",
|
||||
},
|
||||
RedirectURL: "http://localhost:18080/oauth/v2/redirect",
|
||||
Scopes: []string{"test"},
|
||||
}
|
||||
state = uuid.New().String()[:8]
|
||||
)
|
||||
|
||||
func Run(ctx context.Context) error {
|
||||
app := nf.New()
|
||||
app.Get("/login", handleLogin)
|
||||
app.Get("/oauth/v2/redirect", handleRedirect)
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
_ = app.Shutdown(tool.Timeout(2))
|
||||
}()
|
||||
|
||||
return app.Run(":18080")
|
||||
}
|
||||
|
||||
func handleLogin(c *nf.Ctx) error {
|
||||
if c.Query("oauth") != "" {
|
||||
uri := config.AuthCodeURL(state)
|
||||
log.Info("[C] oauth config client_secret = %s", config.ClientSecret)
|
||||
log.Info("[C] redirect to oauth2 server uri = %s", uri)
|
||||
return c.Redirect(uri, http.StatusFound)
|
||||
}
|
||||
|
||||
return c.HTML(page)
|
||||
}
|
||||
|
||||
func handleRedirect(c *nf.Ctx) error {
|
||||
type Req struct {
|
||||
State string `query:"state"`
|
||||
Code string `query:"code"`
|
||||
Scope string `query:"scope"`
|
||||
ClientId string `query:"client_id"`
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
req = new(Req)
|
||||
token *oauth2.Token
|
||||
)
|
||||
|
||||
if err = c.QueryParser(req); err != nil {
|
||||
return resp.Resp400(c, err.Error())
|
||||
}
|
||||
|
||||
if req.State != state {
|
||||
log.Error("[C] state mismatch, want = %s, got = %s", state, req.State)
|
||||
return c.Status(http.StatusBadRequest).SendString("Bad Request: state mismatch")
|
||||
}
|
||||
|
||||
if token, err = config.Exchange(c.Context(), req.Code); err != nil {
|
||||
log.Error("[C] oauth config exchange err: %s", err.Error())
|
||||
return resp.Resp500(c, err.Error())
|
||||
}
|
||||
|
||||
bs, _ := json.Marshal(token)
|
||||
log.Info("[C] oauth finally token =\n%s", string(bs))
|
||||
|
||||
return resp.Resp200(c, token)
|
||||
}
|
52
internal/client/login.html
Normal file
52
internal/client/login.html
Normal file
@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
|
||||
>
|
||||
<title>Client Login</title>
|
||||
<style>
|
||||
body {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h3>这里是 xx 产品登录页面</h3>
|
||||
<form>
|
||||
<fieldset>
|
||||
<label>
|
||||
Username
|
||||
<input
|
||||
name="username"
|
||||
placeholder="username"
|
||||
autocomplete="given-name"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Password
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder="password"
|
||||
autocomplete="password"
|
||||
/>
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<input
|
||||
type="submit"
|
||||
value="登录"
|
||||
/>
|
||||
<a href="/login?oauth=true">使用 OAuth V2 账号登录</a>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user