更新
This commit is contained in:
@@ -174,16 +174,25 @@ func (a *App) enabledAdminOAuthProviders(ctx context.Context) ([]adminOAuthProvi
|
||||
}
|
||||
|
||||
func (a *App) oauthConfigurationReady(ctx context.Context) bool {
|
||||
if _, err := a.adminOAuthFrontendURL(ctx); err != nil {
|
||||
adminProviders, err := a.enabledAdminOAuthProviders(ctx)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if _, err := a.userOAuthFrontendURL(ctx); err != nil {
|
||||
userProviders, err := a.enabledUserOAuthProviders(ctx)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if _, err := a.enabledAdminOAuthProviders(ctx); err != nil {
|
||||
return false
|
||||
if len(adminProviders) > 0 {
|
||||
if _, err := a.adminOAuthFrontendURL(ctx); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
_, err := a.enabledUserOAuthProviders(ctx)
|
||||
if len(userProviders) > 0 {
|
||||
if _, err := a.userOAuthFrontendURL(ctx); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
_, err = a.enabledAppOAuthProviders(ctx)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
@@ -199,24 +208,33 @@ func (a *App) validateAdminOAuthConfigValues(ctx context.Context, values map[str
|
||||
}
|
||||
return strings.TrimSpace(a.configPlain(ctx, key, ""))
|
||||
}
|
||||
frontendURL := value("oauth.admin.frontend_callback_url")
|
||||
if frontendURL == "" {
|
||||
return errors.New("管理端登录结果页不能为空")
|
||||
}
|
||||
if err := a.validateAdminOAuthRedirectURL(frontendURL); err != nil {
|
||||
return fmt.Errorf("管理端登录结果页无效:%w", err)
|
||||
}
|
||||
userFrontendURL := value("oauth.user.frontend_callback_url")
|
||||
if userFrontendURL == "" {
|
||||
return errors.New("客户端 H5 登录结果页不能为空")
|
||||
}
|
||||
if err := a.validateAdminOAuthRedirectURL(userFrontendURL); err != nil {
|
||||
return fmt.Errorf("客户端 H5 登录结果页无效:%w", err)
|
||||
for _, client := range []struct{ prefix, callback, name string }{
|
||||
{"oauth.", "oauth.admin.frontend_callback_url", "管理端"},
|
||||
{"oauth.user.", "oauth.user.frontend_callback_url", "H5"},
|
||||
} {
|
||||
for _, code := range []string{"wechat", "qq", "github", "google"} {
|
||||
if value(client.prefix+code+".enabled") == "true" {
|
||||
if err := a.validateAdminOAuthRedirectURL(value(client.callback)); err != nil {
|
||||
return fmt.Errorf("%s 登录结果页无效:%w", client.name, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, code := range []string{"wechat", "qq", "github", "google"} {
|
||||
adminEnabled := strings.ToLower(value("oauth."+code+".enabled")) == "true"
|
||||
userEnabled := strings.ToLower(value("oauth.user."+code+".enabled")) == "true"
|
||||
if !adminEnabled && !userEnabled {
|
||||
appEnabled := value("oauth.app."+code+".enabled") == "true"
|
||||
if appEnabled {
|
||||
if code == "github" {
|
||||
if value("oauth.app.frontend_callback_url") != appOAuthCallbackURL {
|
||||
return errors.New("App 回调地址必须为 " + appOAuthCallbackURL)
|
||||
}
|
||||
} else if err := validateNativeOAuthConfig(code, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !adminEnabled && !userEnabled && !(appEnabled && code == "github") {
|
||||
continue
|
||||
}
|
||||
prefix := "oauth." + code + "."
|
||||
@@ -425,12 +443,18 @@ func (a *App) adminOAuthExchange(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
return
|
||||
}
|
||||
token, err := a.consumeAdminOAuthCodeAndIssueToken(r.Context(), strings.TrimSpace(req.Code), loginCode.AdminUserID.Int64)
|
||||
adminID, realName, err := a.consumeAdminOAuthCode(r.Context(), strings.TrimSpace(req.Code), loginCode.AdminUserID.Int64)
|
||||
if err != nil {
|
||||
fail(w, http.StatusUnauthorized, 10001, err.Error())
|
||||
return
|
||||
}
|
||||
reply(w, map[string]any{"accessToken": token, "requiresLink": false})
|
||||
payload, err := a.newAdminSession(r.Context(), w, r, adminID, realName)
|
||||
if err != nil {
|
||||
fail(w, http.StatusInternalServerError, 50001, "创建管理会话失败")
|
||||
return
|
||||
}
|
||||
payload["requiresLink"] = false
|
||||
reply(w, payload)
|
||||
}
|
||||
|
||||
func (a *App) adminOAuthLink(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -496,8 +520,13 @@ func (a *App) adminOAuthLink(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
auditPayload, _ := json.Marshal(map[string]any{"provider": loginCode.Provider, "externalSubjectHash": fmt.Sprintf("%x", sha256.Sum256([]byte(loginCode.Subject)))})
|
||||
_, _ = a.db.ExecContext(r.Context(), `INSERT INTO admin_audit_logs(admin_user_id,action,target_type,target_id,request_data,ip) VALUES(?,?,?,?,?,?)`, adminID, "bind_oauth_identity", "admin_user", adminID, auditPayload, clientIP(r))
|
||||
token, _ := a.token(adminID, "admin", realName, 8*time.Hour)
|
||||
reply(w, map[string]any{"accessToken": token, "requiresLink": false})
|
||||
payload, err := a.newAdminSession(r.Context(), w, r, adminID, realName)
|
||||
if err != nil {
|
||||
fail(w, http.StatusInternalServerError, 50001, "创建管理会话失败")
|
||||
return
|
||||
}
|
||||
payload["requiresLink"] = false
|
||||
reply(w, payload)
|
||||
}
|
||||
|
||||
func (a *App) readAdminOAuthLoginCode(ctx context.Context, code string, requireUnlinked bool) (adminOAuthLoginCode, error) {
|
||||
@@ -510,27 +539,23 @@ func (a *App) readAdminOAuthLoginCode(ctx context.Context, code string, requireU
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (a *App) consumeAdminOAuthCodeAndIssueToken(ctx context.Context, code string, adminID int64) (string, error) {
|
||||
func (a *App) consumeAdminOAuthCode(ctx context.Context, code string, adminID int64) (int64, string, error) {
|
||||
var realName string
|
||||
var status int
|
||||
if err := a.db.QueryRowContext(ctx, `SELECT real_name,status FROM admin_users WHERE id=?`, adminID).Scan(&realName, &status); err != nil || status != 1 {
|
||||
return "", errors.New("管理员账号不存在或已停用")
|
||||
return 0, "", errors.New("管理员账号不存在或已停用")
|
||||
}
|
||||
result, err := a.db.ExecContext(ctx, `UPDATE admin_oauth_login_codes SET used_at=NOW(3) WHERE code_hash=? AND admin_user_id=? AND used_at IS NULL AND expires_at>NOW(3)`, oauthHash(code), adminID)
|
||||
if err != nil {
|
||||
return "", errors.New("第三方登录处理失败")
|
||||
return 0, "", errors.New("第三方登录处理失败")
|
||||
}
|
||||
affected, _ := result.RowsAffected()
|
||||
if affected != 1 {
|
||||
return "", errors.New("第三方登录凭证无效或已使用")
|
||||
return 0, "", errors.New("第三方登录凭证无效或已使用")
|
||||
}
|
||||
_, _ = a.db.ExecContext(ctx, `UPDATE admin_users SET last_login_at=NOW(3) WHERE id=?`, adminID)
|
||||
_, _ = a.db.ExecContext(ctx, `UPDATE admin_oauth_identities SET last_login_at=NOW(3) WHERE admin_user_id=?`, adminID)
|
||||
token, err := a.token(adminID, "admin", realName, 8*time.Hour)
|
||||
if err != nil {
|
||||
return "", errors.New("创建登录令牌失败")
|
||||
}
|
||||
return token, nil
|
||||
return adminID, realName, nil
|
||||
}
|
||||
|
||||
func (a *App) cleanupAdminOAuthRecords(ctx context.Context) {
|
||||
|
||||
Reference in New Issue
Block a user