wip: 继续 day2

This commit is contained in:
loveuer
2024-10-24 18:01:44 +08:00
parent f3861184b6
commit a17b9bb16b
14 changed files with 387 additions and 45 deletions

View File

@ -2,24 +2,25 @@ package cmd
import (
"github.com/spf13/cobra"
"uauth/internal/opt"
"uauth/internal/serve"
"uauth/internal/store/cache"
"uauth/internal/tool"
)
func initServe() *cobra.Command {
var (
address string
prefix string
)
svc := &cobra.Command{
Use: "svc",
RunE: func(cmd *cobra.Command, args []string) error {
return serve.Run(cmd.Context(), prefix, address)
tool.TablePrinter(opt.Cfg)
tool.Must(cache.Init(opt.Cfg.Svc.Cache))
return serve.Run(cmd.Context())
},
}
svc.Flags().StringVar(&address, "address", "localhost:8080", "listen address")
svc.Flags().StringVar(&prefix, "prefix", "/api/oauth/v2", "api prefix")
svc.Flags().StringVar(&opt.Cfg.Svc.Address, "address", "localhost:8080", "listen address")
svc.Flags().StringVar(&opt.Cfg.Svc.Prefix, "prefix", "/api/oauth/v2", "api prefix")
svc.Flags().StringVar(&opt.Cfg.Svc.Cache, "cache", "lru::", "cache uri")
return svc
}

View File

