拆出 captcha
This commit is contained in:
parent
9147dc6b1d
commit
af024ef00e
@ -1,20 +1,21 @@
|
||||
package config
|
||||
|
||||
type Config struct {
|
||||
OfflineUUID bool `toml:"offlineUUID" comment:"为 true 则 uuid 生成方式于离线模式相同,若从离线模式切换不会丢失数据。\n已有用户数据的情况下勿更改此项"`
|
||||
Port string `toml:"port"`
|
||||
Log Log `toml:"log"`
|
||||
Sql Sql `toml:"sql"`
|
||||
Debug bool `toml:"debug" comment:"输出每条执行的 sql 语句"`
|
||||
Cache Cache `toml:"cache"`
|
||||
RaelIP bool `toml:"raelIP" comment:"位于反向代理后启用,用于记录真实 ip\n若直接提供服务,请勿打开,否则会被伪造 ip"`
|
||||
MaxIpUser int `toml:"maxIpUser" comment:"ip 段最大注册用户,ipv4 为 /24 ipv6 为 /48"`
|
||||
RsaPriKey string `toml:"rsaPriKey,multiline" comment:"运行后勿修改,若为集群需设置为一致"`
|
||||
TexturePath string `toml:"texturePath" comment:"材质文件保存路径,如果需要对象存储可以把对象储存挂载到本地目录上"`
|
||||
TextureBaseUrl string `toml:"textureBaseUrl" comment:"材质静态文件提供基础地址\n如果静态文件位于 oss 上,比如 https://s3.amazonaws.com/example/1.png\n则填写 https://s3.amazonaws.com/example \n若通过反向代理提供服务并启用了 https,请在在此处填写带有 https 的基础路径,否则游戏内无法加载皮肤"`
|
||||
WebBaseUrl string `toml:"webBaseUrl" comment:"用于在支持的启动器中展示本站的注册地址\n填写类似 https://example.com"`
|
||||
ServerName string `toml:"serverName" comment:"皮肤站名字,用于在多个地方展示"`
|
||||
Captcha Captcha `toml:"captcha"`
|
||||
OfflineUUID bool `toml:"offlineUUID" comment:"为 true 则 uuid 生成方式于离线模式相同,若从离线模式切换不会丢失数据。\n已有用户数据的情况下勿更改此项"`
|
||||
Port string `toml:"port"`
|
||||
Log Log `toml:"log"`
|
||||
Sql Sql `toml:"sql"`
|
||||
Debug bool `toml:"debug" comment:"输出每条执行的 sql 语句"`
|
||||
Cache Cache `toml:"cache"`
|
||||
RaelIP bool `toml:"raelIP" comment:"位于反向代理后启用,用于记录真实 ip\n若直接提供服务,请勿打开,否则会被伪造 ip"`
|
||||
MaxIpUser int `toml:"maxIpUser" comment:"ip 段最大注册用户,ipv4 为 /24 ipv6 为 /48"`
|
||||
RsaPriKey string `toml:"rsaPriKey,multiline" comment:"运行后勿修改,若为集群需设置为一致"`
|
||||
TexturePath string `toml:"texturePath" comment:"材质文件保存路径,如果需要对象存储可以把对象储存挂载到本地目录上"`
|
||||
TextureBaseUrl string `toml:"textureBaseUrl" comment:"材质静态文件提供基础地址\n如果静态文件位于 oss 上,比如 https://s3.amazonaws.com/example/1.png\n则填写 https://s3.amazonaws.com/example \n若通过反向代理提供服务并启用了 https,请在在此处填写带有 https 的基础路径,否则游戏内无法加载皮肤"`
|
||||
WebBaseUrl string `toml:"webBaseUrl" comment:"用于在支持的启动器中展示本站的注册地址\n填写类似 https://example.com"`
|
||||
ServerName string `toml:"serverName" comment:"皮肤站名字,用于在多个地方展示"`
|
||||
Captcha Captcha `toml:"captcha"`
|
||||
Email EmailConfig `toml:"email"`
|
||||
}
|
||||
|
||||
type Log struct {
|
||||
@ -40,6 +41,19 @@ type Captcha struct {
|
||||
Secret string `toml:"secret"`
|
||||
}
|
||||
|
||||
type EmailConfig struct {
|
||||
Enable bool `toml:"enable" comment:"注册验证邮件,且允许使用邮箱找回账号"`
|
||||
Smtp []SmtpUser `toml:"smtp"`
|
||||
}
|
||||
|
||||
type SmtpUser struct {
|
||||
Host string `toml:"host"`
|
||||
Port int `toml:"port"`
|
||||
SSL bool `toml:"SSL"`
|
||||
Name string `toml:"name"`
|
||||
Pass string `toml:"password"`
|
||||
}
|
||||
|
||||
func Default() Config {
|
||||
return Config{
|
||||
OfflineUUID: true,
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/xmdhs/authlib-skin/model"
|
||||
"github.com/xmdhs/authlib-skin/service"
|
||||
"github.com/xmdhs/authlib-skin/service/auth"
|
||||
"github.com/xmdhs/authlib-skin/service/captcha"
|
||||
)
|
||||
|
||||
func (h *Handel) handleErrorService(ctx context.Context, w http.ResponseWriter, err error) {
|
||||
@ -25,7 +26,7 @@ func (h *Handel) handleErrorService(ctx context.Context, w http.ResponseWriter,
|
||||
h.handleError(ctx, w, err.Error(), model.ErrRegLimit, 400, slog.LevelDebug)
|
||||
return
|
||||
}
|
||||
if errors.Is(err, service.ErrCaptcha) {
|
||||
if errors.Is(err, captcha.ErrCaptcha) {
|
||||
h.handleError(ctx, w, err.Error(), model.ErrCaptcha, 400, slog.LevelDebug)
|
||||
return
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ func ProvidePriKey(c config.Config) (*rsa.PrivateKey, error) {
|
||||
return a.GetKey(), nil
|
||||
}
|
||||
|
||||
func ProvidePubKey(pri *rsa.PrivateKey) (yggdrasil.PubRsaKey, error) {
|
||||
func ProvidePubKeyStr(pri *rsa.PrivateKey) (yggdrasil.PubRsaKey, error) {
|
||||
s, err := sign.NewAuthlibSignWithKey(pri).GetPKIXPubKeyWithOutRsa()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ProvidePubKey: %w", err)
|
||||
@ -103,8 +103,12 @@ func ProvidePubKey(pri *rsa.PrivateKey) (yggdrasil.PubRsaKey, error) {
|
||||
return yggdrasil.PubRsaKey(s), nil
|
||||
}
|
||||
|
||||
func ProvidePubKey(pri *rsa.PrivateKey) *rsa.PublicKey {
|
||||
return &pri.PublicKey
|
||||
}
|
||||
|
||||
func ProvideHttpClient() *http.Client {
|
||||
return &http.Client{}
|
||||
}
|
||||
|
||||
var Set = wire.NewSet(ProvideSlog, ProvideDB, ProvideEnt, ProvideValidate, ProvideCache, ProvidePriKey, ProvidePubKey, ProvideHttpClient)
|
||||
var Set = wire.NewSet(ProvideSlog, ProvideDB, ProvideEnt, ProvideValidate, ProvideCache, ProvidePriKey, ProvidePubKey, ProvidePubKeyStr, ProvideHttpClient)
|
||||
|
@ -12,13 +12,19 @@ import (
|
||||
"github.com/xmdhs/authlib-skin/handle/yggdrasil"
|
||||
"github.com/xmdhs/authlib-skin/server/route"
|
||||
"github.com/xmdhs/authlib-skin/service"
|
||||
"github.com/xmdhs/authlib-skin/service/auth"
|
||||
"github.com/xmdhs/authlib-skin/service/captcha"
|
||||
"github.com/xmdhs/authlib-skin/service/email"
|
||||
yggdrasilS "github.com/xmdhs/authlib-skin/service/yggdrasil"
|
||||
)
|
||||
|
||||
var serviceSet = wire.NewSet(service.NewWebService, yggdrasilS.NewYggdrasil, email.NewEmail, auth.NewAuthService,
|
||||
captcha.NewCaptchaService,
|
||||
)
|
||||
|
||||
func InitializeRoute(ctx context.Context, c config.Config) (*http.Server, func(), error) {
|
||||
panic(wire.Build(Set, route.NewRoute, NewSlog,
|
||||
NewServer, handle.NewHandel, yggdrasil.NewYggdrasil,
|
||||
service.NewWebService, yggdrasilS.NewYggdrasil, email.NewEmail,
|
||||
serviceSet,
|
||||
))
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ import (
|
||||
yggdrasil2 "github.com/xmdhs/authlib-skin/handle/yggdrasil"
|
||||
"github.com/xmdhs/authlib-skin/server/route"
|
||||
"github.com/xmdhs/authlib-skin/service"
|
||||
"github.com/xmdhs/authlib-skin/service/auth"
|
||||
"github.com/xmdhs/authlib-skin/service/email"
|
||||
"github.com/xmdhs/authlib-skin/service/yggdrasil"
|
||||
"net/http"
|
||||
)
|
||||
@ -43,8 +45,10 @@ func InitializeRoute(ctx context.Context, c config.Config) (*http.Server, func()
|
||||
cleanup()
|
||||
return nil, nil, err
|
||||
}
|
||||
yggdrasilYggdrasil := yggdrasil.NewYggdrasil(client, cache, c, privateKey)
|
||||
pubRsaKey, err := ProvidePubKey(privateKey)
|
||||
publicKey := ProvidePubKey(privateKey)
|
||||
authService := auth.NewAuthService(client, cache, publicKey, privateKey)
|
||||
yggdrasilYggdrasil := yggdrasil.NewYggdrasil(client, cache, c, privateKey, authService)
|
||||
pubRsaKey, err := ProvidePubKeyStr(privateKey)
|
||||
if err != nil {
|
||||
cleanup2()
|
||||
cleanup()
|
||||
@ -52,8 +56,14 @@ func InitializeRoute(ctx context.Context, c config.Config) (*http.Server, func()
|
||||
}
|
||||
yggdrasil3 := yggdrasil2.NewYggdrasil(logger, validate, yggdrasilYggdrasil, c, pubRsaKey)
|
||||
httpClient := ProvideHttpClient()
|
||||
webService := service.NewWebService(c, client, httpClient, cache, privateKey)
|
||||
handel := handle.NewHandel(webService, validate, c, logger)
|
||||
webService := service.NewWebService(c, client, httpClient, cache, privateKey, authService)
|
||||
emailEmail, err := email.NewEmail(privateKey, c, cache)
|
||||
if err != nil {
|
||||
cleanup2()
|
||||
cleanup()
|
||||
return nil, nil, err
|
||||
}
|
||||
handel := handle.NewHandel(webService, validate, c, logger, emailEmail)
|
||||
httpHandler := route.NewRoute(yggdrasil3, handel, c, handler)
|
||||
server, cleanup3 := NewServer(c, httpHandler)
|
||||
return server, func() {
|
||||
|
@ -1,84 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/xmdhs/authlib-skin/model"
|
||||
)
|
||||
|
||||
func (w *WebService) GetConfig(ctx context.Context) model.Config {
|
||||
return model.Config{
|
||||
Captcha: model.Captcha{
|
||||
Type: w.config.Captcha.Type,
|
||||
SiteKey: w.config.Captcha.SiteKey,
|
||||
},
|
||||
ServerName: w.config.ServerName,
|
||||
AllowChangeName: !w.config.OfflineUUID,
|
||||
}
|
||||
}
|
||||
|
||||
type turnstileRet struct {
|
||||
Success bool `json:"success"`
|
||||
ErrorCodes []string `json:"error-codes"`
|
||||
}
|
||||
|
||||
var ErrCaptcha = errors.New("验证码错误")
|
||||
|
||||
type ErrTurnstile struct {
|
||||
ErrorCodes []string
|
||||
}
|
||||
|
||||
func (e ErrTurnstile) Error() string {
|
||||
return strings.Join(e.ErrorCodes, " ")
|
||||
}
|
||||
|
||||
func (w *WebService) verifyCaptcha(ctx context.Context, token, ip string) error {
|
||||
if w.config.Captcha.Type != "turnstile" {
|
||||
return nil
|
||||
}
|
||||
bw := &bytes.Buffer{}
|
||||
err := json.NewEncoder(bw).Encode(turnstileResponse{
|
||||
Secret: w.config.Captcha.Secret,
|
||||
Response: token,
|
||||
Remoteip: ip,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("verifyTurnstile: %w", err)
|
||||
}
|
||||
reqs, err := http.NewRequestWithContext(ctx, "POST", "https://challenges.cloudflare.com/turnstile/v0/siteverify", bw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("verifyTurnstile: %w", err)
|
||||
}
|
||||
reqs.Header.Set("Accept", "*/*")
|
||||
reqs.Header.Set("Content-Type", "application/json")
|
||||
rep, err := w.httpClient.Do(reqs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("verifyTurnstile: %w", err)
|
||||
}
|
||||
defer rep.Body.Close()
|
||||
|
||||
var t turnstileRet
|
||||
err = json.NewDecoder(rep.Body).Decode(&t)
|
||||
if err != nil {
|
||||
return fmt.Errorf("verifyTurnstile: %w", err)
|
||||
}
|
||||
|
||||
if !t.Success {
|
||||
return fmt.Errorf("verifyTurnstile: %w", errors.Join(ErrTurnstile{
|
||||
ErrorCodes: t.ErrorCodes,
|
||||
}, ErrCaptcha))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type turnstileResponse struct {
|
||||
Secret string `json:"secret"`
|
||||
Response string `json:"response"`
|
||||
Remoteip string `json:"remoteip"`
|
||||
}
|
@ -33,13 +33,23 @@ type Email struct {
|
||||
cache cache.Cache
|
||||
}
|
||||
|
||||
func NewEmail(emailConfig []EmailConfig, pri *rsa.PrivateKey, config config.Config, cache cache.Cache) Email {
|
||||
return Email{
|
||||
emailConfig: emailConfig,
|
||||
func NewEmail(pri *rsa.PrivateKey, c config.Config, cache cache.Cache) (*Email, error) {
|
||||
ec := lo.Map[config.SmtpUser, EmailConfig](c.Email.Smtp, func(item config.SmtpUser, index int) EmailConfig {
|
||||
return EmailConfig{
|
||||
Host: item.Host,
|
||||
Port: item.Port,
|
||||
SSL: item.SSL,
|
||||
Name: item.Name,
|
||||
Pass: item.Pass,
|
||||
}
|
||||
})
|
||||
|
||||
return &Email{
|
||||
emailConfig: ec,
|
||||
pri: pri,
|
||||
config: config,
|
||||
config: c,
|
||||
cache: cache,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e Email) getRandEmailUser() EmailConfig {
|
||||
|
@ -33,7 +33,7 @@ func (w *WebService) Reg(ctx context.Context, u model.UserReg, ipPrefix, ip stri
|
||||
userUuid = strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
}
|
||||
|
||||
err := w.verifyCaptcha(ctx, u.CaptchaToken, ip)
|
||||
err := w.captchaService.VerifyCaptcha(ctx, u.CaptchaToken, ip)
|
||||
if err != nil {
|
||||
return model.LoginRep{}, fmt.Errorf("Reg: %w", err)
|
||||
}
|
||||
@ -109,7 +109,7 @@ func (w *WebService) Reg(ctx context.Context, u model.UserReg, ipPrefix, ip stri
|
||||
}
|
||||
|
||||
func (w *WebService) Login(ctx context.Context, l model.Login, ip string) (model.LoginRep, error) {
|
||||
err := w.verifyCaptcha(ctx, l.CaptchaToken, ip)
|
||||
err := w.captchaService.VerifyCaptcha(ctx, l.CaptchaToken, ip)
|
||||
if err != nil {
|
||||
return model.LoginRep{}, fmt.Errorf("Login: %w", err)
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"github.com/xmdhs/authlib-skin/db/ent/migrate"
|
||||
"github.com/xmdhs/authlib-skin/model"
|
||||
"github.com/xmdhs/authlib-skin/service/auth"
|
||||
"github.com/xmdhs/authlib-skin/service/captcha"
|
||||
)
|
||||
|
||||
var webService *WebService
|
||||
@ -36,7 +37,7 @@ func initWebService(ctx context.Context) func() {
|
||||
lo.Must0(c.Schema.Create(context.Background(), migrate.WithForeignKeys(false), migrate.WithDropIndex(true), migrate.WithDropColumn(true)))
|
||||
rsa4 := lo.Must(rsa.GenerateKey(rand.Reader, 4096))
|
||||
cache := cache.NewFastCache(100000)
|
||||
webService = NewWebService(config.Default(), c, &http.Client{}, cache, rsa4, auth.NewAuthService(c, cache, &rsa4.PublicKey, rsa4))
|
||||
webService = NewWebService(config.Default(), c, &http.Client{}, cache, rsa4, auth.NewAuthService(c, cache, &rsa4.PublicKey, rsa4), captcha.NewCaptchaService(config.Default(), &http.Client{}))
|
||||
|
||||
return func() {
|
||||
c.User.Delete().Exec(ctx)
|
||||
|
@ -9,28 +9,32 @@ import (
|
||||
"github.com/xmdhs/authlib-skin/config"
|
||||
"github.com/xmdhs/authlib-skin/db/cache"
|
||||
"github.com/xmdhs/authlib-skin/db/ent"
|
||||
"github.com/xmdhs/authlib-skin/model"
|
||||
"github.com/xmdhs/authlib-skin/service/auth"
|
||||
"github.com/xmdhs/authlib-skin/service/captcha"
|
||||
"github.com/xmdhs/authlib-skin/utils"
|
||||
)
|
||||
|
||||
type WebService struct {
|
||||
config config.Config
|
||||
client *ent.Client
|
||||
httpClient *http.Client
|
||||
cache cache.Cache
|
||||
prikey *rsa.PrivateKey
|
||||
authService *auth.AuthService
|
||||
config config.Config
|
||||
client *ent.Client
|
||||
httpClient *http.Client
|
||||
cache cache.Cache
|
||||
prikey *rsa.PrivateKey
|
||||
authService *auth.AuthService
|
||||
captchaService *captcha.CaptchaService
|
||||
}
|
||||
|
||||
func NewWebService(c config.Config, e *ent.Client, hc *http.Client,
|
||||
cache cache.Cache, prikey *rsa.PrivateKey, authService *auth.AuthService) *WebService {
|
||||
cache cache.Cache, prikey *rsa.PrivateKey, authService *auth.AuthService, captchaService *captcha.CaptchaService) *WebService {
|
||||
return &WebService{
|
||||
config: c,
|
||||
client: e,
|
||||
httpClient: hc,
|
||||
cache: cache,
|
||||
prikey: prikey,
|
||||
authService: authService,
|
||||
config: c,
|
||||
client: e,
|
||||
httpClient: hc,
|
||||
cache: cache,
|
||||
prikey: prikey,
|
||||
authService: authService,
|
||||
captchaService: captchaService,
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,3 +44,14 @@ func (w *WebService) validatePass(ctx context.Context, u *ent.User, password str
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WebService) GetConfig(ctx context.Context) model.Config {
|
||||
return model.Config{
|
||||
Captcha: model.Captcha{
|
||||
Type: w.config.Captcha.Type,
|
||||
SiteKey: w.config.Captcha.SiteKey,
|
||||
},
|
||||
ServerName: w.config.ServerName,
|
||||
AllowChangeName: !w.config.OfflineUUID,
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user