Skip to content

Commit

Permalink
feat: improve logic in the starbook (#135)
Browse files Browse the repository at this point in the history
  • Loading branch information
oldme-git authored Jan 2, 2025
1 parent bd38409 commit 6feaf2b
Show file tree
Hide file tree
Showing 23 changed files with 529 additions and 449 deletions.
34 changes: 18 additions & 16 deletions docs/course/starbook/第三章-会话管理/3.2.登录.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,20 @@ type LoginRes struct {
## 编写Logic
---
登录逻辑的的难点在于生成`Token`。准备好一个随机字符串`JwtKey`用作签名,我们将其定义在`utility`目录下
登录逻辑的的难点在于生成`Token`。准备好一个随机字符串`JwtKey`用作签名。

*utility/jwt.go*
*internal/consts/consts.go*
```go
package utility
package consts

var JwtKey = []byte("db03d23b03ec405793b38f10592a2f34")
const (
JwtKey = "db03d23b03ec405793b38f10592a2f34"
)
```

编写核心逻辑,先根据用户名进行`Where`查询,获取到数据后,将密码再次加密,如果和数据库中的密文一致则说明是合法用户,生成`Token`返回。

*internal/logic/users/account.go*
*internal/logic/users/users_account.go*
```go
package users

Expand All @@ -56,42 +58,42 @@ import (
"star/utility"
)

type UserClaims struct {
type jwtClaims struct {
Id uint
Username string
jwt.RegisteredClaims
}

func Login(ctx context.Context, username, password string) (tokenString string, err error) {
func (u *Users) Login(ctx context.Context, username, password string) (tokenString string, err error) {
var user entity.Users
err = dao.Users.Ctx(ctx).Where("username", username).Scan(&user)
if err != nil {
return "", errors.New("用户名或密码错误")
return "", gerror.New("用户名或密码错误")
}

if user.Id == 0 {
return "", errors.New("用户不存在")
return "", gerror.New("用户不存在")
}

// 将密码加密后与数据库中的密码进行比对
if user.Password != encryptPassword(password) {
return "", errors.New("用户名或密码错误")
if user.Password != u.encryptPassword(password) {
return "", gerror.New("用户名或密码错误")
}

// 生成token
userClaims := &UserClaims{
uc := &jwtClaims{
Id: user.Id,
Username: user.Username,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(6 * time.Hour)),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, userClaims)
return token.SignedString(utility.JwtKey)
token := jwt.NewWithClaims(jwt.SigningMethodHS256, uc)
return token.SignedString(consts.JwtKey)
}
```

从上面的代码可以看到,我们需要声明一个结构体`UserClaims`保存签名信息,这里保存了`Id``Username`并设置了`Token`有效期为 6 个小时。最后的声明对象还需要调用`SignedString`方法传入`JwtKey`生成签名。
从上面的代码可以看到,我们需要声明一个结构体`userClaims`保存签名信息,这里保存了`Id``Username`并设置了`Token`有效期为 6 个小时。最后的声明对象还需要调用`SignedString`方法传入`JwtKey`生成签名。

## Controller调用Logic
---
Expand All @@ -107,7 +109,7 @@ import (
)

func (c *ControllerV1) Login(ctx context.Context, req *v1.LoginReq) (res *v1.LoginRes, err error) {
token, err := users.Login(ctx, req.Username, req.Password)
token, err := c.users.Login(ctx, req.Username, req.Password)
if err != nil {
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ type InfoRes struct {

## 编写Logic
---
*internal/logic/users/account.go*
*internal/logic/users/users_account.go*
```go
package users

Expand All @@ -92,15 +92,13 @@ import (

...

func Info(ctx context.Context) (user *entity.Users, err error) {
user = new(entity.Users)
func (u *Users) Info(ctx context.Context) (user *entity.Users, err error) {
tokenString := g.RequestFromCtx(ctx).Request.Header.Get("Authorization")

tokenClaims, _ := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) {
return utility.JwtKey, nil
tokenClaims, _ := jwt.ParseWithClaims(tokenString, &jwtClaims{}, func(token *jwt.Token) (interface{}, error) {
return consts.JwtKey, nil
})

if claims, ok := tokenClaims.Claims.(*UserClaims); ok && tokenClaims.Valid {
if claims, ok := tokenClaims.Claims.(*jwtClaims); ok && tokenClaims.Valid {
err = dao.Users.Ctx(ctx).Where("id", claims.Id).Scan(&user)
}
return
Expand All @@ -113,29 +111,51 @@ func Info(ctx context.Context) (user *entity.Users, err error) {

## Controller调用Logic
---
同样将`logic`注册到控制器中。

*internal/controller/account/account_new.go*
```go
...

package account

import (
"star/api/account"
"star/internal/logic/users"
)

type ControllerV1 struct {
users *users.Users
}

func NewV1() account.IAccountV1 {
return &ControllerV1{
users: users.New(),
}
}
```

*internal/controller/account/account_v1_info.go*
```go
package account

import (
"context"

"star/api/account/v1"
"star/internal/logic/users"
)

func (c *ControllerV1) Info(ctx context.Context, req *v1.InfoReq) (res *v1.InfoRes, err error) {
user, err := users.Info(ctx)
user, err := c.users.Info(ctx)
if err != nil {
return nil, err
return nil, err
}
return &v1.InfoRes{
Username: user.Username,
Email: user.Email,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
}, nil
return
Username: user.Username,
Email: user.Email,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
}, nil
}
```

Expand Down
47 changes: 44 additions & 3 deletions docs/course/starbook/第二章-用户注册/2.3.注册接口.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,23 @@ done!
---
`Logic` 是业务逻辑层,存放在`internal/logic`下,供`Controller`调用从而实现具体的业务逻辑。

*internal/logic/users/register.go*
定义一个`Users`对象,并新建一个`New`函数用作实例化它:

*internal/logic/users/users.go*
```go
package users

type Users struct {
}

func New() *Users {
return &Users{}
}
```

编写注册方法:

*internal/logic/users/users_register.go*
```go
package users

Expand All @@ -58,7 +74,7 @@ import (
"star/internal/model/do"
)

func Register(ctx context.Context, username, password, email string) error {
func (u *Users) Register(ctx context.Context, username, password, email string) error {
_, err := dao.Users.Ctx(ctx).Data(do.Users{
Username: username,
Password: password,
Expand All @@ -77,6 +93,31 @@ func Register(ctx context.Context, username, password, email string) error {
---
`Controller` 层负责接收 `Req` 请求对象后调用一个或多个`Logic`完成业务逻辑,一些简单的逻辑也可以直接放在`Controller`中处理。处理完成后的结果封装在约定的 `Res` 数据结构中返回。这里的`Res`数据结构为空,返回`nil`即可。

`Users`对象封装到控制器中,方便后续调用。

*internal/controller/users/users_new.go*
```go
...

package users

import (
"star/api/users"
userLogic "star/internal/logic/users"
)

type ControllerV1 struct {
users *userLogic.Users
}

func NewV1() users.IUsersV1 {
return &ControllerV1{
users: userLogic.New(),
}
}
```


*internal/controller/users/users_v1_register.go*
```go
package users
Expand All @@ -90,7 +131,7 @@ import (
)

func (c *ControllerV1) Register(ctx context.Context, req *v1.RegisterReq) (res *v1.RegisterRes, err error) {
err = users.Register(ctx, req.Username, req.Password, req.Email)
err = c.users.Register(ctx, req.Username, req.Password, req.Email)
return nil, err
}
```
Expand Down
62 changes: 35 additions & 27 deletions docs/course/starbook/第二章-用户注册/2.4.业务优化.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,14 @@ $ curl -X POST http://127.0.0.1:8000/v1/users/register -H "Content-Type: applica
---
用户名是登录的重要依据,如果碰巧系统中有两个同名用户,则会出现重大的逻辑混乱。所以我们需要在数据入库前查询该用户是否存在,如果存在,则返回错误信息,提示用户已经存在。

*internal/logic/users/register.go*
*internal/logic/users/users_register.go*
```go
package users

...

func Register(ctx context.Context, username, password, email string) error {
if err := checkUser(ctx, username); err != nil {
func (u *Users) Register(ctx context.Context, username, password, email string) error {
if err := u.checkUser(ctx, username); err != nil {
return err
}

Expand All @@ -169,7 +169,7 @@ func Register(ctx context.Context, username, password, email string) error {
return nil
}

func checkUser(ctx context.Context, username string) error {
func (u *Users) checkUser(ctx context.Context, username string) error {
count, err := dao.Users.Ctx(ctx).Where("username", username).Count()
if err != nil {
return err
Expand Down Expand Up @@ -201,33 +201,35 @@ ALTER TABLE users ADD UNIQUE (username);
---
密码明文保存是一种非常不安全的行为,通常的做法是对其`hash`计算后存入数据库,例如`md5``SHA-1`等。

新增一个函数`encryptPassword`实现密码加密功能。
新增一个方法`encryptPassword`实现密码加密功能。

*internal/logic/users/utility.go*
*internal/logic/users/users.go*
```go
package users

import "github.com/gogf/gf/v2/crypto/gmd5"

func encryptPassword(password string) string {
...

func (u *Users) encryptPassword(password string) string {
return gmd5.MustEncryptString(password)
}
```

`gmd5`组件帮助我们快速实现`md5`加密功能。编写注册逻辑代码,引入密码加密。

*internal/logic/users/register.go*
*internal/logic/users/users_register.go*
```go
package users

...

func Register(ctx context.Context, username, password, email string) error {
func (u *Users) Register(ctx context.Context, username, password, email string) error {
...

_, err := dao.Users.Ctx(ctx).Data(do.Users{
Username: username,
Password: encryptPassword(password),
Password: u.encryptPassword(password),
Email: email,
}).Insert()
if err != nil {
Expand Down Expand Up @@ -257,20 +259,24 @@ curl -X POST http://127.0.0.1:8000/v1/users/register -H "Content-Type: applicati

## Register 函数优化
---
`model`层自定义一个数据模型,用作`Logic`层的入参。
自定义一个数据模型,用作`Logic`层的入参。

*internal/model/users.go*
*internal/logic/users/users_register.go*
```go
package model

type UserInput struct {

...

type RegisterInput struct {
Username string
Password string
Email string
}

...
```

*internal/logic/users/register.go*
*internal/logic/users/users_register.go*
```go
package users

Expand All @@ -279,8 +285,8 @@ import (
...
)

func Register(ctx context.Context, in *model.UserInput) error {
if err := CheckUser(ctx, in.Username); err != nil {
func (u *Users) Register(ctx context.Context, in RegisterInput) error {
if err := u.checkUser(ctx, in.Username); err != nil {
return err
}

Expand All @@ -298,22 +304,24 @@ func Register(ctx context.Context, in *model.UserInput) error {
...
```

更改`Controller`层,将`UserInput`传入。
更改`Controller`层,将`RegisterInput`传入。

*internal/controller/users/users_v1_register.go*
```go
package users

import (
"star/internal/model"
...
)

import (
"context"

"star/api/users/v1"
"star/internal/logic/users"
)

func (c *ControllerV1) Register(ctx context.Context, req *v1.RegisterReq) (res *v1.RegisterRes, err error) {
err = users.Register(ctx, &model.UserInput{
Username: req.Username,
Password: req.Password,
Email: req.Email,
err = c.users.Register(ctx, users.RegisterInput{
Username: req.Username,
Password: req.Password,
Email: req.Email,
})
return nil, err
}
Expand Down
Loading

0 comments on commit 6feaf2b

Please sign in to comment.