主要使用gin框架、gorm
go mod init
go mod tidy
依赖
go get github.com/gin-gonic/gin@v1.8.1
go get gorm.io/gorm
go get gorm.io/driver/mysql
go get github.com/sirupsen/logrus
go get github.com/lestrrat-go/file-rotatelogs
go get github.com/rifflock/lfshook
go get github.com/go-redis/redis/v8@v8.11.5
go get github.com/mojocn/base64Captcha@v1.3.1
go get github.com/dgrijalva/jwt-go
go get gopkg.in/yaml.v3 go get github.com/go-yaml/yaml
go get -u github.com/wenlng/go-user-agent
go get github.com/gogf/gf
go get github.com/swaggo/files
go get github.com/swaggo/gin-swagger
go get github.com/IBM/sarama
go get github.com/Shopify/sarama@v1.19
config.yaml
server:
address: :2002
model: debug
#release
#model: release
db:
dialects: mysql
host: 127.0.0.1
port: 3306
db: go_vue_manage
username: root
password: root
charset: utf8
maxIdle: 50
maxOpen: 100
redis:
address: 127.0.0.1:6379
password: 123456
imageSetting:
uploadDir: /go_vue_manage/api/upload
imageHost: http://localhost:2002
log:
path: ./log
name: sys
model: console
#model: file
config.go
package config
import (
"io/ioutil"
"gopkg.in/yaml.v3"
)
type config struct{
Server server `yaml:"server"`
Db db `yaml:"db"`
Redis redis `yaml:"redis"`
ImageSetting imageSetting `yaml:"imageSetting"`
Log log `yaml:"log"`
}
type server struct{
Address string `yaml:"address"`
Model string `yaml:model`
}
type db struct{
Dialects string `yaml:"dialects`
Host string `yaml:"host`
Port int `yaml:"port`
DB string `yaml:"db"`
Username string `yaml:"username`
Password string `yaml:"password`
Charset string `yaml:"charset`
MaxIdle int `yaml:"maxIdle`
MaxOpen int `yaml:"maxOpen`
}
type redis struct{
Address string `yaml:"address`
Password string `yaml:"password`
}
type imageSetting struct{
UploadDir string `yaml:"uploadDir`
ImageHost string `yaml:"imageHost`
}
type log struct{
Path string `yaml:"path`
Name string `yaml:"name`
Model string `yaml:"model`
}
var Config *config
func init(){
yamlFile,err :=ioutil.ReadFile("./config.yaml")
if err != nil {
panic(err)
}
err=yaml.Unmarshal(yamlFile,&Config)
if err !=nil {
panic(err)
}
}
db.go
package db
import (
"fmt"
"go_vue_manage/common/config"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
var Db *gorm.DB
func SetUpDBLink() error {
var err error
var dbConfig = config.Config.Db
url:=fmt.Sprintln("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local",dbConfig.Username,dbConfig.Password,dbConfig.Host,dbConfig.Port,dbConfig.DB,dbConfig.Charset)
Db,err =gorm.Open(mysql.Open(url),&gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
DisableForeignKeyConstraintWhenMigrating: true,
})
if err != nil {
panic(err)
}
if Db.Error !=nil {
panic(Db.Error)
}
sqlDb,err :=Db.DB()
sqlDb.SetMaxIdleConns(dbConfig.MaxIdle)
sqlDb.SetMaxOpenConns(dbConfig.MaxOpen)
return nil
}
redis.go
package redis
import (
"context"
"go_vue_manage/common/config"
"github.com/go-redis/redis/v8"
)
var (
RedisDb *redis.Client
)
func SetUpRedisLink() error {
var ctx=context.Background()
RedisDb = redis.NewClient(&redis.Options{Addr: config.Config.Redis.Address,Password:
config.Config.Redis.Password,DB: 0})
_,err :=RedisDb.Ping(ctx).Result()
if err !=nil {
return err
}
return nil
}
cors.go
package middleware
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/ginS"
)
func Cors() gin.HandlerFunc {
return func(ctx *gin.Context) {
method:=ctx.Request.Method
ctx.Header( "Aceess-Control-Allow-Or1gin","*")
ctx.Header( "Aceess-Controt-Allow-Headers","Content-type, AceessToken,X-CSRF-Token, Authorization, Token")
ctx.Header( "Access-Control-Allow-Methods", "POST,GET,OPTIONS")
ctx.Header( "Acess-Control-Expose-Headers", "content-Length, Access-Control-Allow-Origin, Acess-Control-Allow-Headers, Content-Type")
ctx.Header( "Access-Control-Allow-Credentials","true")
if method == "OPTIONS" {
ctx.AbortWithStatus(http.StatusNoContent)
}
ctx.Next()
}
}
code.go
package result
type Codes struct{
SUCCESS uint
FAILED uint
NOAUTH uint
AUTHFORMATERROR uint
Message map[uint]string
}
var ApiCode =&Codes{
SUCCESS: 200,
FAILED: 501,
NOAUTH: 401,
AUTHFORMATERROR: 403,
}
func init(){
ApiCode.Message=map[uint]string{
ApiCode.SUCCESS:"成功",
ApiCode.FAILED:"失败",
ApiCode.NOAUTH:"权限不足",
ApiCode.AUTHFORMATERROR:"认证失败",
}
}
func (c *Codes) GetMessage(code uint) string {
message,ok:=c.Message[code]
if !ok {
return ""
}
return message
}
result.go
package result
import (
"net/http"
"github.com/gin-gonic/gin"
)
type Results struct {
Code int `json:"code"`
Message string `json:"message`
Data interface{} `json:"data"`
}
func Success(c *gin.Context,data interface{}){
if data ==nil {
data=gin.H{}
}
res:=Results{}
res.Code=int(ApiCode.SUCCESS)
res.Message=ApiCode.GetMessage(ApiCode.SUCCESS)
res.Data=data
c.JSON(http.StatusOK,res)
}
func Failed(c *gin.Context,code int,message string){
res:=Results{}
res.Code=code
res.Message=message
res.Data=gin.H{}
c.JSON(http.StatusOK,res)
}
authMiddleware.go
package middleware
import (
"go_vue_manage/constant"
"go_vue_manage/common/result"
"strings"
"github.com/gin-gonic/gin"
)
func AuthMiddleware() func(c *gin.Context){
return func(c *gin.Context) {
authHeader:=c.Request.Header.Get("Authorization")
if authHeader == ""{
result.Failed(c,int(result.ApiCode.NOAUTH),result.ApiCode.GetMessage(result.ApiCode.NOAUTH))
c.Abort()
return
}
parts:=strings.SplitN(authHeader, " ",2)
if !(len(parts)==2 && parts[0]=="Bearer") {
result.Failed(c,int(result.ApiCode.AUTHFORMATERROR),result.ApiCode.GetMessage(result.ApiCode.AUTHFORMATERROR))
c.Abort()
return
}
var token ="token";
c.Set(constant.ContextKeyUserObj,token)
c.Next()
}
}
logger.go
package log
import (
"go_vue_manage/common/config"
"os"
"path/filepath"
"time"
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
"github.com/rifflock/lfshook"
"github.com/sirupsen/logrus"
)
var log *logrus.Logger
var logToFile *logrus.Logger
var loggerFile string
func setLogFile(file string){
loggerFile=file
}
func init(){
setLogFile(filepath.Join(config.Config.Log.Path,config.Config.Log.Name))
}
func Log() *logrus.Logger{
if config.Config.Log.Model == "file" {
return logFile()
}else{
if log ==nil {
log=logrus.New()
log.Out=os.Stdout
log.Formatter=&logrus.JSONFormatter{TimestampFormat: "2008-01-01 15:04:05"}
log.SetLevel(logrus.DebugLevel)
}
}
return log
}
func logFile() *logrus.Logger{
if logToFile ==nil {
logToFile=logrus.New()
logToFile.SetLevel(logrus.DebugLevel)
logWriter,_:=rotatelogs.New(
loggerFile+"_%Y%m%d.log",
//保存时间
rotatelogs.WithMaxAge(30*24*time.Hour),
//切割时间
rotatelogs.WithRotationTime(24*time.Hour),
)
writeMap:=lfshook.WriterMap{
logrus.InfoLevel: logWriter,
logrus.FatalLevel: logWriter,
logrus.DebugLevel: logWriter,
logrus.WarnLevel: logWriter,
logrus.ErrorLevel: logWriter,
logrus.PanicLevel: logWriter,
}
lfHook:=lfshook.NewHook(writeMap,&logrus.JSONFormatter{
TimestampFormat: "2006-01-02 15:04:05",
})
logToFile.AddHook(lfHook)
}
return logToFile
}
main.go
package main
import (
"context"
"go_vue_manage/common/config"
"go_vue_manage/log"
"go_vue_manage/pkg/db"
"go_vue_manage/router"
"net/http"
"os"
"os/signal"
"time"
"github.com/gin-gonic/gin"
)
func main() {
log := log.Log()
gin.SetMode(config.Config.Server.Model)
router:=router.InitRouter()
srv:=&http.Server{
Addr:config.Config.Server.Address,
Handler:router,
}
go func () {
if err:=srv.ListenAndServe();err !=nil && err !=http.ErrServerClosed{
log.Info("listen %s \n",err)
}
log.Info(" listen %s \n",config.Config.Server.Address)
}()
quit:=make(chan os.Signal)
signal.Notify(quit,os.Interrupt)
<-quit
log.Info("Shutdown Server...")
ctx,cancel:=context.WithTimeout(context.Background(),5*time.Second)
defer cancel()
if err :=srv.Shutdown(ctx);err!=nil {
log.Info("Shutdown",err)
}
log.Info("Server exiting")
}
func init(){
db.SetUpDBLink()
// redis.SetUpRedisLink()
}
redisStore.go
package util
import (
"context"
"go_vue_manage/constant"
"go_vue_manage/pkg/redis"
"log"
"time"
)
var ctx=context.Background()
type RedisStore struct{
}
func (r RedisStore) Set(id string , value string){
key:=constant.LOGIN_CODE+id
err:=redis.RedisDb.Set(ctx,key,value,time.Minute*5).Err()
if err !=nil {
log.Panic(err.Error())
}
}
func (r RedisStore) Get(id string,clear bool) string{
key:=constant.LOGIN_CODE+id
val,err:=redis.RedisDb.Get(ctx,key).Result()
if err!=nil {
return ""
}
return val
}
func (c RedisStore) Verify(id string ,val string,clear bool) bool{
v:=RedisStore{}.Get(id,clear)
return v==val
}
constant.go
package constant
const (
ContextKeyUserObj = "authedUserObj"
LOGIN_CODE = "loginCode"
)
router.go
package router
import (
"go_vue_manage/common/config"
"go_vue_manage/middleware"
"net/http"
"github.com/gin-gonic/gin"
// "github.com/swaggo/gin-swagger"
// "github.com/swaggo/files"
)
func InitRouter() *gin.Engine{
router:=gin.New()
router.Use(gin.Recovery())//宕机恢复
router.Use(middleware.Cors())
router.StaticFS(config.Config.ImageSetting.UploadDir,http.Dir(config.Config.ImageSetting.UploadDir))
register(router)
return router
}
func register(router *gin.Engine) {
//todo
// router.GET("/api/captcha",controller.Capycha)
// router.GET("/swagger/*any",ginSwagger.WrapHandler(swaggerFiles.Handler))
}
service.captcha.go
// 验证码 服务层
package service
import (
"go_vue_manage/common/util"
"image/color"
"github.com/mojocn/base64Captcha"
)
var store=util.RedisStore{}
func CaptMake()(id,b64s string){
var driver base64Captcha.Driver
var driverString base64Captcha.DriverString
captchaConfih:=base64Captcha.DriverString{
Height: 60,
Width: 200,
NoiseCount: 0,
ShowLineOptions: 2 | 4,
Length: 6,
Source: "1234567890qwertyuioplkjhgfdsazxcvbnm",
BgColor: &color.RGBA{
R:3,
G:102,
B: 214,
A: 125,
},
Fonts: []string{"wqy-microhei.ttc"},
}
driverString=captchaConfih
driver=driverString.ConvertFonts()
captcha:=base64Captcha.NewCaptcha(driver,store)
lid,lb64s,_:=captcha.Generate()
return lid,lb64s
}
func CaptVerify(id string ,capt string) bool{
if store.Verify(id,capt,false) {
return true
}
return false
}
controller.captcha.go
package controller
import (
"fmt"
"go_vue_manage/constant"
"go_vue_manage/api/entity"
"go_vue_manage/api/service"
"go_vue_manage/common/result"
"go_vue_manage/common/util"
"strings"
"github.com/gin-gonic/gin"
)
var dbstore =util.DBStore{}
func Capycha(c *gin.Context){
fmt.Println("Capycha")
id,base64Imag:=service.CaptMake()
result.Success(c,map[string]interface{}{
"idKey":id,"image":base64Imag})
}
func Login(c *gin.Context){
user:=entity.Userdto{}
c.ShouldBindJSON(&user)
fmt.Println(user)
pwd:=user.Password
//取出数据库hash密码
dbUser:=dbstore.GetUserByName(user.Username)
fmt.Println(dbUser)
succ:= util.CheckPasswordHash(pwd,dbUser[0].Password)
if succ {
token,err:=util.GenerateJWTToken(int64(dbUser[0].ID),dbUser[0].Username)
if err !=nil {
panic(err)
}
fmt.Println(token)
result.Success(c,map[string]interface{}{
"token":token,
})
}else{
result.Failed(c,int(result.ApiCode.AUTHFORMATERROR),constant.FAIL_CODE)
}
}
func GetUser(c *gin.Context){
username:=c.Query("username")
authHeader:=c.Request.Header.Get("Authorization")
if authHeader == ""{
result.Failed(c,int(result.ApiCode.NOAUTH),result.ApiCode.GetMessage(result.ApiCode.NOAUTH))
c.Abort()
return
}
parts:=strings.SplitN(authHeader, " ",2)
if !(len(parts)==2 && parts[0]=="Bearer") {
result.Failed(c,int(result.ApiCode.AUTHFORMATERROR),result.ApiCode.GetMessage(result.ApiCode.AUTHFORMATERROR))
c.Abort()
return
}
var token =parts[1];
fmt.Println(token)
claim,err:=util.VerifyJWTToken(token)
if err!=nil {
panic(err)
}
var claimUserName string
claimUserName=claim.Username
fmt.Println("--------"+username)
fmt.Println("---------"+claimUserName)
fmt.Println("555555----%s",strings.EqualFold(username, claimUserName))
fmt.Println("555555----%s",strings.EqualFold(username, claim.Username))
if strings.Compare((*claim).Username,username)==0 {
result.Success(c,map[string]interface{}{
"userInfo":claim,
})
}else{
result.Failed(c,int(result.ApiCode.AUTHFORMATERROR),constant.FAIL_CODE)
}
}
func Register(c *gin.Context){
user:=entity.Userdto{}
c.ShouldBindJSON(&user)
fmt.Println(user)
pwd:=user.Password
pwdHash,err:=util.HashPassword(pwd)
if err !=nil {
panic(err)
}
user.Password=pwdHash
succ:=dbstore.InsertUser(user)
result.Success(c,map[string]interface{}{
"idKey":user.ID,"succ":succ})
}
jwtStore.go
package util
import (
"fmt"
"time"
"golang.org/x/crypto/bcrypt"
"github.com/dgrijalva/jwt-go"
)
// Define a secret key for signing and verifying tokens
var jwtSecret = []byte("secret-value")
// CustomClaims represents the JWT claims that you want to include
type CustomClaims struct {
UserID int64 `json:"user_id"`
Username string `json:"username"`
jwt.StandardClaims
}
// func main() {
// // Generate JWT token
// token, err := GenerateJWTToken(1, "example_user")
// if err != nil {
// fmt.Println("Error generating JWT token:", err)
// return
// }
// fmt.Println("Generated JWT token:", token)
// // Verify JWT token
// claims, err := VerifyJWTToken(token)
// if err != nil {
// fmt.Println("JWT verification failed:", err)
// return
// }
// fmt.Println("JWT token verified. UserID:", claims.UserID, "Username:", claims.Username)
// }
// GenerateJWTToken generates a new JWT token for the user
func GenerateJWTToken(userID int64, username string) (string, error) {
fmt.Println(userID)
fmt.Println(username)
claims := CustomClaims{
UserID: userID,
Username: username,
StandardClaims: jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Hour * 24).Unix(), // Token expiration time
Issuer: "go_vue_manage",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString(jwtSecret)
if err != nil {
return "", err
}
return tokenString, nil
}
func HashPassword(password string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(hash), nil
}
// CheckPasswordHash 验证密码与哈希值是否匹配 //hash是数据库存储的密码hash,和客户输入的密码进行比较判断是否匹配,因为每次加密的hash是随机的,需要反向校验
func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
// VerifyJWTToken verifies and parses a JWT token
func VerifyJWTToken(tokenString string) (*CustomClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return jwtSecret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
fmt.Println("JWT token verified. UserID:", claims.UserID, "Username:", claims.Username)
return claims, nil
}
return nil, fmt.Errorf("Invalid JWT token")
}
dbstore.go
package util
import (
"go_vue_manage/api/entity"
"go_vue_manage/pkg/db"
)
type DBStore struct{
}
func ( dbstore DBStore) InsertUser(userDTO entity.Userdto) bool {
id:=db.Db.Create(&userDTO)
if id !=nil{
return true
}
return false
}
func (dbstore DBStore) GetUserByName(username string) []entity.Userdto{
var dbUser []entity.Userdto
db.Db.Where("user_name =?",username).Find(&dbUser)
return dbUser
}