@ -1,7 +1,14 @@
package opt
type svc struct {
Address string `json:"address"`
Prefix string `json:"prefix"`
Cache string `json:"cache"`
}
type config struct {
Debug bool `json:"debug"`
Svc svc `json:"svc"`
}
var (

6
internal/opt/var.go Normal file
View File

@ -0,0 +1,6 @@
package opt
const (
// 记得替换这个
JwtTokenSecret = "2(v6UW3pBf1Miz^bY9u4rAUyv&dj8Kdz"
)

View File

@ -7,7 +7,11 @@ import (
"github.com/loveuer/nf"
"github.com/loveuer/nf/nft/log"
"net/http"
"time"
"uauth/internal/opt"
"uauth/internal/store/cache"
"uauth/internal/tool"
"uauth/model"
)
func authenticateUser(username, password string) (bool, error) {
@ -89,44 +93,76 @@ func handleApprove(c *nf.Ctx) error {
// 令牌请求的处理
func handleToken(c *nf.Ctx) error {
var (
err error
grantType string
code string
redirectURI string
// 记录 user
accessToken string
refreshToken string
user = new(model.User)
)
// 获取请求参数
grantType := c.FormValue("grant_type")
code := c.FormValue("code")
redirectURI := c.FormValue("redirect_uri")
grantType = c.FormValue("grant_type")
code = cache.Prefix + c.FormValue("code")
redirectURI = c.FormValue("redirect_uri")
// 简单验证
if grantType != "authorization_code" {
return c.Status(http.StatusBadRequest).SendString("Unsupported grant type")
}
mu.Lock()
defer mu.Unlock()
// 验证授权码是否有效
accessToken, ok := authCodes[code]
if !ok {
if err = cache.Client.GetScan(tool.Timeout(3), code).Scan(&user); err != nil {
return c.Status(http.StatusBadRequest).SendString("Invalid authorization code")
}
defer func() {
// 清除已使用的授权码
cache.Client.Del(tool.Timeout(3), code)
_ = redirectURI
}()
// 生成访问令牌
token := generateAccessToken()
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")
// 返回访问令牌
return c.JSON(map[string]string{
"access_token": token,
"token_type": "bearer",
"expires_in": "3600", // 访问令牌有效期(秒)
"access_token": accessToken,
"refresh_token": refreshToken,
"token_type": "bearer",
"expires_in": "3600", // 访问令牌有效期(秒)
})
// 清除已使用的授权码
delete(authCodes, code)
}
func Run(ctx context.Context, prefix string, address string) error {
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
}
return
}
func Run(ctx context.Context) error {
app := nf.New()
api := app.Group(prefix)
api := app.Group(opt.Cfg.Svc.Prefix)
// 设置路由
api.Get("/login", handleLogin)
api.Get("/authorize", handleAuthorize)
@ -134,11 +170,11 @@ func Run(ctx context.Context, prefix string, address string) error {
api.Post("/token", handleToken)
// 启动 HTTP 服务器
log.Info("Starting server on: %s", address)
log.Info("Starting server on: %s", opt.Cfg.Svc.Address)
go func() {
<-ctx.Done()
_ = app.Shutdown(tool.Timeout(2))
}()
return app.Run(address)
return app.Run(opt.Cfg.Svc.Address)
}

View File

@ -3,6 +3,7 @@ package cache
import (
"context"
"errors"
"github.com/go-redis/redis/v8"
"time"
"uauth/internal/interfaces"
)

View File

@ -5,6 +5,10 @@ import (
"uauth/internal/interfaces"
)
const (
Prefix = "sys:uauth:"
)
var (
Client interfaces.Cacher
)

View File

@ -3,19 +3,19 @@ package cache
import (
"fmt"
"gitea.com/taozitaozi/gredis"
"github.com/go-redis/redis/v8"
"net/url"
"strings"
"uauth/internal/opt"
"uauth/internal/tool"
)
func Init() error {
func Init(uri string) error {
var (
err error
)
strs := strings.Split(opt.Cfg.Cache.Uri, "::")
strs := strings.Split(uri, "::")
switch strs[0] {
case "memory":
@ -32,7 +32,7 @@ func Init() error {
)
if len(strs) != 2 {
return fmt.Errorf("cache.Init: invalid cache uri: %s", opt.Cfg.Cache.Uri)
return fmt.Errorf("cache.Init: invalid cache uri: %s", uri)
}
uri := strs[1]
@ -42,7 +42,7 @@ func Init() error {
}
if ins, err = url.Parse(uri); err != nil {
return fmt.Errorf("cache.Init: url parse cache uri: %s, err: %s", opt.Cfg.Cache.Uri, err.Error())
return fmt.Errorf("cache.Init: url parse cache uri: %s, err: %s", uri, err.Error())
}
addr := ins.Host

104
internal/tool/cert.go Normal file
View File

@ -0,0 +1,104 @@
package tool
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"net"
"time"
)
func GenerateTlsConfig() (serverTLSConf *tls.Config, clientTLSConf *tls.Config, err error) {
ca := &x509.Certificate{
SerialNumber: big.NewInt(2019),
Subject: pkix.Name{
Organization: []string{"Company, INC."},
Country: []string{"US"},
Province: []string{"California"},
Locality: []string{"San Francisco"},
StreetAddress: []string{"Golden Gate Bridge"},
PostalCode: []string{"94016"},
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(99, 0, 0),
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
}
// create our private and public key
caPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return nil, nil, err
}
// create the CA
caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey)
if err != nil {
return nil, nil, err
}
// pem encode
caPEM := new(bytes.Buffer)
pem.Encode(caPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: caBytes,
})
caPrivKeyPEM := new(bytes.Buffer)
pem.Encode(caPrivKeyPEM, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(caPrivKey),
})
// set up our server certificate
cert := &x509.Certificate{
SerialNumber: big.NewInt(2019),
Subject: pkix.Name{
Organization: []string{"Company, INC."},
Country: []string{"US"},
Province: []string{"California"},
Locality: []string{"San Francisco"},
StreetAddress: []string{"Golden Gate Bridge"},
PostalCode: []string{"94016"},
},
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(1, 0, 0),
SubjectKeyId: []byte{1, 2, 3, 4, 6},
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
}
certPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return nil, nil, err
}
certBytes, err := x509.CreateCertificate(rand.Reader, cert, ca, &certPrivKey.PublicKey, caPrivKey)
if err != nil {
return nil, nil, err
}
certPEM := new(bytes.Buffer)
pem.Encode(certPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
})
certPrivKeyPEM := new(bytes.Buffer)
pem.Encode(certPrivKeyPEM, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(certPrivKey),
})
serverCert, err := tls.X509KeyPair(certPEM.Bytes(), certPrivKeyPEM.Bytes())
if err != nil {
return nil, nil, err
}
serverTLSConf = &tls.Config{
Certificates: []tls.Certificate{serverCert},
}
certpool := x509.NewCertPool()
certpool.AppendCertsFromPEM(caPEM.Bytes())
clientTLSConf = &tls.Config{
RootCAs: certpool,
}
return
}