# 中间件使用

# 概要


jwt携带用户id信息
jwt 用户信息与header用户信息校验


# 修改jwt token生成逻辑


编辑loginlogic.go,将getJwtToken方法中添加`userId`参数，并将其添加到jwt token生成逻辑中，变更内容如下


getJwtToken方法


```golang
func (l *LoginLogic) getJwtToken(secretKey string, iat, seconds, userId int64) (string, error) {
	...
	claims["userId"] = userId 
	...
}
```


Login方法


```golang
func (l *LoginLogic) Login(req types.LoginReq) (*types.UserReply, error) {
	...
		jwtToken, err := l.getJwtToken(l.svcCtx.Config.Auth.AccessSecret, now, accessExpire, userInfo.Id)
    ...
}
```


# 添加中间件


添加middleware文件夹，并新建usercheckmiddleware.go


```bash
$ cd book/user/api/internal
$ mkdir middleware && cd middleware
$ touch usercheckmiddleware.go
```


# 修改user.api文件


在`/user/info`路由所在service上添加`middleware`标识，然后重新生成代码


```bash
...

@server(
	jwt: Auth
	middleware: UserCheck
)
service user-api {
	@handler userInfo
	get /user/info () returns (UserReply)
}
```


# 填充ServiceContext
```bash
$ vi ~/book/user/api/internal/svc/servicecontext.go
```


```go
package svc

import (
	"book/user/api/internal/config"
	"book/user/api/internal/middleware"
	"book/user/model"

	"github.com/tal-tech/go-zero/core/stores/sqlx"
	"github.com/tal-tech/go-zero/rest"
)

type ServiceContext struct {
	Config    config.Config
	UserModel model.UserModel
	UserCheck rest.Middleware
}

func NewServiceContext(c config.Config) *ServiceContext {
	conn := sqlx.NewMysql(c.Mysql.DataSource)
	um := model.NewUserModel(conn)
	return &ServiceContext{
		Config:    c,
		UserModel: um,
		UserCheck: middleware.NewUserCheckMiddleware().Handle,
	}
}
```


# 填充中间件逻辑


usercheckmiddleware.go


```go
package middleware

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"

	"github.com/tal-tech/go-zero/rest/httpx"
)

var (
	errorUserInfo = errors.New("用户信息获取失败")
	authDeny      = errors.New("用户信息不一致")
)

const userKey = `x-user-id`

type UserCheckMiddleware struct {
}

func NewUserCheckMiddleware() *UserCheckMiddleware {
	return &UserCheckMiddleware{}
}

func (m *UserCheckMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		userId := r.Header.Get(userKey)
		jwtUserId := r.Context().Value("userId")
		userInt, err := json.Number(userId).Int64()
		if err != nil {
			httpx.Error(w, errorUserInfo)
			return
		}

		jwtInt, err := json.Number(fmt.Sprintf("%v", jwtUserId)).Int64()
		if err != nil {
			httpx.Error(w, errorUserInfo)
			return
		}

		if jwtInt != userInt {
			httpx.Error(w, authDeny)
			return
		}

		next(w, r)
	}
}
```


# 验证


```bash
$ curl -X GET \
  http://127.0.0.1:8888/user/info \
  -H 'authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MDM2NDA5NTMsImlhdCI6MTYwMzU1NDU1MywidXNlcklkIjoxfQ.clyoXd7_5AJLJ_y41Wc8g9nmzvmO2qPYu_JIODK_RAQ' \
  -H 'x-user-id: 2'
```


![user-auth-failed.png](https://cdn.nlark.com/yuque/0/2020/png/465993/1603555726300-90bdb881-ccfc-4b05-8ff5-c83e61b572e4.png#align=left&display=inline&height=616&margin=%5Bobject%20Object%5D&name=user-auth-failed.png&originHeight=616&originWidth=2224&size=137447&status=done&style=none&width=2224)


<Vssue title="中间件使用" />
