kratos v2 基于http Filter 编写鉴权拦截器

kratos发布了v2版本,新版本全面拥抱了grpc,但是相对的对常用的工具组件支持不够友好,小型项目也可能不需要用到protocol,好在kratos开放了一些中间件和handle可以方便的进行自定义,本文介绍http Filter(filters ...FilterFunc) ServerOption

先看一下Filter 的定义

package http

import "net/http"

// FilterFunc is a function which receives an http.Handler and returns another http.Handler.
type FilterFunc func(http.Handler) http.Handler

// FilterChain returns a FilterFunc that specifies the chained handler for HTTP Router.
func FilterChain(filters ...FilterFunc) FilterFunc {
	return func(next http.Handler) http.Handler {
		for i := len(filters) - 1; i >= 0; i-- {
			next = filters[i](next)
		}
		return next
	}
}

filter handle 函数的参数和返回都是http.handler 执行时机在业务逻辑handlefunc之前。下面根据具体定义来编写一个基于Redis的鉴权登录拦截器,具体代码如下:

import (
	"context"
	"encoding/json"
	"github.com/go-kratos/kratos/v2/log"
	http2 "github.com/go-kratos/kratos/v2/transport/http"
	"github.com/go-redis/redis"
	"net/http"
	"note-svr/ecode"
	"note-svr/internal/model/user"
	"note-svr/middleware/reply"
	"time"
)

const (
	// token 有效期
	tokenExpire = 60 * 60 * 24 * 60 * time.Second
)

type Auth struct {
	redis  *redis.Client
	expire time.Duration
}

type authKey struct{}

type Options struct {
	Addr         string
	WriteTimeout time.Duration
	ReadTimeout  time.Duration
	TokenExpire  time.Duration
}

func New(opt *Options) *Auth {
	if opt.TokenExpire == 0 {
		opt.TokenExpire = tokenExpire
	}
	return &Auth{
		redis:  redis.NewClient(&redis.Options{Addr: opt.Addr, WriteTimeout: opt.WriteTimeout, ReadTimeout: opt.ReadTimeout}),
		expire: opt.TokenExpire,
	}
}

func (a *Auth) PutUser(user *user.User) error {
	bs, err := json.Marshal(user)
	if err != nil {
		log.Errorf("auth json marshal err: %v", err)
		return err
	}
	err = a.redis.Set(user.Token, bs, a.expire).Err()
	return err
}

func (a *Auth) GetUser(token string) (*user.User, error) {
	bs, err := a.redis.Get(token).Bytes()
	if err != nil {
		log.Errorf("token %s, bs: %x", token, bs)
		return nil, err
	}
	u := &user.User{}
	if err := json.Unmarshal(bs, &u); err != nil {
		log.Errorf("auth json marshal err: %v", err)
	}
	return u, nil
}

func (a *Auth) Guest() http2.FilterFunc {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
			u, _ := a.tokenUser(request)
			if u != nil {
				ctx := context.WithValue(request.Context(), authKey{}, u)
				request = request.WithContext(ctx)
			}
			next.ServeHTTP(writer, request)
		})
	}
}

func (a *Auth) User() http2.FilterFunc {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
			u, err := a.tokenUser(request)
			if err != nil || u == nil {
				e, ok := err.(*ecode.Err)
				if !ok {
					e = ecode.New(ecode.NoLoginCode, err.Error())
				}
				resp, _ := json.Marshal(reply.Status{Code: e.Code(), Message: e.Message})
				writer.Write(resp)
				return
			}
			ctx := context.WithValue(request.Context(), authKey{}, u)
			request = request.WithContext(ctx)
			next.ServeHTTP(writer, request)
		})
	}
}

func (a *Auth) tokenUser(request *http.Request) (u *user.User, err error) {
	token := ""
	if request.Form.Get("token") != "" {
		token = request.Form.Get("token")
	}
	if ctoken, err := request.Cookie("token"); err == nil {
		if ctoken.Value != "" {
			token = ctoken.Value
		}
	}
	if htoken := request.Header.Get("token"); htoken != "" {
		token = htoken
	}
	if token == "" {
		return nil, ecode.NoLoginErr
	}
	return a.GetUser(token)
}

func FromContext(ctx context.Context) (u *user.User, ok bool) {
	u, ok = ctx.Value(authKey{}).(*user.User)
	return
}

使用时先调用New函数初始化auth对象,Guest() 是游客态拦截器,未登录不会拦截,登录时会向context写入用户信息,User() 未登录会直接返回response。

项目中应用:

1、首先需要在登录时调用PutUser将用户信息写入Redis

2、server中初始化auth

	au := auth.New(&auth.Options{
		Addr:         data.Redis.Addr,
		WriteTimeout: data.Redis.WriteTimeout,
		ReadTimeout:  data.Redis.ReadTimeout,
	})

3、配置路由时添加拦截器

user.GET("/info", UserInfo, au.User())

4、业务中通过context获取用户信息

func UserInfo(ctx http.Context) error {
	u, _ := auth.FromContext(ctx)
	return reply.JSON(ctx, u, nil)
}

kratos v2官方文档地址:简介 | Kratos

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值