pixelqm
5 years ago
14 changed files with 450 additions and 0 deletions
-
38QMPlusServer/config/config.go
-
9QMPlusServer/config/dbconfig/config.json
-
21QMPlusServer/go.mod
-
21QMPlusServer/init/initMysql.go
-
14QMPlusServer/init/initRouter.go
-
32QMPlusServer/main.go
-
138QMPlusServer/middleware/jwt.go
-
60QMPlusServer/middleware/logger.go
-
9QMPlusServer/model/interface.go
-
21QMPlusServer/model/user.go
-
41QMPlusServer/tools/des.go
-
34QMPlusServer/tools/hasGap.go
-
12QMPlusServer/tools/md5.go
-
BINREADME.md
@ -0,0 +1,38 @@ |
|||||
|
package config |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"github.com/fsnotify/fsnotify" |
||||
|
"github.com/spf13/viper" |
||||
|
) |
||||
|
|
||||
|
type Config struct { |
||||
|
Admin Admin |
||||
|
} |
||||
|
type Admin struct { |
||||
|
UserName string |
||||
|
Password string |
||||
|
Path string |
||||
|
Dbname string |
||||
|
Config string |
||||
|
} |
||||
|
|
||||
|
var Dbconfig Config |
||||
|
|
||||
|
func init() { |
||||
|
v := viper.New() |
||||
|
v.SetConfigName("config") // 设置配置文件名 (不带后缀)
|
||||
|
v.AddConfigPath("./config/dbconfig/") // 第一个搜索路径
|
||||
|
v.SetConfigType("json") |
||||
|
err := v.ReadInConfig() // 搜索路径,并读取配置数据
|
||||
|
if err != nil { |
||||
|
panic(fmt.Errorf("Fatal error config file: %s \n", err)) |
||||
|
} |
||||
|
v.WatchConfig() |
||||
|
v.OnConfigChange(func(e fsnotify.Event) { |
||||
|
fmt.Println("Config file changed:", e.Name) |
||||
|
}) |
||||
|
if err := v.Unmarshal(&Dbconfig); err != nil { |
||||
|
fmt.Println(err) |
||||
|
} |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
{ |
||||
|
"admin": { |
||||
|
"userName": "root", |
||||
|
"password": "Aa@6447985", |
||||
|
"path": "127.0.0.1:3306", |
||||
|
"dbname": "zhongzerong", |
||||
|
"config": "charset=utf8&parseTime=True&loc=Local" |
||||
|
} |
||||
|
} |
@ -0,0 +1,21 @@ |
|||||
|
module main |
||||
|
|
||||
|
go 1.12 |
||||
|
|
||||
|
require ( |
||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible |
||||
|
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect |
||||
|
github.com/fsnotify/fsnotify v1.4.7 |
||||
|
github.com/gin-gonic/gin v1.4.0 |
||||
|
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect |
||||
|
github.com/jinzhu/gorm v1.9.10 |
||||
|
github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 // indirect |
||||
|
github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f |
||||
|
github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect |
||||
|
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 |
||||
|
github.com/satori/go.uuid v1.2.0 |
||||
|
github.com/sirupsen/logrus v1.2.0 |
||||
|
github.com/spf13/viper v1.4.0 |
||||
|
github.com/swaggo/gin-swagger v1.2.0 |
||||
|
github.com/tebeka/strftime v0.1.3 // indirect |
||||
|
) |
@ -0,0 +1,21 @@ |
|||||
|
package init |
||||
|
|
||||
|
import ( |
||||
|
"github.com/jinzhu/gorm" |
||||
|
_ "github.com/jinzhu/gorm/dialects/mysql" |
||||
|
"log" |
||||
|
"main/config" |
||||
|
) |
||||
|
|
||||
|
var DEFAULTDB *gorm.DB |
||||
|
|
||||
|
func InitMysql(admin config.Admin) { |
||||
|
if db, err := gorm.Open("mysql", admin.UserName+":"+admin.Password+"@("+admin.Path+")/"+admin.Dbname+"?"+admin.Config); err != nil { |
||||
|
log.Printf("DEFAULTDB数据库启动异常%S", err) |
||||
|
} else { |
||||
|
DEFAULTDB = db |
||||
|
DEFAULTDB.DB().SetMaxIdleConns(10) |
||||
|
DEFAULTDB.DB().SetMaxIdleConns(100) |
||||
|
DEFAULTDB.AutoMigrate() |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
package init |
||||
|
|
||||
|
import ( |
||||
|
"github.com/gin-gonic/gin" |
||||
|
"github.com/swaggo/gin-swagger" |
||||
|
"github.com/swaggo/gin-swagger/swaggerFiles" |
||||
|
) |
||||
|
|
||||
|
var Router = gin.Default() |
||||
|
|
||||
|
func InitRouter() { |
||||
|
Router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) |
||||
|
//Router.Use(middleware.Logger())
|
||||
|
} |
@ -0,0 +1,32 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"main/config" |
||||
|
"main/init" |
||||
|
"net/http" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
// @Summary 打印测试功能
|
||||
|
// @title Swagger Example API
|
||||
|
// @version 0.0.1
|
||||
|
// @description This is a sample server Petstore server.
|
||||
|
// @BasePath /api/v1
|
||||
|
// @Host 127.0.0.1:8080
|
||||
|
// @Produce json
|
||||
|
// @Param name query string true "Name"
|
||||
|
// @Success 200 {string} json "{"code":200,"data":"name","msg":"ok"}"
|
||||
|
// @Router / [get]
|
||||
|
func main() { |
||||
|
init.InitMysql(config.Dbconfig.Admin) |
||||
|
defer init.DEFAULTDB.Close() |
||||
|
init.InitRouter() |
||||
|
s := &http.Server{ |
||||
|
Addr: ":8888", |
||||
|
Handler: init.Router, |
||||
|
ReadTimeout: 10 * time.Second, |
||||
|
WriteTimeout: 10 * time.Second, |
||||
|
MaxHeaderBytes: 1 << 20, |
||||
|
} |
||||
|
_ = s.ListenAndServe() |
||||
|
} |
@ -0,0 +1,138 @@ |
|||||
|
package middleware |
||||
|
|
||||
|
import ( |
||||
|
"errors" |
||||
|
"github.com/dgrijalva/jwt-go" |
||||
|
"github.com/gin-gonic/gin" |
||||
|
uuid "github.com/satori/go.uuid" |
||||
|
"net/http" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
func JWTAuth() gin.HandlerFunc { |
||||
|
return func(c *gin.Context) { |
||||
|
token := c.Request.Header.Get("x-token") |
||||
|
if token == "" { |
||||
|
c.JSON(http.StatusOK, gin.H{ |
||||
|
"success": false, |
||||
|
"msg": "未登录或非法访问", |
||||
|
"data": gin.H{}, |
||||
|
}) |
||||
|
c.Abort() |
||||
|
return |
||||
|
} |
||||
|
j := NewJWT() |
||||
|
// parseToken 解析token包含的信息
|
||||
|
claims, err := j.ParseToken(token) |
||||
|
if err != nil { |
||||
|
if err == TokenExpired { |
||||
|
c.JSON(http.StatusOK, gin.H{ |
||||
|
"success": false, |
||||
|
"msg": "授权已过期", |
||||
|
}) |
||||
|
c.Abort() |
||||
|
return |
||||
|
} |
||||
|
c.JSON(http.StatusOK, gin.H{ |
||||
|
"success": false, |
||||
|
"msg": err.Error(), |
||||
|
}) |
||||
|
c.Abort() |
||||
|
return |
||||
|
} |
||||
|
c.Set("claims", claims) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
type JWT struct { |
||||
|
SigningKey []byte |
||||
|
} |
||||
|
|
||||
|
var ( |
||||
|
TokenExpired error = errors.New("Token is expired") |
||||
|
TokenNotValidYet error = errors.New("Token not active yet") |
||||
|
TokenMalformed error = errors.New("That's not even a token") |
||||
|
TokenInvalid error = errors.New("Couldn't handle this token:") |
||||
|
SignKey string = "newtrekWang" |
||||
|
) |
||||
|
|
||||
|
type CustomClaims struct { |
||||
|
UUID uuid.UUID |
||||
|
ID uint |
||||
|
AuthorityID uint |
||||
|
jwt.StandardClaims |
||||
|
} |
||||
|
|
||||
|
func NewJWT() *JWT { |
||||
|
return &JWT{ |
||||
|
[]byte(GetSignKey()), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//获取token
|
||||
|
func GetSignKey() string { |
||||
|
return SignKey |
||||
|
} |
||||
|
|
||||
|
// 这是SignKey
|
||||
|
func SetSignKey(key string) string { |
||||
|
SignKey = key |
||||
|
return SignKey |
||||
|
} |
||||
|
|
||||
|
//创建一个token
|
||||
|
func (j *JWT) CreateToken(claims CustomClaims) (string, error) { |
||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) |
||||
|
return token.SignedString(j.SigningKey) |
||||
|
} |
||||
|
|
||||
|
//解析 token
|
||||
|
func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) { |
||||
|
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (i interface{}, e error) { |
||||
|
return j.SigningKey, nil |
||||
|
}) |
||||
|
if err != nil { |
||||
|
if ve, ok := err.(*jwt.ValidationError); ok { |
||||
|
if ve.Errors&jwt.ValidationErrorMalformed != 0 { |
||||
|
return nil, TokenMalformed |
||||
|
} else if ve.Errors&jwt.ValidationErrorExpired != 0 { |
||||
|
// Token is expired
|
||||
|
return nil, TokenExpired |
||||
|
} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 { |
||||
|
return nil, TokenNotValidYet |
||||
|
} else { |
||||
|
return nil, TokenInvalid |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if token != nil { |
||||
|
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { |
||||
|
return claims, nil |
||||
|
} |
||||
|
return nil, TokenInvalid |
||||
|
|
||||
|
} else { |
||||
|
return nil, TokenInvalid |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
// 更新token
|
||||
|
func (j *JWT) RefreshToken(tokenString string) (string, error) { |
||||
|
jwt.TimeFunc = func() time.Time { |
||||
|
return time.Unix(0, 0) |
||||
|
} |
||||
|
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) { |
||||
|
return j.SigningKey, nil |
||||
|
}) |
||||
|
if err != nil { |
||||
|
return "", err |
||||
|
} |
||||
|
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { |
||||
|
jwt.TimeFunc = time.Now |
||||
|
claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix() |
||||
|
return j.CreateToken(*claims) |
||||
|
} |
||||
|
return "", TokenInvalid |
||||
|
} |
@ -0,0 +1,60 @@ |
|||||
|
package middleware |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"github.com/gin-gonic/gin" |
||||
|
"github.com/lestrrat/go-file-rotatelogs" |
||||
|
"github.com/rifflock/lfshook" |
||||
|
"github.com/sirupsen/logrus" |
||||
|
"os" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
func Logger() gin.HandlerFunc { |
||||
|
logClient := logrus.New() |
||||
|
//禁止logrus的输出
|
||||
|
src, err := os.OpenFile(os.DevNull, os.O_APPEND|os.O_WRONLY, os.ModeAppend) |
||||
|
if err != nil { |
||||
|
fmt.Println("err", err) |
||||
|
} |
||||
|
logClient.Out = src |
||||
|
logClient.SetLevel(logrus.DebugLevel) |
||||
|
apiLogPath := "api.log" |
||||
|
logWriter, err := rotatelogs.New( |
||||
|
apiLogPath+".%Y-%m-%d-%H-%M.log", |
||||
|
rotatelogs.WithLinkName(apiLogPath), // 生成软链,指向最新日志文件
|
||||
|
rotatelogs.WithMaxAge(7*24*time.Hour), // 文件最大保存时间
|
||||
|
rotatelogs.WithRotationTime(24*time.Hour), // 日志切割时间间隔
|
||||
|
) |
||||
|
writeMap := lfshook.WriterMap{ |
||||
|
logrus.InfoLevel: logWriter, |
||||
|
logrus.FatalLevel: logWriter, |
||||
|
} |
||||
|
lfHook := lfshook.NewHook(writeMap, &logrus.JSONFormatter{}) |
||||
|
logClient.AddHook(lfHook) |
||||
|
|
||||
|
return func(c *gin.Context) { |
||||
|
// 开始时间
|
||||
|
start := time.Now() |
||||
|
// 处理请求
|
||||
|
c.Next() |
||||
|
// 结束时间
|
||||
|
end := time.Now() |
||||
|
//执行时间
|
||||
|
latency := end.Sub(start) |
||||
|
|
||||
|
path := c.Request.URL.Path |
||||
|
clientIP := c.ClientIP() |
||||
|
method := c.Request.Method |
||||
|
statusCode := c.Writer.Status() |
||||
|
buf := make([]byte, 1024) |
||||
|
n, _ := c.Request.Body.Read(buf) |
||||
|
requestParams := buf[0:n] |
||||
|
logClient.Infof("| %3d | %13v | %15s | %s %s |%s|", |
||||
|
statusCode, |
||||
|
latency, |
||||
|
clientIP, |
||||
|
method, path, requestParams, |
||||
|
) |
||||
|
} |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
package model |
||||
|
|
||||
|
// 因为我也不确定项目要不要多人维护 所以定义了CURD接口 凡是对数据库进行简单CURD操作 请实现此接口 默认首位返回 error
|
||||
|
type CURD interface { |
||||
|
Create() (error, interface{}) |
||||
|
Updata() (error, interface{}) |
||||
|
Read() (error, interface{}) |
||||
|
Delete() (error, interface{}) |
||||
|
} |
@ -0,0 +1,21 @@ |
|||||
|
package model |
||||
|
|
||||
|
import ( |
||||
|
"github.com/jinzhu/gorm" |
||||
|
uuid "github.com/satori/go.uuid" |
||||
|
) |
||||
|
|
||||
|
type User struct { |
||||
|
gorm.Model `json:"-"` |
||||
|
UUID uuid.UUID `json:"uuid"` |
||||
|
UserName string `json:"userName"` |
||||
|
PassWord string `json:"passWord"` |
||||
|
NickName string `json:"nickName" gorm:"default:'galeone'"` |
||||
|
HeaderImg string `json:"headerImg" gorm:"default:'galeone'"` |
||||
|
//Propertie // 多余属性自行添加
|
||||
|
//PropertieId uint // 自动关联 Propertie 的Id 附加属性过多 建议创建一对一关系
|
||||
|
} |
||||
|
|
||||
|
//type Propertie struct {
|
||||
|
// gorm.Model
|
||||
|
//}
|
@ -0,0 +1,41 @@ |
|||||
|
package tools |
||||
|
|
||||
|
import ( |
||||
|
"bytes" |
||||
|
"crypto/cipher" |
||||
|
"crypto/des" |
||||
|
) |
||||
|
|
||||
|
func padding(src []byte, blocksize int) []byte { |
||||
|
n := len(src) |
||||
|
padnum := blocksize - n%blocksize |
||||
|
pad := bytes.Repeat([]byte{byte(padnum)}, padnum) |
||||
|
dst := append(src, pad...) |
||||
|
return dst |
||||
|
} |
||||
|
|
||||
|
func unpadding(src []byte) []byte { |
||||
|
n := len(src) |
||||
|
unpadnum := int(src[n-1]) |
||||
|
dst := src[:n-unpadnum] |
||||
|
return dst |
||||
|
} |
||||
|
|
||||
|
func EncryptDES(src []byte) []byte { |
||||
|
key := []byte("qimiao66") |
||||
|
block, _ := des.NewCipher(key) |
||||
|
src = padding(src, block.BlockSize()) |
||||
|
blockmode := cipher.NewCBCEncrypter(block, key) |
||||
|
blockmode.CryptBlocks(src, src) |
||||
|
return src |
||||
|
} |
||||
|
|
||||
|
func DecryptDES(src []byte) []byte { |
||||
|
key := []byte("qimiao66") |
||||
|
block, _ := des.NewCipher(key) |
||||
|
blockmode := cipher.NewCBCDecrypter(block, key) |
||||
|
blockmode.CryptBlocks(src, src) |
||||
|
src = unpadding(src) |
||||
|
return src |
||||
|
|
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
// 空值校验工具 仅用于检验空字符串 其余类型请勿使用
|
||||
|
|
||||
|
package tools |
||||
|
|
||||
|
import ( |
||||
|
"errors" |
||||
|
"fmt" |
||||
|
"reflect" |
||||
|
) |
||||
|
|
||||
|
func HasGap(input interface{}) error { |
||||
|
getType := reflect.TypeOf(input) |
||||
|
fmt.Println("获取类型 :", getType.Name()) |
||||
|
|
||||
|
getValue := reflect.ValueOf(input) |
||||
|
fmt.Println("所有字段", getValue) |
||||
|
|
||||
|
// 获取方法字段
|
||||
|
for i := 0; i < getType.NumField(); i++ { |
||||
|
field := getType.Field(i) |
||||
|
value := getValue.Field(i).Interface() |
||||
|
fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value) |
||||
|
if value == "" { |
||||
|
return errors.New(fmt.Sprintf("%s为空", field.Name)) |
||||
|
} |
||||
|
} |
||||
|
// 获取方法
|
||||
|
// 1. 先获取interface的reflect.Type,然后通过.NumMethod进行遍历
|
||||
|
//for i := 0; i < getType.NumMethod(); i++ {
|
||||
|
// m := getType.Method(i)
|
||||
|
// fmt.Printf("%s: %v\n", m.Name, m.Type)
|
||||
|
//}
|
||||
|
return nil |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
package tools |
||||
|
|
||||
|
import ( |
||||
|
"crypto/md5" |
||||
|
"encoding/hex" |
||||
|
) |
||||
|
|
||||
|
func MD5V(str string) string { |
||||
|
h := md5.New() |
||||
|
h.Write([]byte(str)) |
||||
|
return hex.EncodeToString(h.Sum(nil)) |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue