1、系列目录
2、概览
通过本篇文章,读者将会掌握kratos的一般开发流程,涵盖了从接口定义、自定义配置,到业务逻辑实现,再到数据库存储的一整套流程。注册登陆业务是非常高频常见的业务,几乎所有的系统都有注册和登陆的功能。本文展示的注册登陆业务非常基本、非常简单。因此,只需要用户数据库和采用一些主流的JWT认证策略。
2、开干
2.1、定义接口
在api目录下,新建account/v1目录,然后添加account.proto文件。
kratos的接口统一使用protobuf定义的,无论是走grpc协议还是走http协议。把接口的定义和具体的协议隔离出来,并用统一的定义语言定义接口入参和回参以及其他的信息。kratos提供了http协议的protoc插件,在生成grpc的代码时,也会生成http的代码。定义http接口的方式使用的是google的规范。作为入门级的教程,这里就不再详述了。
然后添加注册和登陆的接口:
syntax = "proto3";
package account.v1;
import "google/api/annotations.proto";
option go_package = "tt-shop/api/account/v1;v1";
service Account {
rpc Login (LoginRequest) returns (LoginResponse) {
option (google.api.http) = {
post: "/account/login"
body: "*"
};
}
rpc Register (RegisterRequest) returns (RegisterResponse) {
option (google.api.http) = {
post: "/account/register"
body: "*"
};
}
}
message LoginRequest {
string phone = 1;
string password = 2;
}
message LoginResponse {
string token = 1;
}
message RegisterRequest {
string phone = 1;
string password = 2;
}
message RegisterResponse {
}
最后执行命令(在项目的根目录下):
make api
命令执行完后就能看到目录生成了多个源文件,包括protobuf生成的、grpc生成的和kratos的http插件生成的。
2.2、建立用户数据库
建立用户表:
create table user
(
id int(64) not null auto_increment,
username varchar(64) not null,
password varchar(128) not null,
phone varchar(18),
nickname varchar(20),
PRIMARY KEY (id)
) engine=innodb default charset=utf8mb4;
2.3、添加认证配置
项目使用的是JWT认证方式,加密JWT token需要一些秘钥、过期时间等等。这些额外的信息不会直接硬编码到代码中,而是写到配置文件中,做成可配置的。在这一小细节,读者将会掌握如何在kratos项目中添加配置。
kratos的配置声明是放在internal/conf/conf.proto
,在proto文件中添加如下配置:
message Auth {
string jwt_secret = 1;
google.protobuf.Duration expire_duration = 2;
}
然后Bootstrap
的message增加认证配置项:
message Bootstrap {
Server server = 1;
Data data = 2;
Auth auth = 3;
}
然后在项目的目录下执行命令:make config
,就能看到如下输出:
❯ make config
protoc --proto_path=./internal \
--proto_path=./third_party \
--go_out=paths=source_relative:./internal \
internal/conf/conf.proto
此时,在定义配置的目录下就能看到新编译出来的pb文件。
然后在configs/config.yaml
增加新增的认证配置auth:
server:
http:
addr: 0.0.0.0:8000
timeout: 1s
grpc:
addr: 0.0.0.0:9000
timeout: 1s
data:
database:
driver: mysql
source: root:root@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local
redis:
addr: 127.0.0.1:6379
read_timeout: 0.2s
write_timeout: 0.2s
# 认证配置
auth:
jwt_secret: "secret"
expire_duration: 3600s
2.4、实现注册登陆业务
在上一篇教程中介绍过kratos的分层结构,业务在biz层,所以这里先从biz实现(这不是一种规范,读者可以按照自己的习惯来做)。在biz目录新建account.go,存放account业务的源码。
2.4.1、定义用户对象
现阶段使用贫血模型(相对于充血模型而言),因为贫血模型比较直观好理解,适合入门。如果读者比较熟悉充血模型,建议看看番外篇。
type User struct {
ID int64 // 用户ID
Username string // 用户名
Password string // 密码
Nickname string // 昵称
Avatar string // 头像
}
2.4.2、定义用户数据仓库操作接口
这里定义两个接口,分别执行获取指定id的用户和保存用户信息的操作。这里通过在biz层定义一层接口实现了依赖反转(正常biz需要调data提供的方法来操作用户的数据,依赖反转后,是biz要求data实现哪些接口,biz依赖的是接口,而不是data的实现)。
type UserRepo interface {
// FetchByUsername 获取指定用户名的用户的信息,如果用户不存在,则返回 ErrUserNotExist。
FetchByUsername(ctx context.Context, username string) (user *User, err error)
// Save 保存用户信息并返回用户的id。
Save(ctx context.Context, user *User) (id int64, err error)
}
2.4.3、实现注册功能
接下来,我们定义一个account的用例,通过用例对象提供注册的功能。account用例对象依赖了用户数据仓库接口UserRepo,需要使用UserRepo来操作用户数据,例如查询。同时,用户的密码加密也抽取到一独立的加密服务中。因为使用了依赖注入工具,所以这些使用到的依赖都不需要自己去构建初始化,只要作为参数传入构造函数即可(事实上,go并有所谓的构造函数,这里为了方便描述)。
然后,account用例对象提供一个注册的方法,实现基本的注册逻辑。这里只是简单的注册需求,有些公司会有特别的安全需求,例如密码长度和包含的符号类型,读者可自行拓展。
注意了!生成环境严禁数据库存储密码明文!!!!!
以下是具体的代码:
type AccountUseCase struct {
authConfig *conf.Auth
encryptService EncryptService
userRepo UserRepo
logger *log.Helper
}
//NewAccountUseCase 创建一个AccountUseCase,依赖作为参数传入
func NewAccountUseCase(logger log.Logger, authConfig *conf.Bootstrap, userRepo UserRepo, encryptService EncryptService) *AccountUseCase {
return &AccountUseCase{
encryptService: encryptService,
userRepo: userRepo,
logger: log.NewHelper(logger),
authCon