go语言web开发系列之二十五:gin框架:用md5方式为接口站验证签名

一,安装用到的库

 

1,gin框架在github的地址:

https://github.com/gin-gonic/gin

2,从命令行安装:

root@ku:/data/go/ginhello# go get -u github.com/gin-gonic/gin@v1.6.3

说明:刘宏缔的go森林是一个专注golang的博客,
网站:https://blog.imgtouch.com
原文: go语言web开发系列之二十五:gin框架:用md5方式为接口站验证签名 – 架构森林

说明:作者:刘宏缔 邮箱: 371125307@qq.com

二,演示项目的相关信息

1,项目地址:

https://github.com/liuhongdi/digv25

2,功能说明:演示为接口站增加签名验证,避免接口受到攻击

3,项目结构:如图:

三,go代码说明

1,controller/userController.go

package controller

import (
	"github.com/gin-gonic/gin"
	"github.com/liuhongdi/digv25/global"
)

type UserController struct{}
func NewUserController() UserController {
	return UserController{}
}
//用户登录
func (g *UserController) Login(c *gin.Context) {
	result := global.NewResult(c)
	result.Success("success");
	return
}

2,global/constants.go

package global

var (
  SignAppId string = "wap"                                    //app id
  SignAppSecret string = "30c722c6acc64306a88dd93a814c9f0a"   //app secret
  SignApiVersion string = "1.0"                               //api version
  SignExpire int64 = 180                                      //过期时间,180秒
)

3,middleware/sign.go

package middleware

import (
	"github.com/gin-gonic/gin"
	"github.com/liuhongdi/digv25/global"
	"github.com/liuhongdi/digv25/pkg/sign"
	"regexp"
)
//限流器
func SignMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
        //检查是否排除当前url
		exclude := regexp.MustCompile("/static/*")
		path := c.Request.RequestURI
		//fmt.Println("path:"+path)
		if exclude != nil && exclude.MatchString(path) {
			c.Next()
			return
		}

		//从header获取参数
		appId := c.Request.Header.Get("appId")
		timestamp := c.Request.Header.Get("timestamp")
		signcli := c.Request.Header.Get("sign")
		nonce := c.Request.Header.Get("nonce")

		//验证签名是否正确
		err := sign.VerifySign(appId,timestamp,signcli,nonce,global.SignAppId,global.SignAppSecret,global.SignApiVersion,global.SignExpire)
        if (err != nil) {
			resultRes := global.NewResult(c)
			resultRes.Error(2004,err.Error())
			return
		} else {
		   //fmt.Println("sign passed")
		   c.Next()
		   return
	    }
	}
}

4,pkg/md5/md5.go

package md5

import (
	"crypto/md5"
	"encoding/hex"
)

//返回md5结果
func MD5(str string) string {
	s := md5.New()
	s.Write([]byte(str))
	return hex.EncodeToString(s.Sum(nil))
}

5,pkg/sign/sign.go

package sign

import (
	"errors"
	"github.com/liuhongdi/digv25/pkg/md5"
	"strconv"
	"time"
)

// 验证签名
func VerifySign(appId,timestamp,sign,nonce,serverAppId,serverAppSecret,serverApiVersion string,signExpire int64) error {

    //验证appid
	if (serverAppId != appId) {
		return errors.New("appId Error")
	}

	// 验证过期时间
	timestampNow := time.Now().Unix()
	//fmt.Println("now:")
	//fmt.Println(timestampNow)
	tsInt, _  := strconv.ParseInt(timestamp, 10, 64)
	//fmt.Println("timeint:")
	//fmt.Println(tsInt)
	if tsInt > timestampNow || timestampNow - tsInt >= signExpire {
		return errors.New("timestamp Error")
	}

	//得到正确的sign供检验用
	origin := appId + serverAppSecret + timestamp + nonce + serverApiVersion;
	signEcrypt := md5.MD5(origin);
	if (signEcrypt != sign) {
		return errors.New("sign Error")
	}
    //未出错,返回
	return nil
}

