浏览代码

remove no need (#87)

* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* add jwt doc

Co-authored-by: kingxt <dream4kingxt@163.com>
kingxt 4 年之前
父节点
当前提交
878fd14739
共有 3 个文件被更改,包括 169 次插入10 次删除
  1. 168 0
      doc/jwt.md
  2. 1 0
      readme.md
  3. 0 10
      tools/goctl/api/gogen/gen.go

+ 168 - 0
doc/jwt.md

@@ -0,0 +1,168 @@
+### 基于go-zero实现JWT认证
+
+关于JWT是什么,大家可以看看[官网](https://jwt.io/),一句话介绍下:是可以实现服务器无状态的鉴权认证方案,也是目前最流行的跨域认证解决方案。
+
+要实现JWT认证,我们需要分成如下两个步骤
+
+* 客户端获取JWT token。
+* 服务器对客户端带来的JWT token认证。
+
+### 1.  客户端获取JWT Token
+
+我们定义一个协议供客户端调用获取JWT token,我们找一个目录执行goctl api new jwt,将生成的jwt.api改成如下:
+
+````go
+type JwtTokenRequest struct {
+}
+
+type JwtTokenResponse struct {
+	AccessToken string `json:"access_token"`
+  AccessExpire int64 `json:"access_expire"`
+  RefreshAfter int64  `json:"refresh_after"` // 建议客户端刷新token的绝对时间
+}
+
+service jwt-api {
+  @server(
+    handler: JwtHandler
+  )
+  post /user/token(JwtTokenRequest) returns (JwtTokenResponse);
+}
+````
+
+再次在生产服务目录中执行:api go -api jwt.api -dir .
+
+打开jwtlogic.go文件,修改 `func (l *JwtLogic) Jwt(req types.Request) (*types.Response, error) {` 方法如下:
+
+```go
+const AccessSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+
+func (l *JwtLogic) Jwt(req types.Request) (*types.Response, error) {
+	var accessExpire int64 = 60 * 60 * 24 * 7
+
+	now := time.Now().Unix()
+	accessToken, err := l.GenToken(now, AccessSecret, nil, accessExpire)
+	if err != nil {
+		return nil, err
+	}
+
+	return &types.Response{AccessToken: accessToken, AccessExpire: now + accessExpire, RefreshAfter: now + accessExpire/2}, nil
+}
+
+func (l *JwtLogic) GenToken(iat int64, secretKey string, payloads map[string]interface{}, seconds int64) (string, error) {
+	claims := make(jwt.MapClaims)
+	claims["exp"] = iat + seconds
+	claims["iat"] = iat
+	for k, v := range payloads {
+		claims[k] = v
+	}
+
+	token := jwt.New(jwt.SigningMethodHS256)
+	token.Claims = claims
+
+	return token.SignedString([]byte(secretKey))
+}
+```
+
+启动服务器,然后测试下获取到的token
+
+```sh
+➜  jwt curl --location --request POST '127.0.0.1:8888/user/token'
+{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MDEyNjE0MjksImlhdCI6MTYwMDY1NjYyOX0.6u_hpE_4m5gcI90taJLZtvfekwUmjrbNJ-5saaDGeQc","access_expire":1601261429,"refresh_after":1600959029}
+```
+
+
+
+### 2 服务器验证JWT token
+
+1. 添加一个测试JWT的路由,修改api文件如下:
+
+```go
+type JwtTokenRequest struct {
+}
+
+type JwtTokenResponse struct {
+	AccessToken  string `json:"access_token"`
+	AccessExpire int64  `json:"access_expire"`
+	RefreshAfter int64  `json:"refresh_after"` // 建议客户端刷新token的绝对时间
+}
+
+type GetUserRequest struct {
+  UserId string `json:"userId"`
+}
+
+type GetUserResponse struct {
+  Name string `json:"name"`
+}
+
+service jwt-api {
+  @server(
+    handler: JwtHandler
+  )
+  post /user/token(JwtTokenRequest) returns (JwtTokenResponse)
+
+  @server(
+    handler: GetUserHandler
+  )
+  post /user/getUser(GetUserRequest) returns (GetUserResponse)
+}
+```
+
+再次执行 `goctl api go -api jwt.api -dir .` 生成代码。
+
+2. 修改 routes.go,给协议添加JWT认证  `rest.WithJwt(logic.AccessSecret)`
+
+```go
+func RegisterHandlers(engine *rest.Server, serverCtx *svc.ServiceContext) {
+	engine.AddRoutes([]rest.Route{
+		{
+			Method:  http.MethodPost,
+			Path:    "/user/token",
+			Handler: jwtHandler(serverCtx),
+		},
+	})
+	engine.AddRoutes([]rest.Route{
+		{
+			Method:  http.MethodPost,
+			Path:    "/user/info",
+			Handler: getUserHandler(serverCtx),
+		},
+	}, rest.WithJwt(logic.AccessSecret))
+}
+```
+
+3. 修改getuserlogic.go如下:
+
+```go
+func (l *GetUserLogic) GetUser(req types.GetUserRequest) (*types.GetUserResponse, error) {
+	return &types.GetUserResponse{Name: "kim"}, nil
+}
+```
+
+* 我们先不带JWT Authorization header请求头测试下,返回http status code是401,符合预期。
+
+```sh
+➜  jwt curl -w  "\nhttp: %{http_code} \n" --location --request POST '127.0.0.1:8888/user/info' \
+--header 'Content-Type: application/json' \
+--data-raw '{
+    "userId": "a"
+}'
+
+http: 401
+```
+
+* 加上Authorization header请求头测试。
+
+```sh
+➜  jwt curl -w  "\nhttp: %{http_code} \n" --location --request POST '127.0.0.1:8888/user/info' \
+--header 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MDEyNjE0MjksImlhdCI6MTYwMDY1NjYyOX0.6u_hpE_4m5gcI90taJLZtvfekwUmjrbNJ-5saaDGeQc' \
+--header 'Content-Type: application/json' \
+--data-raw '{
+    "userId": "a"
+}'
+{"name":"kim"}
+http: 200
+```
+
+
+
+综上所述:基于go-zero的JWT认证完成,在真实生产环境部署时候,AccessSecret, AccessExpire, RefreshAfter可以通过配置文件配置,RefreshAfter 是告诉客户端什么时候该刷新JWT token了,一般都需要设置过期时间前几天。

+ 1 - 0
readme.md

@@ -160,6 +160,7 @@ go get -u github.com/tal-tech/go-zero
 * [防止缓存击穿之进程内共享调用](doc/sharedcalls.md)
 * [基于prometheus的微服务指标监控](doc/metric.md)
 * [文本序列化和反序列化](doc/mapping.md)
+* [快速构建jwt鉴权认证](doc/jwt.md)
 
 ## 9. 微信交流群
 

+ 0 - 10
tools/goctl/api/gogen/gen.go

@@ -60,8 +60,6 @@ func DoGenProject(apiFile, dir string, force bool) error {
 	logx.Must(genHandlers(dir, api))
 	logx.Must(genRoutes(dir, api, force))
 	logx.Must(genLogic(dir, api))
-	// it does not work
-	format(dir)
 	createGoModFileIfNeed(dir)
 
 	if err := backupAndSweep(apiFile); err != nil {
@@ -102,14 +100,6 @@ func backupAndSweep(apiFile string) error {
 	return err
 }
 
-func format(dir string) {
-	cmd := exec.Command("go", "fmt", "./"+dir+"...")
-	_, err := cmd.CombinedOutput()
-	if err != nil {
-		fmt.Println(err.Error())
-	}
-}
-
 func sweep() error {
 	keepTime := time.Now().AddDate(0, 0, -7)
 	return filepath.Walk(tmpDir, func(fpath string, info os.FileInfo, err error) error {