Skip to content

零散小工具合集:bcrypt 密码哈希、mail 邮件发送、sms 短信发送、ip 客户端 IP 识别、useragent UA 解析、strutil 字符串工具、 i18n 国际化。各自职责单一,无横向依赖。

关键导出

  • bcrypt.Hashpw(password) (string, error) / HashpwWithCost(password, cost int) / Verify(plaintext, hashed) error / Checkpw(plaintext, hashed) bool
  • bcrypt.DefaultCost=10 / MaxPasswordSize=72;错误 ErrPasswordMismatch / ErrPasswordTooLong
  • mail.Mailer / New(cfg) *Mailer / Init() / Enabled() bool / Send(ctx, to, subject, body) error;错误 ErrDisabled
  • sms.Sender interface{ Send(ctx, phone, params map[string]string) error }
  • sms.Init() / Enabled() bool / SendCode(ctx, phone, code) error / SetSender(s) (restore func());错误 ErrDisabled
  • ip.ClientIP(r *http.Request) string
  • useragent.Parse(ua string) (browser, os string)
  • strutil.Capitalize(s string) string / IsHTTP(link string) bool
  • i18n.Locale / LocaleZhCN / LocaleEnUS / DefaultLocale=LocaleZhCN
  • i18n.Parse(tag string) (Locale, bool) / NewContext(ctx, loc) / FromContext(ctx) Locale
  • i18n.Msg(ctx, code string, args ...any) string / MsgLocale(loc, code, args...) string

典型用法

go
// internal/auth/service/password_auth_strategy.go:53
return bcrypt.Checkpw(loginBody.Password, user.Password)

// internal/resource/handler/captcha_handler.go:55
if err := sms.SendCode(ctx, phoneNumber, code); err != nil { /* ... */ }

// mail
mail.Send(ctx, to, "登录验证码", code)

// internal/auth/service/sys_login_service.go:50,56
loginUser.IPAddr = ip.ClientIP(req)
browser, osName := useragent.Parse(req.Header.Get("User-Agent"))

// internal/auth/handler/auth_handler.go:75
c.JSON(http.StatusOK, response.OkMsg(i18n.Msg(c.Request.Context(), "user.logout.success")))

坑/约定

WARNING

  • bcrypt Go 标准库把盐折进哈希串一次性返回,没有独立产盐 API(不像 hutool 的 gensalt);密码超 72 字节返回 ErrPasswordTooLongVerify 区分「密码不匹配」与「哈希格式非法」两类错误。
  • mail 只做纯文本单收件人(唯一场景是登录验证码,引第三方库不划算);SSL(465)连上来就握手,STARTTLS(587)先明文再升级,两种加密方式不能混(对着 465 发 STARTTLS 会卡住);主题按 RFC 2047 编码(中文主题直接塞会乱码);正文 \n 统一成 \r\n;ctx 只用于取消建连(SMTP 会话本身不可中途取消)。
  • sms 模板变量名固定 code(阿里云模板 ${code});未开启时 defaultSender=nilSendCode 返回 ErrDisabled ;换/加厂商只需再写一个 Sender,调用方不动;SetSender 仅供测试注入。
  • ip 所有代理头都是可伪造的,依赖 nginx 覆写;X-Forwarded-For 是逗号分隔链路,取第一段才是最初客户端;剥掉 IPv6 字面量的方括号;RemoteAddr 带端口且 IPv6 形如 [::1]:8080
  • useragent 底层用 github.com/mssola/useragent;只返回名字不含版本(登录日志只记名字)。
  • strutil Capitalize 按 rune 处理(UTF-8 安全);IsHTTP 有意不用 strings.HasPrefix 的模糊匹配,非 http 协议进来只会拼出脏路径。
  • i18n 词条缺失时返回 code 本身(不报错)——故键拼错不会有编译错误,enum.LoginTypeTestLoginTypeKeysExistInCatalog 把键与词条表钉在一起;回落链:精确语言 → 语言级(zh→zh-cn)→ DefaultLocaleParse 列表形态取第一段,下划线归一为连字符,只裁空格/制表符(挡 \r\n),长度上限 32;语言由 middleware.I18n 写进 gin.Contextrequest.Context()。 :::

相关页:/modules/auth/modules/resource/pkg-reference/middleware

基于 MIT 协议开源