68 lines
1.5 KiB
Go
68 lines
1.5 KiB
Go
package user
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"time"
|
||
|
||
"gin_test/event"
|
||
// repo 在本模块内,避免 user 模块依赖外部 model 层
|
||
|
||
"golang.org/x/crypto/bcrypt"
|
||
)
|
||
|
||
type Service struct {
|
||
repo *UserRepo
|
||
bus *event.Dispatcher
|
||
}
|
||
|
||
func NewService(repo *UserRepo, bus *event.Dispatcher) *Service {
|
||
return &Service{repo: repo, bus: bus}
|
||
}
|
||
|
||
func (s *Service) Register(ctx context.Context, username, password string) error {
|
||
if username == "" || password == "" {
|
||
return errors.New("username/password required")
|
||
}
|
||
|
||
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
return s.repo.Create(ctx, username, string(hash))
|
||
}
|
||
|
||
func (s *Service) Login(ctx context.Context, username, password string) (string, error) {
|
||
u, err := s.repo.GetByUsername(ctx, username)
|
||
if err != nil {
|
||
return "", errors.New("invalid username or password")
|
||
}
|
||
|
||
if err := bcrypt.CompareHashAndPassword([]byte(u.PasswordHash), []byte(password)); err != nil {
|
||
return "", errors.New("invalid username or password")
|
||
}
|
||
|
||
// 通过事件让 JWT 模块签发 token(request/reply)。
|
||
reply := make(chan event.TokenIssueResult, 1)
|
||
s.bus.Publish(event.TokenIssueRequested{
|
||
At: time.Now(),
|
||
Username: u.Username,
|
||
Reply: reply,
|
||
})
|
||
|
||
select {
|
||
case res := <-reply:
|
||
if res.Err != "" {
|
||
return "", errors.New(res.Err)
|
||
}
|
||
if res.Token == "" {
|
||
return "", errors.New("empty token")
|
||
}
|
||
return res.Token, nil
|
||
case <-time.After(2 * time.Second):
|
||
return "", errors.New("token issue timeout")
|
||
}
|
||
}
|
||
|