wip: 24/10/28
This commit is contained in:
parent
ef6cca510a
commit
d1597509bb
@ -7,6 +7,7 @@ import (
|
||||
"uauth/internal/store/cache"
|
||||
"uauth/internal/store/db"
|
||||
"uauth/internal/tool"
|
||||
"uauth/model"
|
||||
)
|
||||
|
||||
func initServe() *cobra.Command {
|
||||
@ -16,6 +17,7 @@ func initServe() *cobra.Command {
|
||||
tool.TablePrinter(opt.Cfg)
|
||||
tool.Must(cache.Init(opt.Cfg.Svc.Cache))
|
||||
tool.Must(db.Init(cmd.Context(), opt.Cfg.Svc.DB))
|
||||
tool.Must(model.Init(db.Default.Session()))
|
||||
return serve.Run(cmd.Context())
|
||||
},
|
||||
}
|
||||
|
98
internal/middleware/auth/auth.go
Normal file
98
internal/middleware/auth/auth.go
Normal file
@ -0,0 +1,98 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/loveuer/nf"
|
||||
"github.com/loveuer/nf/nft/resp"
|
||||
"time"
|
||||
"uauth/internal/store/cache"
|
||||
"uauth/internal/tool"
|
||||
"uauth/model"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
IgnoreFn func(c *nf.Ctx) bool
|
||||
TokenFn func(c *nf.Ctx) (string, bool)
|
||||
GetUserFn func(c *nf.Ctx, token string) (*model.User, error)
|
||||
}
|
||||
|
||||
var (
|
||||
defaultIgnoreFn = func(c *nf.Ctx) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
defaultTokenFn = func(c *nf.Ctx) (string, bool) {
|
||||
var token string
|
||||
|
||||
if token = c.Request.Header.Get("Authorization"); token != "" {
|
||||
return token, true
|
||||
}
|
||||
|
||||
if token = c.Query("access_token"); token != "" {
|
||||
return token, true
|
||||
}
|
||||
|
||||
if token = c.Cookies("access_token"); token != "" {
|
||||
return token, true
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
defaultGetUserFn = func(c *nf.Ctx, token string) (*model.User, error) {
|
||||
var (
|
||||
err error
|
||||
op = new(model.User)
|
||||
)
|
||||
|
||||
if err = cache.Client.GetExScan(tool.Timeout(3), cache.Prefix+"token:"+token, 24*time.Hour).Scan(op); err != nil {
|
||||
if errors.Is(err, cache.ErrorKeyNotFound) {
|
||||
return nil, resp.Resp401(c, nil, "用户认证信息不存在或已过期, 请重新登录")
|
||||
}
|
||||
|
||||
return nil, resp.Resp500(c, err)
|
||||
}
|
||||
|
||||
return op, nil
|
||||
}
|
||||
)
|
||||
|
||||
func New(cfgs ...*Config) nf.HandlerFunc {
|
||||
var cfg = &Config{}
|
||||
|
||||
if len(cfgs) > 0 && cfgs[0] != nil {
|
||||
cfg = cfgs[0]
|
||||
}
|
||||
|
||||
if cfg.IgnoreFn == nil {
|
||||
cfg.IgnoreFn = defaultIgnoreFn
|
||||
}
|
||||
|
||||
if cfg.TokenFn == nil {
|
||||
cfg.TokenFn = defaultTokenFn
|
||||
}
|
||||
|
||||
if cfg.GetUserFn == nil {
|
||||
cfg.GetUserFn = defaultGetUserFn
|
||||
}
|
||||
|
||||
return func(c *nf.Ctx) error {
|
||||
if cfg.IgnoreFn(c) {
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
token, ok := cfg.TokenFn(c)
|
||||
if !ok {
|
||||
return resp.Resp401(c, nil, "请登录")
|
||||
}
|
||||
|
||||
op, err := cfg.GetUserFn(c, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Locals("user", op)
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
}
|
58
internal/serve/handler/approve.go
Normal file
58
internal/serve/handler/approve.go
Normal file
@ -0,0 +1,58 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/google/uuid"
|
||||
"github.com/loveuer/nf"
|
||||
"github.com/loveuer/nf/nft/resp"
|
||||
"net/http"
|
||||
"time"
|
||||
"uauth/internal/store/cache"
|
||||
"uauth/internal/tool"
|
||||
"uauth/model"
|
||||
)
|
||||
|
||||
func Approve(c *nf.Ctx) error {
|
||||
// 获取表单数据
|
||||
type Req struct {
|
||||
ClientId string `form:"client_id"`
|
||||
ClientSecret string `form:"client_secret"`
|
||||
RedirectURI string `form:"redirect_uri"`
|
||||
Scope string `form:"scope"`
|
||||
State string `form:"state"`
|
||||
}
|
||||
|
||||
var (
|
||||
ok bool
|
||||
op *model.User
|
||||
err error
|
||||
req = new(Req)
|
||||
)
|
||||
|
||||
if op, ok = c.Locals("user").(*model.User); !ok {
|
||||
return resp.Resp401(c, nil)
|
||||
}
|
||||
|
||||
if err = c.BodyParser(req); err != nil {
|
||||
return resp.Resp400(c, err)
|
||||
}
|
||||
|
||||
state := cache.Prefix + "state_code:" + req.State
|
||||
if _, err = cache.Client.Get(c.Context(), state); err != nil {
|
||||
if errors.Is(err, cache.ErrorKeyNotFound) {
|
||||
return resp.Resp400(c, req, "Bad Approve Request")
|
||||
}
|
||||
|
||||
return resp.Resp500(c, err)
|
||||
}
|
||||
|
||||
_ = cache.Client.Del(tool.Timeout(3), state)
|
||||
|
||||
authorizationCode := uuid.New().String()[:8]
|
||||
if err = cache.Client.SetEx(c.Context(), cache.Prefix+"auth_code:"+authorizationCode, op.Id, 10*time.Minute); err != nil {
|
||||
return resp.Resp500(c, err)
|
||||
}
|
||||
|
||||
// 重定向到回调 URL 并附带授权码
|
||||
return c.Redirect(req.RedirectURI+"?code="+authorizationCode, http.StatusFound)
|
||||
}
|
@ -1,39 +1,97 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"errors"
|
||||
"github.com/google/uuid"
|
||||
"github.com/loveuer/nf"
|
||||
"github.com/loveuer/nf/nft/resp"
|
||||
"gorm.io/gorm"
|
||||
"net/http"
|
||||
"time"
|
||||
"uauth/internal/store/cache"
|
||||
"uauth/internal/store/db"
|
||||
"uauth/model"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed serve_authorize.html
|
||||
pageAuthorize string
|
||||
)
|
||||
|
||||
func Authorize(c *nf.Ctx) error {
|
||||
|
||||
// 解析查询参数
|
||||
clientID := c.Query("client_id")
|
||||
responseType := c.Query("response_type")
|
||||
redirectURI := c.Query("redirect_uri")
|
||||
scope := c.Query("scope")
|
||||
type Req struct {
|
||||
ClientId string `query:"client_id"`
|
||||
ClientSecret string `query:"client_secret"`
|
||||
ResponseType string `query:"response_type"`
|
||||
RedirectURI string `query:"redirect_uri"`
|
||||
Scope string `query:"scope"`
|
||||
}
|
||||
|
||||
// 检查客户端 ID 和其他参数
|
||||
// 在实际应用中,你需要检查这些参数是否合法
|
||||
if clientID != "12345" || responseType != "code" || redirectURI != "http://localhost:8080/callback" {
|
||||
var (
|
||||
ok bool
|
||||
op *model.User
|
||||
req = new(Req)
|
||||
err error
|
||||
client = &model.Client{}
|
||||
authRecord = &model.AuthorizationRecord{}
|
||||
)
|
||||
|
||||
if err = c.QueryParser(req); err != nil {
|
||||
return resp.Resp400(c, err)
|
||||
}
|
||||
|
||||
if req.ResponseType != "code" {
|
||||
return c.Status(http.StatusBadRequest).SendString("Invalid request")
|
||||
}
|
||||
|
||||
// 显示授权页面给用户
|
||||
_, err := c.Write([]byte(`
|
||||
<html>
|
||||
<head><title>Authorization</title></head>
|
||||
<body>
|
||||
<h1>Do you want to authorize this application?</h1>
|
||||
<form action="/approve" method="post">
|
||||
<input type="hidden" name="client_id" value="` + clientID + `"/>
|
||||
<input type="hidden" name="redirect_uri" value="` + redirectURI + `"/>
|
||||
<input type="hidden" name="scope" value="` + scope + `"/>
|
||||
<button type="submit">Yes, I authorize</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
`))
|
||||
if op, ok = c.Locals("user").(*model.User); !ok {
|
||||
return resp.Resp401(c, nil)
|
||||
}
|
||||
|
||||
return err
|
||||
if err = db.Default.Session().Where("client_id", req.ClientId).Take(client).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return resp.Resp400(c, err, "无效的 client id")
|
||||
}
|
||||
}
|
||||
|
||||
if err = db.Default.Session().Model(&model.AuthorizationRecord{}).
|
||||
Where("user_id", op.Id).
|
||||
Where("client_id", client.ClientId).
|
||||
Take(authRecord).
|
||||
Error; err != nil {
|
||||
// 用户第一次对该 client 进行授权
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
|
||||
state := uuid.New().String()[:8]
|
||||
if err = cache.Client.SetEx(c.Context(), cache.Prefix+"state_code:"+state, nil, 10*time.Minute); err != nil {
|
||||
return resp.Resp500(c, err.Error())
|
||||
}
|
||||
|
||||
return c.RenderHTML("authorize", pageAuthorize, map[string]any{
|
||||
"user": map[string]any{
|
||||
"username": op.Username,
|
||||
"avatar": "https://picsum.photos/200",
|
||||
},
|
||||
"client_id": req.ClientId,
|
||||
"client_secret": req.ClientSecret,
|
||||
"redirect_uri": req.RedirectURI,
|
||||
"scope": req.Scope,
|
||||
"state": state,
|
||||
})
|
||||
}
|
||||
|
||||
return resp.Resp500(c, err)
|
||||
}
|
||||
|
||||
// 当用户已经授权过时
|
||||
// 生成授权码并缓存授权码
|
||||
authorizationCode := uuid.New().String()[:8]
|
||||
if err = cache.Client.SetEx(c.Context(), cache.Prefix+"auth_code:"+authorizationCode, op.Id, 10*time.Minute); err != nil {
|
||||
return resp.Resp500(c, err)
|
||||
}
|
||||
|
||||
// 重定向到回调 URL 并附带授权码
|
||||
return c.Redirect(req.RedirectURI+"?code="+authorizationCode, http.StatusFound)
|
||||
}
|
||||
|
@ -2,23 +2,26 @@ package handler
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"github.com/google/uuid"
|
||||
"github.com/loveuer/nf"
|
||||
"github.com/loveuer/nf/nft/resp"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
"uauth/internal/store/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed serve_login.html
|
||||
page string
|
||||
pageLogin string
|
||||
)
|
||||
|
||||
func LoginPage(c *nf.Ctx) error {
|
||||
type Req struct {
|
||||
ClientID string `query:"client_id"`
|
||||
ClientSecret string `query:"client_secret"`
|
||||
Scope string `query:"scope"`
|
||||
RedirectURI string `query:"redirect_uri"`
|
||||
ClientId string `query:"client_id" json:"client_id"`
|
||||
ClientSecret string `query:"client_secret" json:"client_secret"`
|
||||
Scope string `query:"scope" json:"scope"`
|
||||
RedirectURI string `query:"redirect_uri" json:"redirect_uri"`
|
||||
}
|
||||
|
||||
var (
|
||||
@ -30,7 +33,7 @@ func LoginPage(c *nf.Ctx) error {
|
||||
return resp.Resp400(c, err.Error())
|
||||
}
|
||||
|
||||
if req.ClientID == "" || req.ClientSecret == "" || req.RedirectURI == "" {
|
||||
if req.ClientId == "" || req.ClientSecret == "" || req.RedirectURI == "" {
|
||||
return resp.Resp400(c, req)
|
||||
}
|
||||
|
||||
@ -38,11 +41,17 @@ func LoginPage(c *nf.Ctx) error {
|
||||
|
||||
// todo: 如果用户是已登录状态,则直接带上信息返回到 authorize 页面
|
||||
|
||||
return c.RenderHTML("login", page, map[string]interface{}{
|
||||
"client_id": req.ClientID,
|
||||
state := uuid.New().String()[:8]
|
||||
if err = cache.Client.SetEx(c.Context(), cache.Prefix+"state_code:"+state, nil, 10*time.Minute); err != nil {
|
||||
return resp.Resp500(c, err.Error())
|
||||
}
|
||||
|
||||
return c.RenderHTML("login", pageLogin, map[string]interface{}{
|
||||
"client_id": req.ClientId,
|
||||
"client_secret": req.ClientSecret,
|
||||
"redirect_uri": req.RedirectURI,
|
||||
"scope": req.Scope,
|
||||
"state": state,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"errors"
|
||||
"github.com/loveuer/nf"
|
||||
"github.com/loveuer/nf/nft/resp"
|
||||
@ -28,7 +29,7 @@ func ClientRegistry(c *nf.Ctx) error {
|
||||
|
||||
Secret := tool.RandomString(32)
|
||||
|
||||
platform := &model.Platform{
|
||||
platform := &model.Client{
|
||||
ClientId: req.ClientId,
|
||||
Icon: req.Icon,
|
||||
Name: req.Name,
|
||||
@ -45,3 +46,66 @@ func ClientRegistry(c *nf.Ctx) error {
|
||||
|
||||
return resp.Resp200(c, platform)
|
||||
}
|
||||
|
||||
var (
|
||||
//go:embed serve_registry.html
|
||||
registryLogin string
|
||||
)
|
||||
|
||||
func UserRegistryPage(c *nf.Ctx) error {
|
||||
return c.HTML(registryLogin)
|
||||
}
|
||||
|
||||
func UserRegistryAction(c *nf.Ctx) error {
|
||||
type Req struct {
|
||||
Username string `form:"username"`
|
||||
Nickname string `form:"nickname"`
|
||||
Password string `form:"password"`
|
||||
ConfirmPassword string `form:"confirm_password"`
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
req = new(Req)
|
||||
)
|
||||
|
||||
if err = c.BodyParser(req); err != nil {
|
||||
return resp.Resp400(c, err.Error())
|
||||
}
|
||||
|
||||
if err = tool.CheckPassword(req.Password); err != nil {
|
||||
return resp.Resp400(c, req, err.Error())
|
||||
}
|
||||
|
||||
op := &model.User{
|
||||
Username: req.Username,
|
||||
Nickname: req.Nickname,
|
||||
Password: tool.NewPassword(req.Password),
|
||||
}
|
||||
|
||||
if err = db.Default.Session().Create(op).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return resp.Resp400(c, err, "用户名已存在")
|
||||
}
|
||||
|
||||
return resp.Resp500(c, err)
|
||||
}
|
||||
|
||||
return c.HTML(`
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.jade.min.css"
|
||||
>
|
||||
<title>注册成功</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>注册成功</h1>
|
||||
<h3>快去试试吧</h3>
|
||||
</body>
|
||||
</html>
|
||||
`)
|
||||
}
|
||||
|
49
internal/serve/handler/serve_authorize.html
Normal file
49
internal/serve/handler/serve_authorize.html
Normal file
@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.jade.min.css"
|
||||
>
|
||||
<title>Server Login</title>
|
||||
<style>
|
||||
body {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h3>授权登录到 {{ .client_name }} 平台</h3>
|
||||
<div class="userinfo">
|
||||
<article style="display: flex; align-items: center;">
|
||||
<div style="height:50px; width:50px;">
|
||||
<img src="{{ .user.avatar }}"/>
|
||||
</div>
|
||||
<div style="margin-left:20px; ">
|
||||
{{ .user.username }}
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<form action="/api/oauth/v2/authorize" method="POST">
|
||||
<fieldset>
|
||||
<input type="hidden" name="client_id" value="{{ .client_id }}"/>
|
||||
<input type="hidden" name="client_secret" value="{{ .client_secret }}"/>
|
||||
<input type="hidden" name="redirect_uri" value="{{ .redirect_uri }}"/>
|
||||
<input type="hidden" name="scope" value="{{ .scope }}"/>
|
||||
<input type="hidden" name="state" value="{{ .state }}"/>
|
||||
</fieldset>
|
||||
|
||||
<div style="display: flex;">
|
||||
<button type="button" class="contrast" style="flex:1; margin-right: 10px">取消</button>
|
||||
<button type="submit" style="flex: 1;">授权</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -19,7 +19,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h3>欢迎来到 Pro</h3>
|
||||
<h3>欢迎来到 UAuth</h3>
|
||||
<form action="/api/oauth/v2/login" method="POST">
|
||||
<fieldset>
|
||||
<label>
|
||||
|
70
internal/serve/handler/serve_registry.html
Normal file
70
internal/serve/handler/serve_registry.html
Normal file
@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.jade.min.css"
|
||||
>
|
||||
<title>Server Login</title>
|
||||
<style>
|
||||
body {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h3>欢迎注册 UAuth</h3>
|
||||
<form action="/api/oauth/v2/registry/user" method="POST" id="form">
|
||||
<fieldset>
|
||||
<label>
|
||||
用户名
|
||||
<input id="username" name="username" autocomplete="given-name"/>
|
||||
</label>
|
||||
<label>
|
||||
昵称
|
||||
<input id="nickname" name="nickname" autocomplete="given-name"/>
|
||||
</label>
|
||||
<label>
|
||||
密码
|
||||
<input id="password" type="password" name="password" autocomplete="password"/>
|
||||
</label>
|
||||
<label>
|
||||
重复密码
|
||||
<input id="confirm_password" type="password" name="confirm_password" autocomplete="password"/>
|
||||
</label>
|
||||
<button type="button" style="flex: 1;width: 100%;" onclick="registry()">注册</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
function registry() {
|
||||
let user = {
|
||||
username: document.querySelector("#username").value,
|
||||
nickname: document.querySelector("#nickname").value,
|
||||
password: document.querySelector("#password").value,
|
||||
confirm_password: document.querySelector("#confirm_password").value,
|
||||
}
|
||||
|
||||
console.log('[D] user = ', user)
|
||||
|
||||
if (!user.username || !user.password || !user.nickname) {
|
||||
window.alert("参数均不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
if (user.password !== user.confirm_password) {
|
||||
window.alert("两次密码不一致")
|
||||
return
|
||||
}
|
||||
|
||||
document.querySelector("#form").submit()
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -8,6 +8,7 @@ import (
|
||||
"github.com/loveuer/nf/nft/log"
|
||||
"net/http"
|
||||
"time"
|
||||
"uauth/internal/middleware/auth"
|
||||
"uauth/internal/opt"
|
||||
"uauth/internal/serve/handler"
|
||||
"uauth/internal/store/cache"
|
||||
@ -42,23 +43,6 @@ func handleLogin(c *nf.Ctx) error {
|
||||
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 {
|
||||
var (
|
||||
@ -131,15 +115,20 @@ func Run(ctx context.Context) error {
|
||||
app := nf.New()
|
||||
|
||||
api := app.Group(opt.Cfg.Svc.Prefix)
|
||||
// 设置路由
|
||||
api.Post("/client/registry", handler.ClientRegistry)
|
||||
|
||||
api.Get("/registry/user", handler.UserRegistryPage)
|
||||
api.Post("/registry/user", handler.UserRegistryAction)
|
||||
api.Post("/registry/client", handler.ClientRegistry)
|
||||
api.Get("/login", handler.LoginPage)
|
||||
api.Post("/login", handler.LoginAction)
|
||||
api.Get("/authorize", handler.Authorize)
|
||||
api.Post("/approve", handleApprove)
|
||||
api.Post("/approve", handler.Approve)
|
||||
api.Post("/token", handleToken)
|
||||
|
||||
// 启动 HTTP 服务器
|
||||
api.Get("/after", auth.New(), func(c *nf.Ctx) error {
|
||||
return c.SendString("hello world")
|
||||
})
|
||||
|
||||
log.Info("Starting server on: %s", opt.Cfg.Svc.Address)
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
|
11
model/authorization.go
Normal file
11
model/authorization.go
Normal file
@ -0,0 +1,11 @@
|
||||
package model
|
||||
|
||||
type AuthorizationRecord struct {
|
||||
Id uint64 `json:"id" gorm:"primaryKey;column:id"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"column:created_at;autoCreateTime:milli"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"column:updated_at;autoUpdateTime:milli"`
|
||||
DeletedAt int64 `json:"deleted_at" gorm:"index;column:deleted_at;default:0"`
|
||||
|
||||
UserId uint64 `json:"user_id" gorm:"column:user_id"`
|
||||
ClientId uint64 `json:"client_id" gorm:"column:client_id"`
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package model
|
||||
|
||||
type Platform struct {
|
||||
type Client struct {
|
||||
Id uint64 `json:"id" gorm:"primaryKey;column:id"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"column:created_at;autoCreateTime:milli"`
|
||||
UpdatedAt int64 `json:"updated_at" gorm:"column:updated_at;autoUpdateTime:milli"`
|
30
model/init.go
Normal file
30
model/init.go
Normal file
@ -0,0 +1,30 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
"uauth/internal/tool"
|
||||
)
|
||||
|
||||
func Init(tx *gorm.DB) error {
|
||||
var err error
|
||||
if err = tx.AutoMigrate(
|
||||
&User{},
|
||||
&Client{},
|
||||
&AuthorizationRecord{},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = tx.Clauses(clause.OnConflict{DoNothing: true}).
|
||||
Create(&User{Username: "admin", Nickname: "admin", Password: tool.NewPassword("Foobar123")}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = tx.Clauses(clause.OnConflict{DoNothing: true}).
|
||||
Create(&Client{ClientId: "test", ClientSecret: "test", Name: "测试", Icon: "https://picsum.photos/200"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -22,6 +22,7 @@ type User struct {
|
||||
Status Status `json:"status" gorm:"column:status;default:0"`
|
||||
|
||||
Nickname string `json:"nickname" gorm:"column:nickname;type:varchar(64)"`
|
||||
Avatar string `json:"avatar" gorm:"column:avatar;type:varchar(256)"`
|
||||
Comment string `json:"comment" gorm:"column:comment"`
|
||||
|
||||
CreatedById uint64 `json:"created_by_id" gorm:"column:created_by_id"`
|
||||
@ -37,6 +38,7 @@ func (u *User) JwtEncode() (token string, err error) {
|
||||
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS512, jwt.MapClaims{
|
||||
"id": u.Id,
|
||||
"username": u.Username,
|
||||
"nickname": u.Nickname,
|
||||
"status": u.Status,
|
||||
"login_at": now.UnixMilli(),
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user