6,router/router.go

package router

import (
	"github.com/gin-gonic/gin"
	"github.com/liuhongdi/digv25/controller"
	"github.com/liuhongdi/digv25/global"
	"github.com/liuhongdi/digv25/middleware"
	"log"
	"net/http"
	"runtime/debug"
)

func Router() *gin.Engine {
	router := gin.Default()
	//处理异常
	router.NoRoute(HandleNotFound)
	router.NoMethod(HandleNotFound)
	router.Use(middleware.SignMiddleware())
	router.Use(Recover)
	//static
	router.StaticFS("/static", http.Dir("/data/liuhongdi/digv25/static"))
	// 路径映射:index
	indexc:=controller.NewIndexController()
	router.GET("/index/index", indexc.Index);
	// 路径映射:user
	userc:=controller.NewUserController()
	router.POST("/user/login", userc.Login);
	return router
}

func HandleNotFound(c *gin.Context) {
	global.NewResult(c).Error(404,"资源未找到")
	return
}

func Recover(c *gin.Context) {
	defer func() {
		if r := recover(); r != nil {
			//打印错误堆栈信息
			log.Printf("panic: %v\n", r)
			debug.PrintStack()
			global.NewResult(c).Error(500,"服务器内部错误")
		}
	}()
	//加载完 defer recover,继续后续接口调用
	c.Next()
}

7,static/form.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" language="JavaScript" src="jquery-1.6.2.min.js"></script>
    <script type="text/javascript" language="JavaScript" src="md5.js"></script>
</head>
<body>
<a href="javascript:login('right')">login(right)</a><br/>
<a href="javascript:login('error')">login(error)</a><br/>
<script>
    //vars
    var appId="wap";
    var version="1.0";

    //得到sign
    function getsign(appSecret,timestamp,nonce) {
        var origin = appId + appSecret + timestamp +  nonce + version;
        console.log("origin:"+origin);
        var sign = hex_md5(origin);
        return sign;
    }

    //访问login这个api
    //说明:这里仅仅是举例子,在ios/android开发中,appSecret要以二进制的形式编译保存
    function login(isright) {
        //right secret
        var appSecret_right="30c722c6acc64306a88dd93a814c9f0a";
        //error secret
        var appSecret_error="aabbccdd";
        var timestamp = parseInt((new Date()).getTime()/1000);
        var nonce = Math.floor(Math.random()*8999)+1000;

        var sign = "";
        if (isright == 'right') {
            sign = getsign(appSecret_right,timestamp,nonce);
        } else {
            sign = getsign(appSecret_error,timestamp,nonce);
        }

        var postdata = {
            username:"a",
            password:"b"
        }

        $.ajax({
            type:"POST",
            url:"/user/login",
            data:postdata,
            //返回数据的格式
            datatype: "json",
            //在请求之前调用的函数
            beforeSend: function(request) {
                request.setRequestHeader("appId", appId);
                request.setRequestHeader("timestamp", timestamp);
                request.setRequestHeader("sign", sign);
                request.setRequestHeader("nonce", nonce);
            },
            //成功返回之后调用的函数
            success:function(data){
                //alert(data.code);
                if (data.code == 0) {
                    alert('访问成功');
                } else {
                    alert("failed:"+data.msg);
                }
            },
            //调用执行后调用的函数
            complete: function(XMLHttpRequest, textStatus){
                //complete
            },
            //调用出错执行的函数
            error: function(){
                //请求出错处理
            }
        });

    }
</script>

</body>
</html>

8,其他相关代码可访问github

四,效果测试

1,访问html页面:

http://127.0.0.1:8080/static/form.html

返回:

2,分别点击:测试验证是否有效:

点击error链接时:

五,查看库的版本:

module github.com/liuhongdi/digv25

go 1.15

require (
	github.com/gin-gonic/gin v1.6.3
)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老刘你真牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值