跟Gin一块搭建自己的web框架(七)

本篇介绍HTTP Basic Auth的实现以及Recovery机制。

HTTP Basic Auth

Basic Auth是一种开放平台认证方式,简单的说就是需要你输入用户名和密码才能继续访问。对于Basic Auth的概念不过多的进行介绍,直接进入如何实现的过程。

Basic Auth说白了就是账号和密码的组合,所以定义用来存储账号和密码的结构体。

type (
	// BasicAuthPair .
	BasicAuthPair struct {
		Code string
		User string
	}
	// Account .
	Account struct {
		User     string
		Password string
	}
	// Accounts .
	Accounts []Account
	// Pairs .
	Pairs []BasicAuthPair
)

其中, Accounts 用来存放原始原始的账号密码组合, Pairs 用来存经过编码的账号密码组合。

Basic Auth初始化的过程,就是将 “账号:密码” 串经过base64编码之后,存放在Pairs中。

func processCredentials(accounts Accounts) (Pairs, error) {
	if len(accounts) == 0 {
		return nil, errors.New("Empty list of authorized credentials.")
	}
	pairs := make(Pairs, 0, len(accounts))
	for _, account := range accounts {
		if len(account.User) == 0 || len(account.Password) == 0 {
			return nil, errors.New("User or password is empty")
		}
		base := account.User + ":" + account.Password
		code := "Basic " + base64.StdEncoding.EncodeToString([]byte(base))
		pairs = append(pairs, BasicAuthPair{code, account.User})
	}
	// We have to sort the credentials in order to use bsearch later.
	sort.Sort(pairs)
	return pairs, nil
}

在访问对应url的时候,会提取auth字段,然后将auth字段在我们保存的Pairs 中进行比对查找,如果找到了,就会返回对应的user

func searchCredential(pairs Pairs, auth string) string {
	if len(auth) == 0 {
		return ""
	}
	// Search user in the slice of allowed credentials
	r := sort.Search(len(pairs), func(i int) bool { return pairs[i].Code >= auth })

	if r < len(pairs) && subtle.ConstantTimeCompare([]byte(pairs[r].Code), []byte(auth)) == 1 {
		// user is allowed, set UserId to key "user" in this context, the userId can be read later using
		// c.Get("user"
		return pairs[r].User
	}
	return ""
}

相应的, 如果我们需要对一个 url 开启Basic Auth认证,首先定义一个路由组,调用对应的Basic Auth的中间件函数。

accounts := gin.Accounts{
		{User: "admin", Password: "password"},
		{User: "foo", Password: "bar"},
	}
authorized := r.Group("/auth", gin.BasicAuth(accounts))

然后把需要认证的url都挂在下边

authorized.GET("/secret", func(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"secret": "The secret url need to be authorized",
	})
})

这样当我们访问 /auth/secret 的时候,就需要经过Basic Auth的认证。如果我们在浏览器端访问,会弹出一个提示框,要求输入账号和密码; 如果是在 postman 等工具中访问, 需要在header中添加一个 Authorization 字段,字段的值是 “Basic 账号:密码的base64编码”。 比如账号是admin, 密码是password, 那么“admin:password” 经过base64编码的结果是“YWRtaW46cGFzc3dvcmQ=”, 所以对应的Authorization填的值应该是 “Basic YWRtaW46cGFzc3dvcmQ=”。

Recovery

Recovery在web框架中也是一个必不可少的中间件,如果某个请求出现了panic,服务需要捕获请求中的panic信息,并且把信息打印到日志中,有利于后期的维护和修复。

Recovery 其实就是定义了一个defer函数来处理请求中的panic,在defer中,会通过golang的recover() 函数catch住panic,然后将对应的栈信息打印出来。

func Recovery() HandlerFunc {
	return func(c *Context) {
		defer func() {
			if len(c.Errors) > 0 {
				log.Println(c.Errors)
			}
			if err := recover(); err != nil {
				stack := stack(3)
				log.Printf("PANIC: %s\n%s", err, stack)
				c.Writer.WriteHeader(http.StatusInternalServerError)
			}
		}()

		c.Next()
	}
}

一般来说,log中间件和recovery中间件都是必须的,所以可以定义一个默认的框架初始化函数,自动把log中间件和recovery中间件加载进去。

// Default Returns a Engine instance with the Logger and Recovery already attached.
func Default() *Engine {
	engine := New()
	engine.Use(Recovery(), Logger())
	return engine
}

至此,一个简易版本的gin web框架就成型了,包括最基本的日志功能、错误恢复、路由功能、认证功能等。当然也可以在这个基础之上在增加ORM等模块,当是后续的功能扩展了。

完整的代码参见: https://github.com/harleylau/myGin/tree/master/v0.5

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值