forked from alterminal/alterminal
重組前檢查點:根目錄 main package
This commit is contained in:
+76
@@ -0,0 +1,76 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Session 為使用者瀏覽器 Session(SSO 核心):ID 為加密安全亂數,
|
||||
// 存於 HttpOnly Cookie,效期內使用者再經任何 RP 發起授權請求時
|
||||
// 無須重新輸入帳密。
|
||||
type Session struct {
|
||||
ID string `gorm:"primaryKey;size:43"` // 32 bytes 亂數的 base64url
|
||||
UserID uint `gorm:"not null;index"`
|
||||
User User
|
||||
ExpiresAt time.Time `gorm:"not null"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// sessionTTL 為 Session 有效時間,到期後 Cookie 失效、列為可清除。
|
||||
const sessionTTL = 24 * time.Hour
|
||||
|
||||
// ErrSessionExpired 表示 Session 不存在或已過期。
|
||||
var ErrSessionExpired = errors.New("session 不存在或已過期")
|
||||
|
||||
// newRandomToken 產生 n bytes 加密安全亂數的 base64url 字串(無填充;
|
||||
// n=32 時為 43 字元),供 Session ID 與 CSRF token 共用。
|
||||
func newRandomToken(n int) (string, error) {
|
||||
b := make([]byte, n)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return "", fmt.Errorf("read random: %w", err)
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
// createSession 為使用者建立新 Session,順帶刪除所有已過期 Session
|
||||
// (最佳清除,失敗不影響登入結果)。
|
||||
func createSession(db *gorm.DB, userID uint) (*Session, error) {
|
||||
id, err := newRandomToken(32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := &Session{ID: id, UserID: userID, ExpiresAt: time.Now().Add(sessionTTL)}
|
||||
if err := db.Create(s).Error; err != nil {
|
||||
return nil, fmt.Errorf("create session: %w", err)
|
||||
}
|
||||
db.Where("expires_at < ?", time.Now()).Delete(&Session{})
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// deleteSession 以 ID 刪除 Session(登出用)。查無該 Session 不視為
|
||||
// 錯誤,讓登出維持冪等。
|
||||
func deleteSession(db *gorm.DB, id string) error {
|
||||
if err := db.Delete(&Session{}, "id = ?", id).Error; err != nil {
|
||||
return fmt.Errorf("delete session: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getSession 以 ID 查詢效期內的 Session(含所屬使用者)。
|
||||
func getSession(db *gorm.DB, id string) (*Session, error) {
|
||||
var s Session
|
||||
err := db.Preload("User").Where("id = ? AND expires_at > ?", id, time.Now()).First(&s).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrSessionExpired
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query session: %w", err)
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
Reference in New Issue
Block a user