wip: 继续 day2
This commit is contained in:
@ -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
|
||||
}
|
||||
|
@ -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
6
internal/opt/var.go
Normal file
@ -0,0 +1,6 @@
|
||||
package opt
|
||||
|
||||
const (
|
||||
// 记得替换这个
|
||||
JwtTokenSecret = "2(v6UW3pBf1Miz^bY9u4rAUyv&dj8Kdz"
|
||||
)
|
@ -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)
|
||||
}
|
||||
|
1
internal/store/cache/cache_redis.go
vendored
1
internal/store/cache/cache_redis.go
vendored
@ -3,6 +3,7 @@ package cache
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"time"
|
||||
"uauth/internal/interfaces"
|
||||
)
|
||||
|
4
internal/store/cache/client.go
vendored
4
internal/store/cache/client.go
vendored
@ -5,6 +5,10 @@ import (
|
||||
"uauth/internal/interfaces"
|
||||
)
|
||||
|
||||
const (
|
||||
Prefix = "sys:uauth:"
|
||||
)
|
||||
|
||||
var (
|
||||
Client interfaces.Cacher
|
||||
)
|
||||
|
10
internal/store/cache/init.go
vendored
10
internal/store/cache/init.go
vendored
@ -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
104
internal/tool/cert.go
Normal 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
|
||||
}
|
Reference in New Issue
Block a user