mirror of
https://github.com/snowykami/neo-blog.git
synced 2025-09-05 16:56:22 +00:00
108 lines
3.9 KiB
Go
108 lines
3.9 KiB
Go
package model
|
||
|
||
import (
|
||
"fmt"
|
||
"github.com/snowykami/neo-blog/internal/dto"
|
||
"gorm.io/gorm"
|
||
"resty.dev/v3"
|
||
"time"
|
||
)
|
||
|
||
type OidcConfig struct {
|
||
gorm.Model
|
||
Name string `gorm:"uniqueIndex"` // OIDC配置名称,唯一
|
||
ClientID string // 客户端ID
|
||
ClientSecret string // 客户端密钥
|
||
DisplayName string // 显示名称,例如:轻雪通行证
|
||
Icon string // 图标url,为空则使用内置默认图标
|
||
OidcDiscoveryUrl string // OpenID自动发现URL,例如 :https://pass.liteyuki.icu/.well-known/openid-configuration
|
||
Enabled bool `gorm:"default:true"` // 是否启用
|
||
// 以下字段为自动获取字段,每次更新配置时自动填充
|
||
Issuer string
|
||
AuthorizationEndpoint string
|
||
TokenEndpoint string
|
||
UserInfoEndpoint string
|
||
JwksUri string
|
||
}
|
||
|
||
type oidcDiscoveryResp struct {
|
||
Issuer string `json:"issuer" validate:"required"`
|
||
AuthorizationEndpoint string `json:"authorization_endpoint" validate:"required"`
|
||
TokenEndpoint string `json:"token_endpoint" validate:"required"`
|
||
UserInfoEndpoint string `json:"userinfo_endpoint" validate:"required"`
|
||
JwksUri string `json:"jwks_uri" validate:"required"`
|
||
// 可选字段
|
||
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
|
||
ScopesSupported []string `json:"scopes_supported,omitempty"`
|
||
ResponseTypesSupported []string `json:"response_types_supported,omitempty"`
|
||
GrantTypesSupported []string `json:"grant_types_supported,omitempty"`
|
||
SubjectTypesSupported []string `json:"subject_types_supported,omitempty"`
|
||
IdTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported,omitempty"`
|
||
ClaimsSupported []string `json:"claims_supported,omitempty"`
|
||
EndSessionEndpoint string `json:"end_session_endpoint,omitempty"`
|
||
}
|
||
|
||
func updateOidcConfigFromUrl(url string) (*oidcDiscoveryResp, error) {
|
||
client := resty.New()
|
||
client.SetTimeout(10 * time.Second) // 设置超时时间
|
||
var discovery oidcDiscoveryResp
|
||
resp, err := client.R().
|
||
SetHeader("Accept", "application/json").
|
||
SetResult(&discovery).
|
||
Get(url)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("请求OIDC发现端点失败: %w", err)
|
||
}
|
||
if resp.StatusCode() != 200 {
|
||
return nil, fmt.Errorf("请求OIDC发现端点失败,状态码: %d", resp.StatusCode())
|
||
}
|
||
// 验证必要字段
|
||
if discovery.Issuer == "" ||
|
||
discovery.AuthorizationEndpoint == "" ||
|
||
discovery.TokenEndpoint == "" ||
|
||
discovery.UserInfoEndpoint == "" ||
|
||
discovery.JwksUri == "" {
|
||
return nil, fmt.Errorf("OIDC发现端点响应缺少必要字段")
|
||
}
|
||
return &discovery, nil
|
||
}
|
||
|
||
func (o *OidcConfig) BeforeSave(tx *gorm.DB) (err error) {
|
||
// 只有在创建新记录或更新 OidcDiscoveryUrl 字段时才更新端点信息
|
||
if tx.Statement.Changed("OidcDiscoveryUrl") {
|
||
discoveryResp, err := updateOidcConfigFromUrl(o.OidcDiscoveryUrl)
|
||
if err != nil {
|
||
return fmt.Errorf("更新OIDC配置失败: %w", err)
|
||
}
|
||
o.Issuer = discoveryResp.Issuer
|
||
o.AuthorizationEndpoint = discoveryResp.AuthorizationEndpoint
|
||
o.TokenEndpoint = discoveryResp.TokenEndpoint
|
||
o.UserInfoEndpoint = discoveryResp.UserInfoEndpoint
|
||
o.JwksUri = discoveryResp.JwksUri
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// ToUserDto 返回给用户侧
|
||
func (o *OidcConfig) ToUserDto() *dto.UserOidcConfigDto {
|
||
return &dto.UserOidcConfigDto{
|
||
Name: o.Name,
|
||
DisplayName: o.DisplayName,
|
||
Icon: o.Icon,
|
||
}
|
||
}
|
||
|
||
// ToAdminDto 返回给管理员侧
|
||
func (o *OidcConfig) ToAdminDto() *dto.AdminOidcConfigDto {
|
||
return &dto.AdminOidcConfigDto{
|
||
ID: o.ID,
|
||
Name: o.Name,
|
||
ClientID: o.ClientID,
|
||
ClientSecret: o.ClientSecret,
|
||
DisplayName: o.DisplayName,
|
||
Icon: o.Icon,
|
||
OidcDiscoveryUrl: o.OidcDiscoveryUrl,
|
||
Enabled: o.Enabled,
|
||
}
|
||
}
|