feat: App→网页免登录(一次性换票 SSO)——移动/桌面点链接直达已登录官网
- backend:POST /auth/web-ticket(需登录,签 60s 单次票据,每用户限频)+ POST /auth/web-ticket/exchange(公开,原子即焚兑换正常登录态,响应同 /auth/login 另带 shop_code);web_tickets 新表 + user_sessions.kind 列 (sso 会话:web 平台、refresh 12h 硬到期、不占设备并发配额、设备管理可见 可踢);清理任务顺带清过期票据;schema.sql/AutoMigrate/testutil 同步 - web:/sso 落地页——兑票写入与 Web 版共享的 localStorage 后跳 redirect; redirect 白名单只收本站相对路径(防 open redirect),票据即时从地址栏抹除 - client:launchAuthedWebPath(ref, path) 封装(签票→拼 /sso URL→launchUrl, 失败降级未登录直开;桌面/移动同一套);授权面板「查看套餐价格」接入 test(backend): web_ticket_test 三用例(兑换+续期/单次即焚/过期拒绝/ sso 不占配额双向验证) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,7 +24,10 @@ type UserSession struct {
|
||||
// 否则 revoke/cleanup 等任意 Updates 都会把已撤销会话误刷成「刚活跃」。
|
||||
LastSeenAt time.Time `gorm:"autoCreateTime" json:"last_seen_at"`
|
||||
RevokedAt *time.Time `gorm:"index" json:"revoked_at,omitempty"`
|
||||
RevokedReason string `gorm:"size:30" json:"revoked_reason,omitempty"` // kicked|logout|admin|disabled|reuse|pwd_reset|relogin
|
||||
RevokedReason string `gorm:"size:30" json:"revoked_reason,omitempty"` // kicked|logout|admin|disabled|reuse|pwd_reset|relogin
|
||||
// Kind 会话种类:login=正常登录;sso=App 换票的网页短时会话
|
||||
//(不计设备并发配额,refresh 短时效)。
|
||||
Kind string `gorm:"size:10;default:'login'" json:"kind"`
|
||||
RevokedBy *uint64 `gorm:"column:revoked_by" json:"revoked_by,omitempty"` // 吊销操作人 user_id;系统/自助吊销为 NULL
|
||||
RefreshExpAt time.Time `json:"refresh_exp_at"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// WebTicket App→网页免登录的一次性换票凭据(magic-link)。
|
||||
// App 持 JWT 签票(TTL 60s),浏览器打开 /sso 后凭票兑换正常 web 会话;
|
||||
// 单次可用(used_at 原子置位防重放),票据本身不含任何令牌信息。
|
||||
type WebTicket struct {
|
||||
ID uint64 `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
Ticket string `gorm:"size:64;not null;uniqueIndex" json:"-"`
|
||||
ShopID uint64 `gorm:"not null" json:"shop_id"`
|
||||
UserID uint64 `gorm:"not null" json:"user_id"`
|
||||
ExpiresAt time.Time `gorm:"not null;index" json:"expires_at"`
|
||||
UsedAt *time.Time `json:"used_at,omitempty"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
}
|
||||
|
||||
func (WebTicket) TableName() string { return "web_tickets" }
|
||||
Reference in New Issue
Block a user