2024-10-23 22:42:13 +08:00
|
|
|
package serve
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/loveuer/nf"
|
|
|
|
"github.com/loveuer/nf/nft/log"
|
|
|
|
"net/http"
|
2024-10-24 18:01:44 +08:00
|
|
|
"time"
|
|
|
|
"uauth/internal/opt"
|
2024-10-25 18:18:11 +08:00
|
|
|
"uauth/internal/serve/handler"
|
2024-10-24 18:01:44 +08:00
|
|
|
"uauth/internal/store/cache"
|
2024-10-23 22:42:13 +08:00
|
|
|
"uauth/internal/tool"
|
2024-10-24 18:01:44 +08:00
|
|
|
"uauth/model"
|
2024-10-23 22:42:13 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
func authenticateUser(username, password string) (bool, error) {
|
|
|
|
// 这里你应该实现真实的用户认证逻辑
|
|
|
|
// 为了简化,我们这里直接硬编码一个用户名和密码
|
|
|
|
if username == "user" && password == "pass" {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return false, fmt.Errorf("invalid username or password")
|
|
|
|
}
|
|
|
|
|
|
|
|
// 处理登录请求
|
|
|
|
func handleLogin(c *nf.Ctx) error {
|
|
|
|
username := c.FormValue("username")
|
|
|
|
password := c.FormValue("password")
|
|
|
|
|
|
|
|
// 认证用户
|
|
|
|
ok, err := authenticateUser(username, password)
|
|
|
|
if err != nil || !ok {
|
|
|
|
return c.Status(http.StatusUnauthorized).SendString("Unauthorized")
|
|
|
|
}
|
|
|
|
|
|
|
|
// 用户认证成功,重定向到授权页面
|
|
|
|
http.Redirect(c.Writer, c.Request, "/authorize?client_id=12345&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fcallback&scope=read%20write", http.StatusFound)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// 处理用户的授权批准
|
|
|
|
func handleApprove(c *nf.Ctx) error {
|
|
|
|
// 获取表单数据
|
|
|
|
clientID := c.FormValue("client_id")
|
|
|
|
redirectURI := c.FormValue("redirect_uri")
|
|
|
|
scope := c.FormValue("scope")
|
|
|
|
|
|
|
|
// 生成授权码
|
|
|
|
authorizationCode := uuid.New().String()[:8]
|
|
|
|
|
|
|
|
log.Info("[D] client_id = %s, scope = %s, auth_code = %s", clientID, scope, authorizationCode)
|
|
|
|
|
|
|
|
// 重定向到回调 URL 并附带授权码
|
|
|
|
http.Redirect(c.Writer, c.Request, redirectURI+"?code="+authorizationCode, http.StatusFound)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// 令牌请求的处理
|
|
|
|
func handleToken(c *nf.Ctx) error {
|
2024-10-24 18:01:44 +08:00
|
|
|
var (
|
|
|
|
err error
|
|
|
|
grantType string
|
|
|
|
code string
|
|
|
|
redirectURI string
|
|
|
|
|
|
|
|
// 记录 user
|
|
|
|
accessToken string
|
|
|
|
refreshToken string
|
|
|
|
user = new(model.User)
|
|
|
|
)
|
|
|
|
|
2024-10-23 22:42:13 +08:00
|
|
|
// 获取请求参数
|
2024-10-24 18:01:44 +08:00
|
|
|
grantType = c.FormValue("grant_type")
|
|
|
|
code = cache.Prefix + c.FormValue("code")
|
|
|
|
redirectURI = c.FormValue("redirect_uri")
|
2024-10-23 22:42:13 +08:00
|
|
|
|
|
|
|
// 简单验证
|
|
|
|
if grantType != "authorization_code" {
|
|
|
|
return c.Status(http.StatusBadRequest).SendString("Unsupported grant type")
|
|
|
|
}
|
|
|
|
|
2024-10-24 18:01:44 +08:00
|
|
|
if err = cache.Client.GetScan(tool.Timeout(3), code).Scan(&user); err != nil {
|
2024-10-23 22:42:13 +08:00
|
|
|
return c.Status(http.StatusBadRequest).SendString("Invalid authorization code")
|
|
|
|
}
|
2024-10-24 18:01:44 +08:00
|
|
|
defer func() {
|
|
|
|
// 清除已使用的授权码
|
|
|
|
cache.Client.Del(tool.Timeout(3), code)
|
|
|
|
_ = redirectURI
|
|
|
|
}()
|
2024-10-23 22:42:13 +08:00
|
|
|
|
|
|
|
// 生成访问令牌
|
2024-10-24 18:01:44 +08:00
|
|
|
if accessToken, refreshToken, err = generateAccessToken(user); err != nil {
|
|
|
|
return c.Status(http.StatusInternalServerError).SendString(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
c.Writer.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
2024-10-23 22:42:13 +08:00
|
|
|
|
|
|
|
// 返回访问令牌
|
|
|
|
return c.JSON(map[string]string{
|
2024-10-24 18:01:44 +08:00
|
|
|
"access_token": accessToken,
|
|
|
|
"refresh_token": refreshToken,
|
|
|
|
"token_type": "bearer",
|
|
|
|
"expires_in": "3600", // 访问令牌有效期(秒)
|
2024-10-23 22:42:13 +08:00
|
|
|
})
|
2024-10-24 18:01:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func generateAccessToken(user *model.User) (accessToken string, refreshToken string, err error) {
|
|
|
|
if accessToken, err = user.JwtEncode(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
refreshToken = uuid.New().String()[:8]
|
|
|
|
|
|
|
|
if err = cache.Client.SetEx(tool.Timeout(3), cache.Prefix+"refresh_token", refreshToken, 24*time.Hour); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = cache.Client.SetEx(tool.Timeout(3), cache.Prefix+"access_token", accessToken, time.Hour); err != nil {
|
|
|
|
return
|
|
|
|
}
|
2024-10-23 22:42:13 +08:00
|
|
|
|
2024-10-24 18:01:44 +08:00
|
|
|
return
|
2024-10-23 22:42:13 +08:00
|
|
|
}
|
|
|
|
|
2024-10-24 18:01:44 +08:00
|
|
|
func Run(ctx context.Context) error {
|
2024-10-23 22:42:13 +08:00
|
|
|
|
|
|
|
app := nf.New()
|
|
|
|
|
2024-10-24 18:01:44 +08:00
|
|
|
api := app.Group(opt.Cfg.Svc.Prefix)
|
2024-10-23 22:42:13 +08:00
|
|
|
// 设置路由
|
2024-10-27 21:47:57 +08:00
|
|
|
api.Post("/client/registry", handler.ClientRegistry)
|
2024-10-25 18:18:11 +08:00
|
|
|
api.Get("/login", handler.LoginPage)
|
|
|
|
api.Post("/login", handler.LoginAction)
|
|
|
|
api.Get("/authorize", handler.Authorize)
|
2024-10-23 22:42:13 +08:00
|
|
|
api.Post("/approve", handleApprove)
|
|
|
|
api.Post("/token", handleToken)
|
|
|
|
|
|
|
|
// 启动 HTTP 服务器
|
2024-10-24 18:01:44 +08:00
|
|
|
log.Info("Starting server on: %s", opt.Cfg.Svc.Address)
|
2024-10-23 22:42:13 +08:00
|
|
|
go func() {
|
|
|
|
<-ctx.Done()
|
|
|
|
_ = app.Shutdown(tool.Timeout(2))
|
|
|
|
}()
|
|
|
|
|
2024-10-24 18:01:44 +08:00
|
|
|
return app.Run(opt.Cfg.Svc.Address)
|
2024-10-23 22:42:13 +08:00
|
|
|
}
|