【Gin-API系列】Gin中间件之鉴权访问(五)
2020-09-02 18:32
861 查看
在完成中间件的介绍和日志中间件的代码后,我们的程序已经基本能正常跑通了,但如果要上生产,还少了一些必要的功能,例如鉴权、异常捕捉等。本章我们介绍如何编写鉴权中间件。
鉴权访问,说白了就是给用户的请求增加一些限制条件,过滤掉不符合要求的请求。完善的鉴权模块可以让我们的服务跑得更加安全,特别是面向公共的服务。
常用的无状态鉴权方式
- 网络鉴权
通常有IP白名单方式,通过获取客户端的真实IP来对请求进行过滤
- 用户鉴权
通过账号密码或者分配的密钥、Token等方式进行认证,常用的cookies、oauth2.0都是这种方式
- 加密算法鉴权
客户端使用加密算法对用户的参数进行计算加密得到Token,并将参数和Token一起发送;服务端使用同样的加密算法对请求参数进行加密后比较Token的值是否一致。
Gin-IPs 鉴权访问
为了让我们的服务更加安全,我们通常是混合多种鉴权方式使用。本文采取“用户鉴权”和“加密算法鉴权”混合方式。
- 鉴权算法介绍
通过某种方式生成或者手动指定分配公钥和私钥对给用户,用户使用公钥和请求参数组成消息内容,使用私钥对消息内容进行哈希计算,得到固定长度的字符串。
服务器使用同样的方式对用户请求的参数进行哈希计算后比较。由于这里面的算法和密钥对都是私有的,所以安全性较高,适用于大多数场景。
- 加密代码
// 签名算法如下 /* Signature = HMAC-SHA1('SecretKey', UTF-8-Encoding-Of( StringToSign ) ) ); StringToSign = method + "\n" + URL + "\n" + Sort-UrlParams + "\n" + Content-MD5 + "\n" + // md5(params) Expires + "\n" + AccessKey; */ func genSignature(accessKey, secretKey, uri, method, urlParams, params, nowTS string) (string, error) { if params != "" { md5Ctx := md5.New() _, _ = io.WriteString(md5Ctx, params) params = fmt.Sprintf("%x", md5Ctx.Sum(nil)) } // HTTP-Verb + "\n" +URL + "\n" +Parameters + "\n" +Content-Type + "\n" +Content-MD5 + "\n" +Date + "\n" +AccessKey; strSign := method + "\n" + uri + "\n" + urlParams + "\n" + "\n" + params + "\n" + nowTS + "\n" + accessKey sign := hmacSHA1Encrypt(strSign, secretKey) return sign, nil } // hmacSHA1Encrypt encrypt the encryptText use encryptKey func hmacSHA1Encrypt(encryptText, encryptKey string) string { key := []byte(encryptKey) mac := hmac.New(sha1.New, key) mac.Write([]byte(encryptText)) var str = hex.EncodeToString(mac.Sum(nil)) return str }
- Gin鉴权中间件使用
func Validate() gin.HandlerFunc { return func(c *gin.Context) { response := route_response.Response{} response.Data.List = []interface{}{} // 初始化为空切片,而不是空引用 uri := c.Request.URL.Path contentType := c.Request.Header.Get("Content-Type") accessKey := c.DefaultQuery("accesskey", "") expires := c.DefaultQuery("expires", "") signature := c.DefaultQuery("signature", "") secret, err := dao.FetchSecret(accessKey) if err != nil || "valid" != secret.State { c.Abort() response.Code, response.Message = configure.RequestKeyNotFound, "无效的Token" c.JSON(http.StatusUnauthorized, response) return } secretKey := secret.SecretKey if nowTs, err := strconv.ParseInt(expires, 10, 64); err != nil { c.Abort() response.Code, response.Message = configure.RequestParameterTypeError, "有效期参数类型错误" c.JSON(http.StatusUnauthorized, response) return } else { passTime := time.Now().Unix() - nowTs if passTime < 0 || passTime >= configure.GinConfigValue.Expires { c.Abort() response.Code, response.Message = configure.RequestExpired, "请求已过期" c.JSON(http.StatusUnauthorized, response) return } } method := strings.ToUpper(c.Request.Method) var urlParams, params string if "POST" == method || "PUT" == method { body, _ := ioutil.ReadAll(c.Request.Body) c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) // 重设body params = string(body) } else if "GET" == method || "DELETE" == method { queryParams := c.Request.URL.Query() allParams := make(map[string]string) for k, v := range queryParams { if k != "accesskey" && k != "expires" && k != "signature" { allParams[k] = v[0] // 如果某个key传入了2个,只用第一个的值 } } keys := getMapKeysSorted(allParams) for _, k := range keys { urlParams += k + allParams[k] } } if signatureString, err := genSignature(accessKey, secretKey, uri, method, urlParams, params, expires); err != nil { c.Abort() response.Code, response.Message = configure.ApiGenSignatureError, "API内部错误" c.JSON(http.StatusUnauthorized, response) return } else { if signature != signatureString { c.Abort() response.Code, response.Message = configure.RequestAuthorizedFailed, "API认证失败" c.JSON(http.StatusUnauthorized, response) return } } c.Next() } }
本文关于鉴权访问的介绍和使用到此为止,下一章我们将使用异常捕捉中间件来完善我们的程序。
Github 代码
请访问 Gin-IPs 或者搜索 Gin-IPs
相关文章推荐
- [系列] go-gin-api 路由中间件 - 捕获异常(四)
- [系列] go-gin-api 路由中间件 - 日志记录(三)
- [系列] go-gin-api 路由中间件 - Jaeger 链路追踪(五)
- 实战DeviceIoControl系列 之一:通过API访问设备驱动程序
- 实战DeviceIoControl系列 之一:通过API访问设备驱动程序
- Windows Azure入门教学系列 (七):使用REST API访问Storage Service
- Silverlight 5 RC新特性探索系列:12.Silverlight 5 RC 窗口模式下访问自定义DLL和WIN32 API
- [系列] go-gin-api 规划目录和参数验证(二)
- 循序渐进学.Net Core Web Api开发系列【8】:访问数据库(基本功能)
- Silverlight 5 RC新特性探索系列:12.Silverlight 5 RC 窗口模式下访问自定义DLL和WIN32 API
- 【WEB API项目实战干货系列】- API访问客户端(WebApiClient适用于MVC/WebForms/WinForm)(四)
- 在gin中使用中间件实现RestAPI权限验证的示例
- Struts2系列:(5)与Servlet API解耦(Servlet相关对象访问)
- 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi
- 【WEB API项目实战干货系列】- API访问客户端(WebApiClient适用于MVC/WebForms/WinForm)(四)
- Kubernetes 系列(四):使用Traefik访问.net core api
- asp.net core系列 57 IS4 使用混合流(OIDC+OAuth2.0)添加API访问
- Kong对用户访问api鉴权
- 循序渐进学.Net Core Web Api开发系列【13】:中间件(Middleware)
- gin系列-中间件