批量更新
This commit is contained in:
@@ -0,0 +1,592 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"server/models"
|
||||
"server/pkg/jwtutil"
|
||||
authsvc "server/services/auth"
|
||||
"server/services/auth/idp"
|
||||
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
"github.com/beego/beego/v2/server/web/context"
|
||||
)
|
||||
|
||||
// authBase 认证中心对外基址(用于拼接第三方回调地址)
|
||||
func authBase(c *AuthThirdController) string {
|
||||
if iss := jwtutil.Issuer(); iss != "" {
|
||||
return iss
|
||||
}
|
||||
scheme := "https"
|
||||
if c.Ctx.Request.TLS == nil {
|
||||
scheme = "http"
|
||||
}
|
||||
return scheme + "://" + c.Ctx.Request.Host + "/auth"
|
||||
}
|
||||
|
||||
// claimsFromCtxThird 从 Authorization 头解析登录态
|
||||
func claimsFromCtxThird(ctx *context.Context) *jwtutil.Claims {
|
||||
header := ctx.Request.Header.Get("Authorization")
|
||||
if header == "" {
|
||||
return nil
|
||||
}
|
||||
parts := strings.SplitN(header, " ", 2)
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
return nil
|
||||
}
|
||||
claims, err := jwtutil.ParseToken(strings.TrimSpace(parts[1]))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return claims
|
||||
}
|
||||
|
||||
// 第三方登录未绑定时的错误
|
||||
var ErrThirdNotBound = errors.New("该第三方账号尚未绑定,请先用账号密码登录后绑定")
|
||||
|
||||
// AuthThirdController 第三方登录(微信/钉钉/飞书/QQ/GitHub/Google)
|
||||
type AuthThirdController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
func (c *AuthThirdController) serveJSON(data map[string]interface{}) {
|
||||
c.Data["json"] = data
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
// thirdState 第三方授权流程中通过 state 透传的上下文(OIDC 原始参数)
|
||||
type thirdState struct {
|
||||
ClientID string `json:"client_id"`
|
||||
RedirectURI string `json:"redirect_uri"`
|
||||
CodeChallenge string `json:"code_challenge"`
|
||||
Method string `json:"code_challenge_method"`
|
||||
Scope string `json:"scope"`
|
||||
Nonce string `json:"nonce"`
|
||||
OIDCState string `json:"oidc_state"`
|
||||
Tid uint64 `json:"tid"`
|
||||
// Action:login=第三方登录(默认);bind=已登录用户绑定第三方账号
|
||||
Action string `json:"action"`
|
||||
}
|
||||
|
||||
// List 已开通的第三方登录方式(供登录页展示按钮)
|
||||
// GET /auth/third/list?tid=
|
||||
func (c *AuthThirdController) List() {
|
||||
tid, _ := c.GetInt64("tid", 0)
|
||||
list := make([]map[string]interface{}, 0)
|
||||
for _, p := range idp.Supported() {
|
||||
_, configured := idp.LoadConfig(p, uint64(tid))
|
||||
list = append(list, map[string]interface{}{
|
||||
"provider": p,
|
||||
"name": providerName(p),
|
||||
"enabled": configured,
|
||||
})
|
||||
}
|
||||
c.serveJSON(map[string]interface{}{"code": 200, "data": list})
|
||||
}
|
||||
|
||||
// Authorize 跳转到第三方授权页
|
||||
// GET /auth/third/:provider/authorize?client_id=&redirect_uri=&code_challenge=&state=&tid=
|
||||
func (c *AuthThirdController) Authorize() {
|
||||
provider := strings.TrimSpace(c.Ctx.Input.Param(":provider"))
|
||||
if !idp.IsSupported(provider) {
|
||||
c.Ctx.Output.SetStatus(400)
|
||||
_, _ = c.Ctx.ResponseWriter.Write([]byte("不支持的登录方式: " + provider))
|
||||
return
|
||||
}
|
||||
tid, _ := c.GetInt64("tid", 0)
|
||||
|
||||
cfg, configured := idp.LoadConfig(provider, uint64(tid))
|
||||
if !configured || cfg.AppID == "" || cfg.AppSecret == "" {
|
||||
c.Ctx.Output.SetStatus(400)
|
||||
_, _ = c.Ctx.ResponseWriter.Write([]byte("该登录方式尚未开通,请联系管理员配置"))
|
||||
return
|
||||
}
|
||||
|
||||
st := thirdState{
|
||||
ClientID: c.GetString("client_id"),
|
||||
RedirectURI: c.GetString("redirect_uri"),
|
||||
CodeChallenge: c.GetString("code_challenge"),
|
||||
Method: c.GetString("code_challenge_method"),
|
||||
Scope: c.GetString("scope"),
|
||||
Nonce: c.GetString("nonce"),
|
||||
OIDCState: c.GetString("state"),
|
||||
Tid: uint64(tid),
|
||||
Action: strings.TrimSpace(c.GetString("action")),
|
||||
}
|
||||
|
||||
// 登录页场景:参数封装在 redirect(base64 编码的 authorize URL)里,
|
||||
// 这里解出来,使第三方登录完成后能回到原来的 OIDC 授权流程。
|
||||
if enc := c.GetString("redirect"); enc != "" && st.ClientID == "" {
|
||||
if raw, err := base64.RawURLEncoding.DecodeString(enc); err == nil {
|
||||
if u, err := url.Parse(string(raw)); err == nil {
|
||||
q := u.Query()
|
||||
st.ClientID = q.Get("client_id")
|
||||
st.RedirectURI = q.Get("redirect_uri")
|
||||
st.CodeChallenge = q.Get("code_challenge")
|
||||
st.Method = q.Get("code_challenge_method")
|
||||
st.Scope = q.Get("scope")
|
||||
st.Nonce = q.Get("nonce")
|
||||
st.OIDCState = q.Get("state")
|
||||
}
|
||||
}
|
||||
}
|
||||
// 第三方登录仅面向租户域应用:平台端(platform.yunzer.cn)不走认证中心,
|
||||
// 这里再校验一次 realm,避免平台域应用被误接入。
|
||||
if st.ClientID != "" {
|
||||
if client, err := findClient(st.ClientID); err == nil && client.Realm != models.AuthRealmTenant {
|
||||
c.Ctx.Output.SetStatus(400)
|
||||
_, _ = c.Ctx.ResponseWriter.Write([]byte("该应用不支持第三方登录"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
raw, err := json.Marshal(st)
|
||||
if err != nil {
|
||||
c.Ctx.Output.SetStatus(500)
|
||||
return
|
||||
}
|
||||
state := base64.RawURLEncoding.EncodeToString(raw)
|
||||
|
||||
// 注意:authBase 已包含 /auth 前缀(issuer = https://api.yunzer.cn/auth),
|
||||
// 这里不能再拼 /auth,否则回调地址会变成 /auth/auth/third/...
|
||||
callbackURI := authBase(c) + "/third/" + provider + "/callback"
|
||||
c.Redirect(idp.BuildAuthURL(cfg, callbackURI, state), 302)
|
||||
}
|
||||
|
||||
// Callback 第三方回调:换取身份 → 建会话 → 回到 OIDC authorize 继续发码
|
||||
// GET /auth/third/:provider/callback?code=&state=
|
||||
func (c *AuthThirdController) Callback() {
|
||||
provider := strings.TrimSpace(c.Ctx.Input.Param(":provider"))
|
||||
code := c.GetString("code")
|
||||
stateParam := c.GetString("state")
|
||||
|
||||
fail := func(msg string) {
|
||||
c.Redirect("/auth/login?error="+url.QueryEscape(msg), 302)
|
||||
}
|
||||
|
||||
if code == "" {
|
||||
fail("第三方登录失败:缺少授权码")
|
||||
return
|
||||
}
|
||||
|
||||
var st thirdState
|
||||
if raw, err := base64.RawURLEncoding.DecodeString(stateParam); err == nil {
|
||||
_ = json.Unmarshal(raw, &st)
|
||||
}
|
||||
|
||||
cfg, configured := idp.LoadConfig(provider, st.Tid)
|
||||
if !configured {
|
||||
fail("该登录方式尚未开通")
|
||||
return
|
||||
}
|
||||
info, err := idp.Exchange(cfg, code)
|
||||
if err != nil {
|
||||
fail(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 绑定场景:把第三方账号挂到当前登录身份上
|
||||
if st.Action == "bind" {
|
||||
finishBind(c, provider, info)
|
||||
return
|
||||
}
|
||||
|
||||
// 查找绑定关系:open_id 优先,其次 union_id
|
||||
bind, err := findThirdBinding(provider, info.OpenID, info.UnionID)
|
||||
if err != nil {
|
||||
fail(ErrThirdNotBound.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 校验身份状态
|
||||
var identity models.AuthIdentity
|
||||
if err := models.Orm.QueryTable(new(models.AuthIdentity)).
|
||||
Filter("id", bind.IdentityID).One(&identity); err != nil {
|
||||
fail("账号不存在")
|
||||
return
|
||||
}
|
||||
if identity.Status == models.AuthIdentityStatusDisabled {
|
||||
fail("账号已禁用")
|
||||
return
|
||||
}
|
||||
|
||||
// 建会话(tid=0 表示待选企业,authorize 会引导用户选择)
|
||||
sess, err := authsvc.CreateSession(authsvc.SessionInfo{
|
||||
IdentityID: identity.ID,
|
||||
Tid: authsvc.PendingTenantID,
|
||||
ClientID: st.ClientID,
|
||||
IP: c.Ctx.Input.IP(),
|
||||
UserAgent: c.Ctx.Request.UserAgent(),
|
||||
LoginType: authsvc.LoginTypeThird,
|
||||
Amr: provider,
|
||||
})
|
||||
if err != nil {
|
||||
fail("创建会话失败: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 更新绑定信息(昵称/头像可能变化)
|
||||
update := map[string]interface{}{}
|
||||
if info.Nickname != "" {
|
||||
update["nickname"] = info.Nickname
|
||||
}
|
||||
if info.Avatar != "" {
|
||||
update["avatar"] = info.Avatar
|
||||
}
|
||||
if info.Raw != "" {
|
||||
update["raw"] = info.Raw
|
||||
}
|
||||
if len(update) > 0 {
|
||||
_, _ = models.Orm.QueryTable(new(models.AuthIdentityThird)).
|
||||
Filter("id", bind.ID).Update(update)
|
||||
}
|
||||
|
||||
setSessionCookieForCtx(c.Ctx, sess.Sid)
|
||||
|
||||
// 回到 OIDC 授权流程继续签发授权码
|
||||
if st.ClientID != "" && st.RedirectURI != "" {
|
||||
v := url.Values{}
|
||||
v.Set("client_id", st.ClientID)
|
||||
v.Set("redirect_uri", st.RedirectURI)
|
||||
v.Set("response_type", ResponseTypeCode)
|
||||
v.Set("code_challenge", st.CodeChallenge)
|
||||
v.Set("code_challenge_method", defaultStr(st.Method, "S256"))
|
||||
if st.Scope != "" {
|
||||
v.Set("scope", st.Scope)
|
||||
}
|
||||
if st.Nonce != "" {
|
||||
v.Set("nonce", st.Nonce)
|
||||
}
|
||||
if st.OIDCState != "" {
|
||||
v.Set("state", st.OIDCState)
|
||||
}
|
||||
c.Redirect("/auth/authorize?"+v.Encode(), 302)
|
||||
return
|
||||
}
|
||||
c.Redirect("/auth/login", 302)
|
||||
}
|
||||
|
||||
// Bind 已登录用户绑定第三方账号
|
||||
// POST /auth/third/bind { provider, code, tid }
|
||||
func (c *AuthThirdController) Bind() {
|
||||
claims := claimsFromCtxThird(c.Ctx)
|
||||
if claims == nil || claims.UserID <= 0 {
|
||||
c.Ctx.Output.SetStatus(401)
|
||||
c.serveJSON(map[string]interface{}{"code": 401, "msg": "未登录"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Provider string `json:"provider"`
|
||||
Code string `json:"code"`
|
||||
Tid uint64 `json:"tid"`
|
||||
}
|
||||
_ = json.Unmarshal(c.Ctx.Input.RequestBody, &req)
|
||||
req.Provider = strings.TrimSpace(req.Provider)
|
||||
if !idp.IsSupported(req.Provider) || req.Code == "" {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||
return
|
||||
}
|
||||
|
||||
cfg, configured := idp.LoadConfig(req.Provider, req.Tid)
|
||||
if !configured {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "该登录方式尚未开通"})
|
||||
return
|
||||
}
|
||||
info, err := idp.Exchange(cfg, req.Code)
|
||||
if err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 该第三方账号若已绑定到别的身份,拒绝重复绑定
|
||||
if exist := models.Orm.QueryTable(new(models.AuthIdentityThird)).
|
||||
Filter("provider", req.Provider).
|
||||
Filter("open_id", info.OpenID).Exist(); exist {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "该第三方账号已被其他账号绑定"})
|
||||
return
|
||||
}
|
||||
|
||||
item := &models.AuthIdentityThird{
|
||||
IdentityID: uint64(claims.UserID),
|
||||
Provider: req.Provider,
|
||||
OpenID: info.OpenID,
|
||||
Nickname: strPtr(info.Nickname),
|
||||
Avatar: strPtr(info.Avatar),
|
||||
Raw: strPtr(info.Raw),
|
||||
}
|
||||
if info.UnionID != "" {
|
||||
item.UnionID = &info.UnionID
|
||||
}
|
||||
if _, err := models.Orm.Insert(item); err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 500, "msg": "绑定失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
c.serveJSON(map[string]interface{}{"code": 200, "msg": "绑定成功"})
|
||||
}
|
||||
|
||||
// BoundList 当前登录身份已绑定的第三方账号
|
||||
// GET /auth/third/bound
|
||||
func (c *AuthThirdController) BoundList() {
|
||||
claims := claimsFromCtxThird(c.Ctx)
|
||||
if claims == nil || claims.UserID <= 0 {
|
||||
c.Ctx.Output.SetStatus(401)
|
||||
c.serveJSON(map[string]interface{}{"code": 401, "msg": "未登录"})
|
||||
return
|
||||
}
|
||||
var rows []models.AuthIdentityThird
|
||||
if _, err := models.Orm.QueryTable(new(models.AuthIdentityThird)).
|
||||
Filter("identity_id", uint64(claims.UserID)).All(&rows); err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 500, "msg": "查询失败"})
|
||||
return
|
||||
}
|
||||
list := make([]map[string]interface{}, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
list = append(list, map[string]interface{}{
|
||||
"id": r.ID,
|
||||
"provider": r.Provider,
|
||||
"name": providerName(r.Provider),
|
||||
"nickname": deref(r.Nickname),
|
||||
"bind_time": r.BindTime,
|
||||
})
|
||||
}
|
||||
c.serveJSON(map[string]interface{}{"code": 200, "data": list})
|
||||
}
|
||||
|
||||
// Unbind 解绑第三方账号
|
||||
// POST /auth/third/unbind { provider }
|
||||
func (c *AuthThirdController) Unbind() {
|
||||
claims := claimsFromCtxThird(c.Ctx)
|
||||
if claims == nil || claims.UserID <= 0 {
|
||||
c.Ctx.Output.SetStatus(401)
|
||||
c.serveJSON(map[string]interface{}{"code": 401, "msg": "未登录"})
|
||||
return
|
||||
}
|
||||
var req struct {
|
||||
Provider string `json:"provider"`
|
||||
}
|
||||
_ = json.Unmarshal(c.Ctx.Input.RequestBody, &req)
|
||||
if strings.TrimSpace(req.Provider) == "" {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||
return
|
||||
}
|
||||
// 至少保留一种登录方式:若未设置密码则不允许解绑最后一个第三方
|
||||
var identity models.AuthIdentity
|
||||
// 未设置密码的用户,解绑最后一个第三方账号后将无法登录
|
||||
if err := models.Orm.QueryTable(new(models.AuthIdentity)).
|
||||
Filter("id", uint64(claims.UserID)).One(&identity); err == nil {
|
||||
hasPassword := identity.PasswordHash != nil && *identity.PasswordHash != ""
|
||||
if !hasPassword {
|
||||
cnt, _ := models.Orm.QueryTable(new(models.AuthIdentityThird)).
|
||||
Filter("identity_id", identity.ID).Count()
|
||||
if cnt <= 1 {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "未设置密码,不能解绑最后一个第三方账号"})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := models.Orm.QueryTable(new(models.AuthIdentityThird)).
|
||||
Filter("identity_id", uint64(claims.UserID)).
|
||||
Filter("provider", strings.TrimSpace(req.Provider)).
|
||||
Delete(); err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 500, "msg": "解绑失败"})
|
||||
return
|
||||
}
|
||||
c.serveJSON(map[string]interface{}{"code": 200, "msg": "已解绑"})
|
||||
}
|
||||
|
||||
// BindPage 第三方账号绑定管理页(浏览器访问,依赖认证中心会话 Cookie)
|
||||
// GET /auth/bind
|
||||
func (c *AuthThirdController) BindPage() {
|
||||
sid := strings.TrimSpace(c.Ctx.GetCookie(sessionCookieName))
|
||||
if sid == "" {
|
||||
c.Redirect("/auth/login", 302)
|
||||
return
|
||||
}
|
||||
session, err := authsvc.GetSession(sid)
|
||||
if err != nil {
|
||||
c.Redirect("/auth/login", 302)
|
||||
return
|
||||
}
|
||||
|
||||
// 已绑定列表
|
||||
var bound []models.AuthIdentityThird
|
||||
_, _ = models.Orm.QueryTable(new(models.AuthIdentityThird)).
|
||||
Filter("identity_id", session.IdentityID).All(&bound)
|
||||
boundMap := map[string]models.AuthIdentityThird{}
|
||||
for _, b := range bound {
|
||||
boundMap[b.Provider] = b
|
||||
}
|
||||
|
||||
// 全部平台(只展示已开通的)
|
||||
items := make([]map[string]interface{}, 0)
|
||||
for _, p := range idp.Supported() {
|
||||
_, configured := idp.LoadConfig(p, session.Tid)
|
||||
if !configured {
|
||||
continue
|
||||
}
|
||||
item := map[string]interface{}{
|
||||
"provider": p,
|
||||
"name": providerName(p),
|
||||
"bound": false,
|
||||
"nickname": "",
|
||||
}
|
||||
if b, ok := boundMap[p]; ok {
|
||||
item["bound"] = true
|
||||
item["nickname"] = deref(b.Nickname)
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
c.Data["Providers"] = items
|
||||
c.Data["Success"] = c.GetString("success")
|
||||
c.Data["Error"] = c.GetString("error")
|
||||
c.TplName = "auth/bind.tpl"
|
||||
}
|
||||
|
||||
// UnbindPage 解绑(页面入口,用会话 Cookie 鉴权,解绑后回到绑定页)
|
||||
// GET /auth/third/:provider/unbind
|
||||
func (c *AuthThirdController) UnbindPage() {
|
||||
provider := strings.TrimSpace(c.Ctx.Input.Param(":provider"))
|
||||
sid := strings.TrimSpace(c.Ctx.GetCookie(sessionCookieName))
|
||||
if sid == "" {
|
||||
c.Redirect("/auth/login", 302)
|
||||
return
|
||||
}
|
||||
session, err := authsvc.GetSession(sid)
|
||||
if err != nil {
|
||||
c.Redirect("/auth/login", 302)
|
||||
return
|
||||
}
|
||||
|
||||
var identity models.AuthIdentity
|
||||
hasPassword := true
|
||||
if err := models.Orm.QueryTable(new(models.AuthIdentity)).
|
||||
Filter("id", session.IdentityID).One(&identity); err == nil {
|
||||
hasPassword = identity.PasswordHash != nil && *identity.PasswordHash != ""
|
||||
}
|
||||
if !hasPassword {
|
||||
cnt, _ := models.Orm.QueryTable(new(models.AuthIdentityThird)).
|
||||
Filter("identity_id", session.IdentityID).Count()
|
||||
if cnt <= 1 {
|
||||
c.Redirect("/auth/bind?error="+url.QueryEscape("未设置密码,不能解绑最后一个第三方账号"), 302)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := models.Orm.QueryTable(new(models.AuthIdentityThird)).
|
||||
Filter("identity_id", session.IdentityID).
|
||||
Filter("provider", provider).Delete(); err != nil {
|
||||
c.Redirect("/auth/bind?error="+url.QueryEscape("解绑失败"), 302)
|
||||
return
|
||||
}
|
||||
c.Redirect("/auth/bind?success="+url.QueryEscape("已解绑"), 302)
|
||||
}
|
||||
|
||||
// finishBind 绑定场景的回调处理:把第三方账号挂到当前登录身份
|
||||
func finishBind(c *AuthThirdController, provider string, info *idp.UserInfo) {
|
||||
sid := strings.TrimSpace(c.Ctx.GetCookie(sessionCookieName))
|
||||
if sid == "" {
|
||||
c.Redirect("/auth/login", 302)
|
||||
return
|
||||
}
|
||||
session, err := authsvc.GetSession(sid)
|
||||
if err != nil {
|
||||
c.Redirect("/auth/login", 302)
|
||||
return
|
||||
}
|
||||
|
||||
// 该第三方账号已绑定到别的身份 → 拒绝
|
||||
if exist := models.Orm.QueryTable(new(models.AuthIdentityThird)).
|
||||
Filter("provider", provider).
|
||||
Filter("open_id", info.OpenID).Exist(); exist {
|
||||
c.Redirect("/auth/bind?error="+url.QueryEscape("该"+providerName(provider)+"账号已被其他账号绑定"), 302)
|
||||
return
|
||||
}
|
||||
// 同一身份重复绑定同一平台 → 先清掉旧的
|
||||
_, _ = models.Orm.QueryTable(new(models.AuthIdentityThird)).
|
||||
Filter("identity_id", session.IdentityID).
|
||||
Filter("provider", provider).Delete()
|
||||
|
||||
item := &models.AuthIdentityThird{
|
||||
IdentityID: session.IdentityID,
|
||||
Provider: provider,
|
||||
OpenID: info.OpenID,
|
||||
Nickname: strPtr(info.Nickname),
|
||||
Avatar: strPtr(info.Avatar),
|
||||
Raw: strPtr(info.Raw),
|
||||
}
|
||||
if info.UnionID != "" {
|
||||
item.UnionID = &info.UnionID
|
||||
}
|
||||
if _, err := models.Orm.Insert(item); err != nil {
|
||||
c.Redirect("/auth/bind?error="+url.QueryEscape("绑定失败"), 302)
|
||||
return
|
||||
}
|
||||
c.Redirect("/auth/bind?success="+url.QueryEscape("绑定成功"), 302)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- 工具
|
||||
|
||||
// findThirdBinding 按 open_id 查找,其次按 union_id 查找
|
||||
func findThirdBinding(provider, openID, unionID string) (*models.AuthIdentityThird, error) {
|
||||
var bind models.AuthIdentityThird
|
||||
qs := models.Orm.QueryTable(new(models.AuthIdentityThird)).Filter("provider", provider)
|
||||
|
||||
if openID != "" {
|
||||
if err := qs.Filter("open_id", openID).One(&bind); err == nil {
|
||||
return &bind, nil
|
||||
}
|
||||
}
|
||||
if unionID != "" {
|
||||
if err := qs.Filter("union_id", unionID).One(&bind); err == nil {
|
||||
return &bind, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("not bound")
|
||||
}
|
||||
|
||||
func providerName(p string) string {
|
||||
switch p {
|
||||
case models.IdPWechat:
|
||||
return "微信"
|
||||
case models.IdPDingTalk:
|
||||
return "钉钉"
|
||||
case models.IdPFeishu:
|
||||
return "飞书"
|
||||
case models.IdPQQ:
|
||||
return "QQ"
|
||||
case models.IdPGitHub:
|
||||
return "GitHub"
|
||||
case models.IdPGoogle:
|
||||
return "Google"
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func defaultStr(v, def string) string {
|
||||
if strings.TrimSpace(v) == "" {
|
||||
return def
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func strPtr(s string) *string {
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
return &s
|
||||
}
|
||||
|
||||
func deref(p *string) string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
return *p
|
||||
}
|
||||
Reference in New Issue
Block a user