service.xpcool.com/internal/controller/admin/auth.go
夏犀麟 06484774ad feat(auth): 登录密码 RSA+AES 混合加密传输与加密字段支持
- 新增 internal/library/crypto:RSA-OAEP(SHA-256)+AES-256-GCM 混合解密,
  私钥按 配置PEM > data/crypto/rsa_private.pem > 自动生成 三级来源,公钥 SPKI 输出
- 公开接口 POST /system/auth/public-key;LoginReq 支持 encryptedKey/encryptedData
- AdminCreate/AdminResetPwd 密码字段支持加密传输(allowPlain 仅开发联调)
- 配置 encrypt.privateKey/allowPlain;data/ 加入 gitignore 防止私钥入库
2026-08-27 23:43:41 +08:00

74 lines
3.4 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package admin
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
authv1 "service.xpcool.com/api/admin/admin/login"
"service.xpcool.com/internal/model/dto"
auth "service.xpcool.com/internal/service/admin/admin/login"
loginlog "service.xpcool.com/internal/service/admin/system/login_log"
)
// AuthController 仅暴露公开的登录相关端点。
type AuthController struct{}
// NewAuth 创建公开的管理端认证控制器(仅登录)。
func NewAuth() *AuthController { return &AuthController{} }
// Login 校验管理员身份并签发令牌对,成功与失败均写入登录日志。
// 密码采用「RSA + AES-GCM」混合加密传输先解出明文用户名/密码再走原校验逻辑。
func (c *AuthController) Login(ctx context.Context, req *authv1.LoginReq) (res *authv1.LoginRes, err error) {
// 从请求上下文提取来源信息用于登录审计
r := ghttp.RequestFromCtx(ctx)
ip, ua := "", ""
if r != nil {
ip = r.GetClientIp()
ua = r.Header.Get("User-Agent")
}
// 解析登录凭据密文encryptedKey/encryptedData优先明文仅开发环境 allowPlain 时可用。
username, password, err := auth.AdminAuth().ResolveLogin(ctx, req.EncryptedKey, req.EncryptedData, req.Username, req.Password)
if err != nil {
_ = loginlog.AdminLoginLog().Record(ctx, loginlog.LoginEvent{Username: "(encrypted)", IP: ip, UserAgent: ua, Status: 0, FailReason: err.Error()})
return nil, err
}
p, id, err := auth.AdminAuth().Login(ctx, dto.AdminLoginInput{Username: username, Password: password})
if err != nil {
// 登录失败也落库(含失败原因),便于排查异常登录;日志失败不回传
_ = loginlog.AdminLoginLog().Record(ctx, loginlog.LoginEvent{Username: username, IP: ip, UserAgent: ua, Status: 0, FailReason: err.Error()})
return nil, err
}
// 登录成功落库;日志落库失败不影响登录结果
_ = loginlog.AdminLoginLog().Record(ctx, loginlog.LoginEvent{Username: username, IP: ip, UserAgent: ua, Status: 1})
return &authv1.LoginRes{AccessToken: p.AccessToken, RefreshToken: p.RefreshToken, ExpiresIn: p.ExpiresIn, AdminID: id}, nil
}
// PublicKey 返回登录加密 RSA 公钥(公开端点,前端登录前获取用于混合加密密码)。
func (c *AuthController) PublicKey(ctx context.Context, _ *authv1.PublicKeyReq) (res *authv1.PublicKeyRes, err error) {
pub, err := auth.AdminAuth().PublicKey(ctx)
if err != nil {
return nil, err
}
return &authv1.PublicKeyRes{PublicKey: pub}, nil
}
// Refresh 使用有效刷新令牌轮换管理员令牌对。
func (c *AuthController) Refresh(ctx context.Context, req *authv1.RefreshReq) (res *authv1.RefreshRes, err error) {
p, id, err := auth.AdminAuth().Refresh(ctx, req.RefreshToken)
if err != nil {
return nil, err
}
return &authv1.RefreshRes{AccessToken: p.AccessToken, RefreshToken: p.RefreshToken, ExpiresIn: p.ExpiresIn, AdminID: id}, nil
}
// Logout 结束管理员会话。无状态 JWT 的 access token 无法立即作废(等自然过期),
// 但携带 refreshToken 时可撤销刷新会话,阻止该设备继续续期。
// 幂等设计:即使令牌无效也返回成功,保证前端登出流程不阻塞。
func (c *AuthController) Logout(ctx context.Context, req *authv1.LogoutReq) (res *authv1.LogoutRes, err error) {
if req.RefreshToken != "" {
_ = auth.AdminAuth().Revoke(ctx, req.RefreshToken)
}
return &authv1.LogoutRes{}, nil
}