安全修复: - CRITICAL: SSO重定向URL注入漏洞 - 修复redirect_uri白名单验证 - HIGH: SSO ClientSecret未验证 - 使用crypto/subtle.ConstantTimeCompare验证 - HIGH: 邮件验证码熵值过低(3字节) - 提升到6字节(48位熵) - HIGH: 短信验证码熵值过低(4字节) - 提升到6字节 - HIGH: Goroutine使用已取消上下文 - auth_email.go使用独立context+超时 - HIGH: SQL LIKE查询注入风险 - permission/role仓库使用escapeLikePattern 新功能: - Go SDK: sdk/go/user-management/ 完整SDK实现 - CAS SSO框架: internal/auth/cas.go CAS协议支持 其他: - L1Cache实例问题修复 - AuthMiddleware共享l1Cache - 设备指纹XSS防护 - 内存存储替代localStorage - 响应格式协议中间件 - 导出无界查询修复
247 lines
6.0 KiB
Go
247 lines
6.0 KiB
Go
package userManagement
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
)
|
|
|
|
// LoginRequest 登录请求
|
|
type LoginRequest struct {
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
DeviceID string `json:"device_id,omitempty"`
|
|
DeviceName string `json:"device_name,omitempty"`
|
|
RememberMe bool `json:"remember_me"`
|
|
}
|
|
|
|
// LoginResponse 登录响应
|
|
type LoginResponse struct {
|
|
AccessToken string `json:"access_token"`
|
|
RefreshToken string `json:"refresh_token,omitempty"`
|
|
ExpiresIn int64 `json:"expires_in"`
|
|
TokenType string `json:"token_type"`
|
|
User *User `json:"user,omitempty"`
|
|
}
|
|
|
|
// RegisterRequest 注册请求
|
|
type RegisterRequest struct {
|
|
Username string `json:"username"`
|
|
Email string `json:"email"`
|
|
Password string `json:"password"`
|
|
Phone string `json:"phone,omitempty"`
|
|
Nickname string `json:"nickname,omitempty"`
|
|
}
|
|
|
|
// RefreshTokenRequest 刷新令牌请求
|
|
type RefreshTokenRequest struct {
|
|
RefreshToken string `json:"refresh_token"`
|
|
}
|
|
|
|
// CapabilitiesResponse 能力响应
|
|
type CapabilitiesResponse struct {
|
|
LoginMethods []string `json:"login_methods"`
|
|
SocialProviders []string `json:"social_providers,omitempty"`
|
|
CaptchaRequired bool `json:"captcha_required"`
|
|
SocialBindRequired bool `json:"social_bind_required,omitempty"`
|
|
}
|
|
|
|
// TwoFactorVerifyRequest 两因素验证请求
|
|
type TwoFactorVerifyRequest struct {
|
|
Code string `json:"code"`
|
|
DeviceID string `json:"device_id,omitempty"`
|
|
TrustDevice bool `json:"trust_device,omitempty"`
|
|
}
|
|
|
|
// PasswordResetRequest 密码重置请求
|
|
type PasswordResetRequest struct {
|
|
Token string `json:"token"`
|
|
NewPassword string `json:"new_password"`
|
|
}
|
|
|
|
// Login 执行登录
|
|
func (c *Client) Login(ctx context.Context, req *LoginRequest) (*LoginResponse, error) {
|
|
resp, err := c.doRequest(ctx, "POST", "/api/v1/auth/login", req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result LoginResponse
|
|
if err := c.parseResponse(resp, &result); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 自动设置 access token
|
|
if result.AccessToken != "" {
|
|
c.SetAccessToken(result.AccessToken)
|
|
}
|
|
|
|
return &result, nil
|
|
}
|
|
|
|
// Register 注册用户
|
|
func (c *Client) Register(ctx context.Context, req *RegisterRequest) (*User, error) {
|
|
resp, err := c.doRequest(ctx, "POST", "/api/v1/auth/register", req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result User
|
|
if err := c.parseResponse(resp, &result); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &result, nil
|
|
}
|
|
|
|
// GetCapabilities 获取登录能力
|
|
func (c *Client) GetCapabilities(ctx context.Context) (*CapabilitiesResponse, error) {
|
|
resp, err := c.doRequest(ctx, "GET", "/api/v1/auth/capabilities", nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result CapabilitiesResponse
|
|
if err := c.parseResponse(resp, &result); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &result, nil
|
|
}
|
|
|
|
// RefreshToken 刷新令牌
|
|
func (c *Client) RefreshToken(ctx context.Context, req *RefreshTokenRequest) (*LoginResponse, error) {
|
|
resp, err := c.doRequest(ctx, "POST", "/api/v1/auth/refresh", req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result LoginResponse
|
|
if err := c.parseResponse(resp, &result); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if result.AccessToken != "" {
|
|
c.SetAccessToken(result.AccessToken)
|
|
}
|
|
|
|
return &result, nil
|
|
}
|
|
|
|
// VerifyTwoFactor 验证两因素验证码
|
|
func (c *Client) VerifyTwoFactor(ctx context.Context, req *TwoFactorVerifyRequest) error {
|
|
resp, err := c.doRequest(ctx, "POST", "/api/v1/auth/2fa/verify", req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return c.parseResponse(resp, nil)
|
|
}
|
|
|
|
// Logout 登出
|
|
func (c *Client) Logout(ctx context.Context) error {
|
|
resp, err := c.doRequest(ctx, "POST", "/api/v1/auth/logout", nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
c.accessToken = ""
|
|
return c.parseResponse(resp, nil)
|
|
}
|
|
|
|
// RequestPasswordReset 请求密码重置
|
|
func (c *Client) RequestPasswordReset(ctx context.Context, email string) error {
|
|
resp, err := c.doRequest(ctx, "POST", "/api/v1/auth/password/reset", map[string]string{"email": email})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return c.parseResponse(resp, nil)
|
|
}
|
|
|
|
// ResetPassword 重置密码
|
|
func (c *Client) ResetPassword(ctx context.Context, req *PasswordResetRequest) error {
|
|
resp, err := c.doRequest(ctx, "POST", "/api/v1/auth/password/reset/confirm", req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return c.parseResponse(resp, nil)
|
|
}
|
|
|
|
// SendVerifyCode 发送验证码
|
|
func (c *Client) SendVerifyCode(ctx context.Context, phone string) error {
|
|
resp, err := c.doRequest(ctx, "POST", "/api/v1/auth/phone/send-code", map[string]string{"phone": phone})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return c.parseResponse(resp, nil)
|
|
}
|
|
|
|
// LoginWithPhone 手机号登录
|
|
func (c *Client) LoginWithPhone(ctx context.Context, phone, code string) (*LoginResponse, error) {
|
|
req := map[string]string{
|
|
"phone": phone,
|
|
"code": code,
|
|
}
|
|
resp, err := c.doRequest(ctx, "POST", "/api/v1/auth/login/phone", req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result LoginResponse
|
|
if err := c.parseResponse(resp, &result); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if result.AccessToken != "" {
|
|
c.SetAccessToken(result.AccessToken)
|
|
}
|
|
|
|
return &result, nil
|
|
}
|
|
|
|
// OAuthURL 获取 OAuth 授权 URL
|
|
func (c *Client) OAuthURL(provider string, redirectURI, state string) (string, error) {
|
|
params := map[string]string{
|
|
"provider": provider,
|
|
"redirect_uri": redirectURI,
|
|
}
|
|
if state != "" {
|
|
params["state"] = state
|
|
}
|
|
|
|
query := ""
|
|
for k, v := range params {
|
|
if query != "" {
|
|
query += "&"
|
|
}
|
|
query += k + "=" + v
|
|
}
|
|
|
|
return fmt.Sprintf("%s/api/v1/auth/oauth/authorize?%s", c.baseURL, query), nil
|
|
}
|
|
|
|
// HandleOAuthCallback 处理 OAuth 回调
|
|
func (c *Client) HandleOAuthCallback(ctx context.Context, provider, code string) (*LoginResponse, error) {
|
|
req := map[string]string{
|
|
"provider": provider,
|
|
"code": code,
|
|
}
|
|
resp, err := c.doRequest(ctx, "POST", "/api/v1/auth/oauth/callback", req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result LoginResponse
|
|
if err := c.parseResponse(resp, &result); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if result.AccessToken != "" {
|
|
c.SetAccessToken(result.AccessToken)
|
|
}
|
|
|
|
return &result, nil
|
|
}
|