diff --git a/go.mod b/go.mod index 695110c..ad8d81d 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/mattn/go-sqlite3 v1.14.7 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 + github.com/satori/go.uuid v1.2.0 github.com/sirupsen/logrus v1.8.1 golang.org/x/sys v0.0.0-20210112080510-489259a85091 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect diff --git a/go.sum b/go.sum index 994140d..74e5829 100644 --- a/go.sum +++ b/go.sum @@ -76,6 +76,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo= github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/src/base/simple_page_data.go b/src/base/simple_page_data.go index 8a63c49..9a89be6 100644 --- a/src/base/simple_page_data.go +++ b/src/base/simple_page_data.go @@ -1,6 +1,6 @@ package base type SimplePageData struct { - total uint `json:"total"` - data interface{} `json:"data"` + Total int `json:"total"` + Data interface{} `json:"data"` } diff --git a/src/main.go b/src/main.go index b20b1d5..37f1b5e 100644 --- a/src/main.go +++ b/src/main.go @@ -3,11 +3,15 @@ package main import ( "lingye-gin/src/config" "lingye-gin/src/middleware" + v1 "lingye-gin/src/test/v1" ) func main() { // 初始化yaml配置 new(config.ApplicationProperties).Init() + + v1.DescribeStudents(nil) + // 初始化redis new(middleware.RedisPool).Init() // 初始化gorm, 注册表 diff --git a/src/modules/system/rest/user_rest.go b/src/modules/system/rest/user_rest.go index e33854e..1476b31 100644 --- a/src/modules/system/rest/user_rest.go +++ b/src/modules/system/rest/user_rest.go @@ -6,7 +6,6 @@ import ( "lingye-gin/src/modules/system/service/dto" "lingye-gin/src/modules/system/service/query" "lingye-gin/src/util" - "net/http" ) // 统一Service对象 @@ -15,30 +14,18 @@ var userService = &service.UserService{} func DescribeUsers(c *gin.Context) { var userQuery query.UserQuery _ = c.BindJSON(&userQuery) - users, total := userService.DescribeUsers(userQuery) - c.JSON(http.StatusOK, gin.H{ - "code": http.StatusOK, - "data": users, - "total": total, - }) + util.RSuccessJson(c, util.BuildPageData(users, total)) } func DescribeUserById(c *gin.Context) { id := c.Params.ByName("id") user := userService.DescribeUserById(util.StringToUInt64(id)) if user.ID == 0 { - c.JSON(http.StatusNotFound, gin.H{ - "code": http.StatusNotFound, - "message": "user not found", - }) + util.RErrorMsg(c, "该用户信息未查询到") return } - c.JSON(http.StatusOK, gin.H{ - "code": http.StatusOK, - "message": "success", - "data": user, - }) + util.RSuccessJson(c, user) } func CreateUser(c *gin.Context) { @@ -46,29 +33,20 @@ func CreateUser(c *gin.Context) { // 绑定一个请求主体到一个类型 _ = c.BindJSON(&userDTO) userService.Save(&userDTO) - c.JSON(http.StatusOK, gin.H{ - "code": http.StatusOK, - "message": "success", - }) + util.RSuccessMsg(c, "创建成功!") } func ModifyUserById(c *gin.Context) { id := c.Params.ByName("id") localUser := userService.DescribeUserById(util.StringToUInt64(id)) if localUser.ID == 0 { - c.JSON(http.StatusNotFound, gin.H{ - "code": http.StatusNotFound, - "message": "user not found", - }) + util.RErrorMsg(c, "非法操作, 该用户信息未查询到!") return } else { var userDTO dto.UserDTO _ = c.BindJSON(&userDTO) userService.ModifyById(&userDTO) - c.JSON(http.StatusOK, gin.H{ - "code": http.StatusOK, - "message": "success", - }) + util.RSuccessMsg(c, "修改成功!") } } @@ -77,16 +55,10 @@ func DeleteUserById(c *gin.Context) { uid := util.StringToUInt64(id) localUser := userService.DescribeUserById(uid) if localUser.ID == 0 { - c.JSON(http.StatusNotFound, gin.H{ - "code": http.StatusNotFound, - "message": "user not found", - }) + util.RErrorMsg(c, "非法操作, 该用户信息未查询到!") return } else { userService.RemoveById(uid) - c.JSON(http.StatusOK, gin.H{ - "code": http.StatusOK, - "message": "success", - }) + util.RSuccessMsg(c, "删除成功!") } } diff --git a/src/test/sign_rest.go b/src/test/sign_rest.go index f62556f..2f6d717 100644 --- a/src/test/sign_rest.go +++ b/src/test/sign_rest.go @@ -17,5 +17,5 @@ func DescribeSign(c *gin.Context) { } res["sn"] = util.CreateSign(params) res["ts"] = ts - util.RetJson("200", "", res, c) + util.RSuccessJson(c, res) } diff --git a/src/test/v1/student_rest.go b/src/test/v1/student_rest.go index bcfd64d..0173bbb 100644 --- a/src/test/v1/student_rest.go +++ b/src/test/v1/student_rest.go @@ -3,6 +3,8 @@ package v1 import ( "fmt" "github.com/gin-gonic/gin" + "lingye-gin/src/base" + "lingye-gin/src/util" ) type Student struct { @@ -13,11 +15,11 @@ type Student struct { func DescribeStudents(c *gin.Context) { var students []Student - c.JSON(200, gin.H{ - "data": students, - "page": 1, - "pageSize": 15, - }) + var pageData base.SimplePageData + + pageData.Total = 0 + pageData.Data = students + util.RSuccessWithMsgJson(c, "分页获取学生列表成功!", pageData) } func DescribeStudentById(c *gin.Context) { @@ -25,17 +27,17 @@ func DescribeStudentById(c *gin.Context) { fmt.Println("id=", id) var student Student if student.ID == 0 { - c.JSON(404, gin.H{"message": "student not found"}) + util.RErrorJson(c, "没有找到该学生信息", nil) return } - c.JSON(200, student) + util.RSuccessJson(c, student) } func CreateStudent(c *gin.Context) { var student Student // 绑定一个请求主体到一个类型 _ = c.BindJSON(&student) - c.JSON(200, "创建成功") + util.RSuccessWithMsgJson(c, "创建成功", nil) } func ModifyStudentById(c *gin.Context) { @@ -43,11 +45,11 @@ func ModifyStudentById(c *gin.Context) { fmt.Println("id=", id) var student Student if student.ID == 0 { - c.JSON(404, gin.H{"message": "student not found"}) + util.RErrorJson(c, "非法操作, 没有找到该学生信息!", nil) return } else { _ = c.BindJSON(&student) - c.JSON(200, student) + util.RSuccessJson(c, student) } } @@ -56,10 +58,10 @@ func DeleteStudentById(c *gin.Context) { fmt.Println("id=", id) var student Student if student.ID == 0 { - c.JSON(404, gin.H{"message": "student not found"}) + util.RErrorJson(c, "非法操作, 没有找到该学生信息!", nil) return } else { _ = c.BindJSON(&student) - c.JSON(200, gin.H{"message": "delete success"}) + util.RSuccessMsg(c, "删除成功") } } diff --git a/src/test/v2/student_rest.go b/src/test/v2/student_rest.go index 170437d..407e757 100644 --- a/src/test/v2/student_rest.go +++ b/src/test/v2/student_rest.go @@ -2,6 +2,8 @@ package v2 import ( "github.com/gin-gonic/gin" + "lingye-gin/src/base" + "lingye-gin/src/util" ) type Student struct { @@ -12,9 +14,9 @@ type Student struct { func DescribeStudents(c *gin.Context) { var students []Student - c.JSON(200, gin.H{ - "data": students, - "page": 1, - "pageSize": 15, - }) + var pageData base.SimplePageData + + pageData.Total = 0 + pageData.Data = students + util.RSuccessWithMsgJson(c, "分页获取学生列表成功!", pageData) } diff --git a/src/util/aes_utils.go b/src/util/aes_utils.go new file mode 100644 index 0000000..fb3ca5e --- /dev/null +++ b/src/util/aes_utils.go @@ -0,0 +1,113 @@ +package util + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "encoding/base64" + "errors" + "fmt" + "github.com/satori/go.uuid" + "lingye-gin/src/config" + "strings" +) + +// 高级加密标准(Advanced Encryption Standard, AES) +type AESUtils struct{} + +// 16,24,32位字符串的话, 分别对应AES-128, AES-192, AES-256 加密方法 +// key不能泄露 +func (AESUtils) GetRandomPwdKey() string { + u2 := uuid.NewV4() + return strings.Replace(u2.String(), "-", "", -1) +} + +// PKCS7 填充模式 +func (AESUtils) PKCS7Padding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + // Repeat()函数的功能是把切片[]byte{byte(padding)}复制padding个, 然后合并成新的字节切片返回 + padText := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padText...) +} + +// 填充的反向操作, 删除填充字符串 +func (AESUtils) PKCS7UnPadding(origData []byte) ([]byte, error) { + // 获取数据长度 + length := len(origData) + if length == 0 { + return nil, errors.New("加密字符串错误!") + } else { + // 获取填充字符串长度 + unPadding := int(origData[length-1]) + // 截取切片, 删除填充字节, 并且返回明文 + return origData[:(length - unPadding)], nil + } +} + +// 实现加密 +func (util AESUtils) AesEncrypt(origData []byte, key []byte) ([]byte, error) { + // 创建加密算法实例 + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + // 获取块的大小 + blockSize := block.BlockSize() + // 对数据进行填充,让数据长度满足需求 + origData = util.PKCS7Padding(origData, blockSize) + // 采用AES加密方法中CBC加密模式 + blocMode := cipher.NewCBCEncrypter(block, key[:blockSize]) + result := make([]byte, len(origData)) + // 执行加密 + blocMode.CryptBlocks(result, origData) + return result, nil +} + +// 实现解密 +func (util AESUtils) AesDeCrypt(cypted []byte, key []byte) ([]byte, error) { + // 创建加密算法实例 + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + // 获取块大小 + blockSize := block.BlockSize() + // 创建加密客户端实例 + blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) + origData := make([]byte, len(cypted)) + // 这个函数也可以用来解密 + blockMode.CryptBlocks(origData, cypted) + // 去除填充字符串 + origData, err = util.PKCS7UnPadding(origData) + if err != nil { + return nil, err + } + return origData, err +} + +// 加密base64 +func (util AESUtils) EnPwdCode(pwd []byte) (string, error) { + result, err := util.AesEncrypt(pwd, []byte(config.AppProps.Jwt.Secret)) + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(result), err +} + +// 解密 +func (util AESUtils) DePwdCode(pwd string) ([]byte, error) { + // 解密base64字符串 + pwdByte, err := base64.StdEncoding.DecodeString(pwd) + if err != nil { + return nil, err + } + // 执行AES解密 + return util.AesDeCrypt(pwdByte, []byte(config.AppProps.Jwt.Secret)) + +} +func (util AESUtils) Test() { + str := []byte("加密测试") + pwd, _ := util.EnPwdCode(str) + bt, _ := util.DePwdCode(pwd) + fmt.Println(string(bt)) +} diff --git a/src/util/http_utils.go b/src/util/http_utils.go new file mode 100644 index 0000000..f63d093 --- /dev/null +++ b/src/util/http_utils.go @@ -0,0 +1,49 @@ +package util + +import ( + "github.com/gin-gonic/gin" + "net/http" +) + +func RErrorJson(c *gin.Context, msg string, data interface{}) { + c.JSON(http.StatusBadRequest, gin.H{ + "code": http.StatusBadRequest, + "msg": msg, + "data": data, + }) + c.Abort() +} + +func RSuccessJson(c *gin.Context, data interface{}) { + c.JSON(http.StatusOK, gin.H{ + "code": http.StatusOK, + "msg": "success", + "data": data, + }) + c.Abort() +} + +func RSuccessMsg(c *gin.Context, msg string) { + c.JSON(http.StatusOK, gin.H{ + "code": http.StatusOK, + "msg": "success", + }) + c.Abort() +} + +func RErrorMsg(c *gin.Context, msg string) { + c.JSON(http.StatusBadRequest, gin.H{ + "code": http.StatusBadRequest, + "msg": "success", + }) + c.Abort() +} + +func RSuccessWithMsgJson(c *gin.Context, msg string, data interface{}) { + c.JSON(http.StatusOK, gin.H{ + "code": http.StatusOK, + "msg": msg, + "data": data, + }) + c.Abort() +} diff --git a/src/util/sign_utils.go b/src/util/sign_utils.go new file mode 100644 index 0000000..bad7f87 --- /dev/null +++ b/src/util/sign_utils.go @@ -0,0 +1,84 @@ +package util + +import ( + "crypto/md5" + "encoding/hex" + "fmt" + "github.com/gin-gonic/gin" + "lingye-gin/src/config" + "net/url" + "sort" + "strconv" + "time" +) + +// MD5 方法 +func MD5(str string) string { + s := md5.New() + s.Write([]byte(str)) + return hex.EncodeToString(s.Sum(nil)) +} + +// 获取当前时间戳 +func GetTimeUnix() int64 { + return time.Now().Unix() +} + +// 生成签名 +func CreateSign(params url.Values) string { + var key []string + var str = "" + for k := range params { + if k != "sn" { + key = append(key, k) + } + } + sort.Strings(key) + for i := 0; i < len(key); i++ { + if i == 0 { + str = fmt.Sprintf("%v=%v", key[i], params.Get(key[i])) + } else { + str = str + fmt.Sprintf("&%v=%v", key[i], params.Get(key[i])) + } + } + // 自定义签名算法 + sign := MD5(MD5(str) + MD5(config.AppProps.App.Name+config.AppProps.Jwt.Secret)) + return sign +} + +// 验证签名 +func VerifySign(c *gin.Context) { + var method = c.Request.Method + var ts int64 + var sn string + var req url.Values + + if method == "GET" { + req = c.Request.URL.Query() + sn = c.Query("sn") + ts, _ = strconv.ParseInt(c.Query("ts"), 10, 64) + + } else if method == "POST" { + _ = c.Request.ParseForm() + req = c.Request.PostForm + sn = c.PostForm("sn") + ts, _ = strconv.ParseInt(c.PostForm("ts"), 10, 64) + } else { + RErrorJson(c, "Illegal requests", "") + return + } + + exp, _ := strconv.ParseInt(string(rune(config.AppProps.Jwt.Expiry)), 10, 64) + + // 验证过期时间 + if ts > GetTimeUnix() || GetTimeUnix()-ts >= exp { + RErrorJson(c, "Ts Error", "") + return + } + + // 验证签名 + if sn == "" || sn != CreateSign(req) { + RErrorJson(c, "Sn Error", "") + return + } +} diff --git a/src/util/util.go b/src/util/util.go index c9ec764..01eb0e1 100644 --- a/src/util/util.go +++ b/src/util/util.go @@ -1,48 +1,11 @@ package util import ( - "crypto/md5" - "encoding/hex" - "fmt" - "github.com/gin-gonic/gin" - "lingye-gin/src/config" - "net/http" - "net/url" + "lingye-gin/src/base" "reflect" - "sort" "strconv" - "time" ) -// 打印 -func Print(i interface{}) { - fmt.Println("---") - fmt.Println(i) - fmt.Println("---") -} - -// 返回JSON -func RetJson(code, msg string, data interface{}, c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "code": code, - "msg": msg, - "data": data, - }) - c.Abort() -} - -// 获取当前时间戳 -func GetTimeUnix() int64 { - return time.Now().Unix() -} - -// MD5 方法 -func MD5(str string) string { - s := md5.New() - s.Write([]byte(str)) - return hex.EncodeToString(s.Sum(nil)) -} - // 修正分页参数 func FixPage(page uint, pageSize uint) (p uint, ps uint) { if page == 0 { @@ -54,65 +17,6 @@ func FixPage(page uint, pageSize uint) (p uint, ps uint) { return p, ps } -// 生成签名 -func CreateSign(params url.Values) string { - var key []string - var str = "" - for k := range params { - if k != "sn" { - key = append(key, k) - } - } - sort.Strings(key) - for i := 0; i < len(key); i++ { - if i == 0 { - str = fmt.Sprintf("%v=%v", key[i], params.Get(key[i])) - } else { - str = str + fmt.Sprintf("&%v=%v", key[i], params.Get(key[i])) - } - } - // 自定义签名算法 - sign := MD5(MD5(str) + MD5(config.AppProps.App.Name+config.AppProps.Jwt.Secret)) - return sign -} - -// 验证签名 -func VerifySign(c *gin.Context) { - var method = c.Request.Method - var ts int64 - var sn string - var req url.Values - - if method == "GET" { - req = c.Request.URL.Query() - sn = c.Query("sn") - ts, _ = strconv.ParseInt(c.Query("ts"), 10, 64) - - } else if method == "POST" { - _ = c.Request.ParseForm() - req = c.Request.PostForm - sn = c.PostForm("sn") - ts, _ = strconv.ParseInt(c.PostForm("ts"), 10, 64) - } else { - RetJson("500", "Illegal requests", "", c) - return - } - - exp, _ := strconv.ParseInt(string(rune(config.AppProps.Jwt.Expiry)), 10, 64) - - // 验证过期时间 - if ts > GetTimeUnix() || GetTimeUnix()-ts >= exp { - RetJson("500", "Ts Error", "", c) - return - } - - // 验证签名 - if sn == "" || sn != CreateSign(req) { - RetJson("500", "Sn Error", "", c) - return - } -} - // source 有数据的结构体 // target 空的结构体 func StructCopy(source interface{}, target interface{}) { @@ -133,3 +37,11 @@ func StringToUInt64(number string) uint64 { result, _ := strconv.Atoi(number) return uint64(result) } + +// 构成分页对象 +func BuildPageData(data interface{}, total int) base.SimplePageData { + var pageData base.SimplePageData + pageData.Data = data + pageData.Total = total + return pageData +}