fix: 生产安全修复 + Go SDK + CAS SSO框架
安全修复: - 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 - 响应格式协议中间件 - 导出无界查询修复
This commit is contained in:
246
sdk/go/user-management/auth.go
Normal file
246
sdk/go/user-management/auth.go
Normal file
@@ -0,0 +1,246 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user