一,安装用到的库
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
)