2021SC@SDUSC
引言
在上一篇文章中我们提到了如何生成和解析 JWT,接下来我们逐步分析如何去使用它。首先,在我们的系统中,对于每个用户来说,他们的账号和密码是不同的,我们要根据这些信息,来生成一个 Token 令牌,以后进行接口调用的时候,只需要出示令牌即可,不需要重新输入账号密码。
源码分析
在数据定义语言中,app_key
和app_secret
是我们的认证信息,而created_on
、created_by
、modified_on
、modified_by
、deleted_on
和is_del
对该行记录的一些描述,这些描述往往适用于大多数表。
create table blog_auth
(
id int unsigned auto_increment
primary key,
app_key varchar(20) default '' null comment 'Key',
app_secret varchar(50) default '' null comment 'Secret',
created_on int unsigned default 0 null comment '创建时间',
created_by varchar(100) default '' null comment '创建人',
modified_on int unsigned default 0 null comment '修改时间',
modified_by varchar(100) default '' null comment '修改人',
deleted_on int unsigned default 0 null comment '删除时间',
is_del tinyint unsigned default 0 null comment '是否删除 0为未删除'
)
comment '认证管理' charset = utf8mb4;
在internal/model
下的auto.go
中,为该表添加模型结构,在斜引号中json
后面的字符串如app_key
是变量AppKey
所映射的列名。而*Model
中包含了一些类似于CreatedBy
的通用信息。
type Auth struct {
*Model
AppKey string `json:"app_key"`
AppSecret string `json:"app_secret"`
}
Auth
的Get
方法查询了数据库,如果能找到符合条件的记录,则将记录中的第一条保存到变量auth
中;如果找不到的话,auth
中的值都为零值。
func (a Auth) Get(db *gorm.DB) (Auth, error) {
var auth Auth
db = db.Where(
"app_key = ? AND app_secret = ? AND is_del = ?",
a.AppKey,
a.AppSecret,
0,
)
err := db.First(&auth).Error
if err != nil && err != gorm.ErrRecordNotFound {
return auth, err
}
return auth, nil
}
在数据访问层internal/dao
下的auto.go
文件中,创建Auth
结构体对象,并调用它的Get
方法,这样可以查看数据库中是否有满足条件的记录。
func (d *Dao) GetAuth(appKey, appSecret string) (model.Auth, error) {
auth := model.Auth{AppKey: appKey, AppSecret: appSecret}
return auth.Get(d.engine)
}
在业务层internal/service
下的auth.go
中,AuthRequest
规定了请求体的格式,binging
中的required
表示请求体中该参数不能为空,否则报错。CheckAuth
会将参数体中的AppKey
和AppSecret
传入GetAuth
方法中,然后获取验证信息。如果验证成功的话,auth.ID
的值会数据库中的记录保持一致;如果记录不存在,那么auth
中的属性会是零值,auth.ID
的值为 0。
type AuthRequest struct {
AppKey string `form:"app_key" binding:"required"`
AppSecret string `form:"app_secret" binding:"required"`
}
func (svc *Service) CheckAuth(param *AuthRequest) error {
auth, err := svc.dao.GetAuth(param.AppKey, param.AppSecret)
if err != nil {
return err
}
if auth.ID > 0 {
return nil
}
return errors.New("auth info does not exist")
}
在internal/routers/api
下的auth.go
中,我们会看到GetAuth
方法大致分为三个部分,第一部分为接口校验,验证前端传来的请求是否符合规范;第二部分是身份验证,从数据库中寻找能满足条件的记录;第三部分是生成一个 Token 令牌,返回给用户。
值得一说的是,参数的绑定和校验是在BindAndValid
方法中进行的,如果参数不符合规则的话,就会把错误返回出去。
func GetAuth(c *gin.Context) {
param := service.AuthRequest{}
response := app.NewResponse(c)
valid, errs := app.BindAndValid(c, ¶m)
if !valid {
global.Logger.Errorf("app.BindAndValid errs: %v", errs)
errRsp := errcode.InvalidParams.WithDetails(errs.Errors()...)
response.ToErrorResponse(errRsp)
return
}
svc := service.New(c.Request.Context())
err := svc.CheckAuth(¶m)
if err != nil {
global.Logger.Errorf("svc.CheckAuth err: %v", err)
response.ToErrorResponse(errcode.UnauthorizedAuthNotExist)
return
}
token, err := app.GenerateToken(param.AppKey, param.AppSecret)
if err != nil {
global.Logger.Errorf("app.GenerateToken err: %v", err)
response.ToErrorResponse(errcode.UnauthorizedTokenGenerate)
return
}
response.ToResponse(gin.H{
"token": token,
})
}