拆出 captcha
This commit is contained in:
parent
9147dc6b1d
commit
af024ef00e
@ -15,6 +15,7 @@ type Config struct {
|
|||||||
WebBaseUrl string `toml:"webBaseUrl" comment:"用于在支持的启动器中展示本站的注册地址\n填写类似 https://example.com"`
|
WebBaseUrl string `toml:"webBaseUrl" comment:"用于在支持的启动器中展示本站的注册地址\n填写类似 https://example.com"`
|
||||||
ServerName string `toml:"serverName" comment:"皮肤站名字,用于在多个地方展示"`
|
ServerName string `toml:"serverName" comment:"皮肤站名字,用于在多个地方展示"`
|
||||||
Captcha Captcha `toml:"captcha"`
|
Captcha Captcha `toml:"captcha"`
|
||||||
|
Email EmailConfig `toml:"email"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Log struct {
|
type Log struct {
|
||||||
@ -40,6 +41,19 @@ type Captcha struct {
|
|||||||
Secret string `toml:"secret"`
|
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 {
|
func Default() Config {
|
||||||
return Config{
|
return Config{
|
||||||
OfflineUUID: true,
|
OfflineUUID: true,
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/xmdhs/authlib-skin/model"
|
"github.com/xmdhs/authlib-skin/model"
|
||||||
"github.com/xmdhs/authlib-skin/service"
|
"github.com/xmdhs/authlib-skin/service"
|
||||||
"github.com/xmdhs/authlib-skin/service/auth"
|
"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) {
|
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)
|
h.handleError(ctx, w, err.Error(), model.ErrRegLimit, 400, slog.LevelDebug)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if errors.Is(err, service.ErrCaptcha) {
|
if errors.Is(err, captcha.ErrCaptcha) {
|
||||||
h.handleError(ctx, w, err.Error(), model.ErrCaptcha, 400, slog.LevelDebug)
|
h.handleError(ctx, w, err.Error(), model.ErrCaptcha, 400, slog.LevelDebug)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ func ProvidePriKey(c config.Config) (*rsa.PrivateKey, error) {
|
|||||||
return a.GetKey(), nil
|
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()
|
s, err := sign.NewAuthlibSignWithKey(pri).GetPKIXPubKeyWithOutRsa()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("ProvidePubKey: %w", err)
|
return "", fmt.Errorf("ProvidePubKey: %w", err)
|
||||||
@ -103,8 +103,12 @@ func ProvidePubKey(pri *rsa.PrivateKey) (yggdrasil.PubRsaKey, error) {
|
|||||||
return yggdrasil.PubRsaKey(s), nil
|
return yggdrasil.PubRsaKey(s), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ProvidePubKey(pri *rsa.PrivateKey) *rsa.PublicKey {
|
||||||
|
return &pri.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
func ProvideHttpClient() *http.Client {
|
func ProvideHttpClient() *http.Client {
|
||||||
return &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/handle/yggdrasil"
|
||||||
"github.com/xmdhs/authlib-skin/server/route"
|
"github.com/xmdhs/authlib-skin/server/route"
|
||||||
"github.com/xmdhs/authlib-skin/service"
|
"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"
|
"github.com/xmdhs/authlib-skin/service/email"
|
||||||
yggdrasilS "github.com/xmdhs/authlib-skin/service/yggdrasil"
|
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) {
|
func InitializeRoute(ctx context.Context, c config.Config) (*http.Server, func(), error) {
|
||||||
panic(wire.Build(Set, route.NewRoute, NewSlog,
|
panic(wire.Build(Set, route.NewRoute, NewSlog,
|
||||||
NewServer, handle.NewHandel, yggdrasil.NewYggdrasil,
|
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"
|
yggdrasil2 "github.com/xmdhs/authlib-skin/handle/yggdrasil"
|
||||||
"github.com/xmdhs/authlib-skin/server/route"
|
"github.com/xmdhs/authlib-skin/server/route"
|
||||||
"github.com/xmdhs/authlib-skin/service"
|
"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"
|
"github.com/xmdhs/authlib-skin/service/yggdrasil"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
@ -43,8 +45,10 @@ func InitializeRoute(ctx context.Context, c config.Config) (*http.Server, func()
|
|||||||
cleanup()
|
cleanup()
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
yggdrasilYggdrasil := yggdrasil.NewYggdrasil(client, cache, c, privateKey)
|
publicKey := ProvidePubKey(privateKey)
|
||||||
pubRsaKey, err := ProvidePubKey(privateKey)
|
authService := auth.NewAuthService(client, cache, publicKey, privateKey)
|
||||||
|
yggdrasilYggdrasil := yggdrasil.NewYggdrasil(client, cache, c, privateKey, authService)
|
||||||
|
pubRsaKey, err := ProvidePubKeyStr(privateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cleanup2()
|
cleanup2()
|
||||||
cleanup()
|
cleanup()
|
||||||
@ -52,8 +56,14 @@ func InitializeRoute(ctx context.Context, c config.Config) (*http.Server, func()
|
|||||||
}
|
}
|
||||||
yggdrasil3 := yggdrasil2.NewYggdrasil(logger, validate, yggdrasilYggdrasil, c, pubRsaKey)
|
yggdrasil3 := yggdrasil2.NewYggdrasil(logger, validate, yggdrasilYggdrasil, c, pubRsaKey)
|
||||||
httpClient := ProvideHttpClient()
|
httpClient := ProvideHttpClient()
|
||||||
webService := service.NewWebService(c, client, httpClient, cache, privateKey)
|
webService := service.NewWebService(c, client, httpClient, cache, privateKey, authService)
|
||||||
handel := handle.NewHandel(webService, validate, c, logger)
|
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)
|
httpHandler := route.NewRoute(yggdrasil3, handel, c, handler)
|
||||||
server, cleanup3 := NewServer(c, httpHandler)
|
server, cleanup3 := NewServer(c, httpHandler)
|
||||||
return server, func() {
|
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
|
cache cache.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEmail(emailConfig []EmailConfig, pri *rsa.PrivateKey, config config.Config, cache cache.Cache) Email {
|
func NewEmail(pri *rsa.PrivateKey, c config.Config, cache cache.Cache) (*Email, error) {
|
||||||
return Email{
|
ec := lo.Map[config.SmtpUser, EmailConfig](c.Email.Smtp, func(item config.SmtpUser, index int) EmailConfig {
|
||||||
emailConfig: emailConfig,
|
return EmailConfig{
|
||||||
pri: pri,
|
Host: item.Host,
|
||||||
config: config,
|
Port: item.Port,
|
||||||
cache: cache,
|
SSL: item.SSL,
|
||||||
|
Name: item.Name,
|
||||||
|
Pass: item.Pass,
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return &Email{
|
||||||
|
emailConfig: ec,
|
||||||
|
pri: pri,
|
||||||
|
config: c,
|
||||||
|
cache: cache,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e Email) getRandEmailUser() EmailConfig {
|
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(), "-", "")
|
userUuid = strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := w.verifyCaptcha(ctx, u.CaptchaToken, ip)
|
err := w.captchaService.VerifyCaptcha(ctx, u.CaptchaToken, ip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.LoginRep{}, fmt.Errorf("Reg: %w", err)
|
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) {
|
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 {
|
if err != nil {
|
||||||
return model.LoginRep{}, fmt.Errorf("Login: %w", err)
|
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/db/ent/migrate"
|
||||||
"github.com/xmdhs/authlib-skin/model"
|
"github.com/xmdhs/authlib-skin/model"
|
||||||
"github.com/xmdhs/authlib-skin/service/auth"
|
"github.com/xmdhs/authlib-skin/service/auth"
|
||||||
|
"github.com/xmdhs/authlib-skin/service/captcha"
|
||||||
)
|
)
|
||||||
|
|
||||||
var webService *WebService
|
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)))
|
lo.Must0(c.Schema.Create(context.Background(), migrate.WithForeignKeys(false), migrate.WithDropIndex(true), migrate.WithDropColumn(true)))
|
||||||
rsa4 := lo.Must(rsa.GenerateKey(rand.Reader, 4096))
|
rsa4 := lo.Must(rsa.GenerateKey(rand.Reader, 4096))
|
||||||
cache := cache.NewFastCache(100000)
|
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() {
|
return func() {
|
||||||
c.User.Delete().Exec(ctx)
|
c.User.Delete().Exec(ctx)
|
||||||
|
@ -9,7 +9,9 @@ import (
|
|||||||
"github.com/xmdhs/authlib-skin/config"
|
"github.com/xmdhs/authlib-skin/config"
|
||||||
"github.com/xmdhs/authlib-skin/db/cache"
|
"github.com/xmdhs/authlib-skin/db/cache"
|
||||||
"github.com/xmdhs/authlib-skin/db/ent"
|
"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/auth"
|
||||||
|
"github.com/xmdhs/authlib-skin/service/captcha"
|
||||||
"github.com/xmdhs/authlib-skin/utils"
|
"github.com/xmdhs/authlib-skin/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,10 +22,11 @@ type WebService struct {
|
|||||||
cache cache.Cache
|
cache cache.Cache
|
||||||
prikey *rsa.PrivateKey
|
prikey *rsa.PrivateKey
|
||||||
authService *auth.AuthService
|
authService *auth.AuthService
|
||||||
|
captchaService *captcha.CaptchaService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWebService(c config.Config, e *ent.Client, hc *http.Client,
|
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{
|
return &WebService{
|
||||||
config: c,
|
config: c,
|
||||||
client: e,
|
client: e,
|
||||||
@ -31,6 +34,7 @@ func NewWebService(c config.Config, e *ent.Client, hc *http.Client,
|
|||||||
cache: cache,
|
cache: cache,
|
||||||
prikey: prikey,
|
prikey: prikey,
|
||||||
authService: authService,
|
authService: authService,
|
||||||
|
captchaService: captchaService,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,3 +44,14 @@ func (w *WebService) validatePass(ctx context.Context, u *ent.User, password str
|
|||||||
}
|
}
|
||||||
return nil
|
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