├── common ├── lib │ ├── conf.go │ ├── file.go │ ├── func.go │ ├── log.go │ ├── mysql.go │ └── redis_lib │ │ ├── helper.go │ │ └── redis.go └── log │ ├── config.go │ ├── console_writer.go │ ├── file_writer.go │ ├── log.go │ └── log_test.go ├── conf └── dev │ ├── base.toml │ ├── mysql_map.toml │ └── redis_map.toml ├── constant └── constants.go ├── controller ├── api.go ├── base │ └── base.go ├── demo.go └── login.go ├── gin.go ├── go.mod ├── go.sum ├── logs ├── gin_scaffold.inf.log ├── gin_scaffold.inf.log.2020051810 ├── gin_scaffold.inf.log.2020051811 ├── gin_scaffold.inf.log.2020051812 ├── gin_scaffold.inf.log.2020051813 ├── gin_scaffold.inf.log.2020051814 ├── gin_scaffold.inf.log.2020051816 ├── gin_scaffold.inf.log.2020051817 ├── gin_scaffold.inf.log.2020051908 ├── gin_scaffold.inf.log.2020051909 ├── gin_scaffold.inf.log.2020051910 ├── gin_scaffold.inf.log.2020051911 ├── gin_scaffold.inf.log.2020051912 ├── gin_scaffold.inf.log.2020051913 ├── gin_scaffold.inf.log.2020051914 ├── gin_scaffold.inf.log.2020051915 ├── gin_scaffold.inf.log.2020051916 ├── gin_scaffold.inf.log.2020051917 ├── gin_scaffold.inf.log.2020051918 ├── gin_scaffold.inf.log.2020051919 ├── gin_scaffold.inf.log.2020052006 └── gin_scaffold.wf.log ├── main.go ├── middleware ├── crpy.go ├── ip_auth.go ├── jwt_auth.go ├── limiter.go ├── logger.go ├── recovery.go ├── refresh_token.go ├── request_log.go ├── requestlog.go ├── response.go ├── session_auth.go └── translation.go ├── model ├── demo.go ├── form │ └── User.go └── param │ ├── base_param.go │ └── login_param.go ├── public ├── const.go ├── log.go └── params.go ├── readme.md ├── router ├── httpserver.go └── route.go └── util ├── bind └── bind.go ├── convert └── convert.go ├── cryp ├── aes.go ├── ase_test.go ├── base64.go ├── hmac.go ├── hmac_test.go ├── md5.go ├── myrsa.go ├── ras_test.go ├── rsa.go ├── rsa_ext.go ├── rsa_private_key.pem ├── rsa_public_key.pem ├── sha256.go └── uuid.go ├── json └── json.go ├── jwt └── jwt.go ├── mail └── mail.go ├── map_builder └── map_builder.go ├── math └── math.go ├── response └── response.go ├── rsaaaa ├── gorsa.go ├── gorsaSign.go ├── rsa.go └── rsa_ext.go ├── time └── time.go └── validator └── validator.go /common/lib/conf.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "bytes" 5 | "database/sql" 6 | "fehu/common/log" 7 | "github.com/e421083458/gorm" 8 | "github.com/spf13/viper" 9 | "io/ioutil" 10 | "os" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | type BaseConf struct { 16 | DebugMode string `mapstructure:"debug_mode"` 17 | TimeLocation string `mapstructure:"time_location"` 18 | Log LogConfig `mapstructure:"log"` 19 | Base struct { 20 | DebugMode string `mapstructure:"debug_mode"` 21 | TimeLocation string `mapstructure:"time_location"` 22 | } `mapstructure:"base"` 23 | } 24 | 25 | type LogConfFileWriter struct { 26 | On bool `mapstructure:"on"` 27 | LogPath string `mapstructure:"log_path"` 28 | RotateLogPath string `mapstructure:"rotate_log_path"` 29 | WfLogPath string `mapstructure:"wf_log_path"` 30 | RotateWfLogPath string `mapstructure:"rotate_wf_path"` 31 | } 32 | 33 | type LogConfConsoleWriter struct { 34 | On bool `mapstructure:"on"` 35 | Color bool `mapstructure:"color"` 36 | } 37 | 38 | type LogConfig struct { 39 | Level string `mapstructure:"log_level"` 40 | FW LogConfFileWriter `mapstructure:"file_writer"` 41 | CW LogConfConsoleWriter `mapstructure:"console_writer"` 42 | } 43 | 44 | type MysqlMapConf struct { 45 | List map[string]*MySQLConf `mapstructure:"list"` 46 | } 47 | 48 | type MySQLConf struct { 49 | DriverName string `mapstructure:"driver_name"` 50 | DataSourceName string `mapstructure:"data_source_name"` 51 | MaxOpenConn int `mapstructure:"max_open_conn"` 52 | MaxIdleConn int `mapstructure:"max_idle_conn"` 53 | MaxConnLifeTime int `mapstructure:"max_conn_life_time"` 54 | } 55 | 56 | type RedisMapConf struct { 57 | List map[string]*RedisConf `mapstructure:"list"` 58 | } 59 | 60 | type RedisConf struct { 61 | ProxyList []string `mapstructure:"proxy_list"` 62 | MaxActive int `mapstructure:"max_active"` // 最大活动连接数,值为0时表示不限制 63 | MaxIdle int `mapstructure:"max_idle"` // 最大空闲连接数 64 | Downgrade bool `mapstructure:"down_grade"` 65 | Network string `mapstructure:"network"` // 通讯协议,默认为 tcp 66 | Password string `mapstructure:"password"` // redis鉴权密码 67 | Db int `mapstructure:"db"` // 数据库 68 | IdleTimeout int `mapstructure:"idleTimeout"` // 空闲连接的超时时间,超过该时间则关闭连接。单位为秒。默认值是5分钟。值为0时表示不关闭空闲连接。此值应该总是大于redis服务的超时时间。 69 | Prefix string `mapstructure:"prefix"` // 键名前缀 70 | } 71 | 72 | //全局变量 73 | var ConfBase *BaseConf 74 | var DBMapPool map[string]*sql.DB 75 | var GORMMapPool map[string]*gorm.DB 76 | var DBDefaultPool *sql.DB 77 | var GORMDefaultPool *gorm.DB 78 | var ConfRedis *RedisConf 79 | var ConfRedisMap *RedisMapConf 80 | var ViperConfMap map[string]*viper.Viper 81 | 82 | //获取基本配置信息 83 | func GetBaseConf() *BaseConf { 84 | return ConfBase 85 | } 86 | 87 | func InitBaseConf(path string) error { 88 | ConfBase = &BaseConf{} 89 | err := ParseConfig(path, ConfBase) 90 | if err != nil { 91 | return err 92 | } 93 | 94 | if ConfBase.DebugMode == "" { 95 | if ConfBase.Base.DebugMode != "" { 96 | ConfBase.DebugMode = ConfBase.Base.DebugMode 97 | } else { 98 | ConfBase.DebugMode = "debug" 99 | } 100 | } 101 | if ConfBase.TimeLocation == "" { 102 | if ConfBase.Base.TimeLocation != "" { 103 | ConfBase.TimeLocation = ConfBase.Base.TimeLocation 104 | } else { 105 | ConfBase.TimeLocation = "Asia/Chongqing" 106 | } 107 | } 108 | if ConfBase.Log.Level == "" { 109 | ConfBase.Log.Level = "trace" 110 | } 111 | 112 | //配置日志 113 | logConf := log.LogConfig{ 114 | Level: ConfBase.Log.Level, 115 | FW: log.ConfFileWriter{ 116 | On: ConfBase.Log.FW.On, 117 | LogPath: ConfBase.Log.FW.LogPath, 118 | RotateLogPath: ConfBase.Log.FW.RotateLogPath, 119 | WfLogPath: ConfBase.Log.FW.WfLogPath, 120 | RotateWfLogPath: ConfBase.Log.FW.RotateWfLogPath, 121 | }, 122 | CW: log.ConfConsoleWriter{ 123 | On: ConfBase.Log.CW.On, 124 | Color: ConfBase.Log.CW.Color, 125 | }, 126 | } 127 | if err := log.SetupDefaultLogWithConf(logConf); err != nil { 128 | panic(err) 129 | } 130 | log.SetLayout("2006-01-02T15:04:05.000") 131 | return nil 132 | } 133 | 134 | // 135 | //func InitLogger(path string) error { 136 | // if err := dlog.SetupDefaultLogWithFile(path); err != nil { 137 | // panic(err) 138 | // } 139 | // dlog.SetLayout("2006-01-02T15:04:05.000") 140 | // return nil 141 | //} 142 | 143 | func InitRedisConf(path string) error { 144 | ConfRedis := &RedisMapConf{} 145 | err := ParseConfig(path, ConfRedis) 146 | if err != nil { 147 | return err 148 | } 149 | ConfRedisMap = ConfRedis 150 | return nil 151 | } 152 | 153 | //初始化配置文件 154 | func InitViperConf() error { 155 | f, err := os.Open(ConfEnvPath + "/") 156 | if err != nil { 157 | return err 158 | } 159 | fileList, err := f.Readdir(1024) 160 | if err != nil { 161 | return err 162 | } 163 | for _, f0 := range fileList { 164 | if !f0.IsDir() { 165 | bts, err := ioutil.ReadFile(ConfEnvPath + "/" + f0.Name()) 166 | if err != nil { 167 | return err 168 | } 169 | v := viper.New() 170 | v.SetConfigType("toml") 171 | v.ReadConfig(bytes.NewBuffer(bts)) 172 | pathArr := strings.Split(f0.Name(), ".") 173 | if ViperConfMap == nil { 174 | ViperConfMap = make(map[string]*viper.Viper) 175 | } 176 | ViperConfMap[pathArr[0]] = v 177 | } 178 | } 179 | return nil 180 | } 181 | 182 | //获取get配置信息 183 | func GetStringConf(key string) string { 184 | keys := strings.Split(key, ".") 185 | if len(keys) < 2 { 186 | return "" 187 | } 188 | v, ok := ViperConfMap[keys[0]] 189 | if !ok { 190 | return "" 191 | } 192 | confString := v.GetString(strings.Join(keys[1:len(keys)], ".")) 193 | return confString 194 | } 195 | 196 | //获取get配置信息 197 | func GetStringMapConf(key string) map[string]interface{} { 198 | keys := strings.Split(key, ".") 199 | if len(keys) < 2 { 200 | return nil 201 | } 202 | v := ViperConfMap[keys[0]] 203 | conf := v.GetStringMap(strings.Join(keys[1:len(keys)], ".")) 204 | return conf 205 | } 206 | 207 | //获取get配置信息 208 | func GetConf(key string) interface{} { 209 | keys := strings.Split(key, ".") 210 | if len(keys) < 2 { 211 | return nil 212 | } 213 | v := ViperConfMap[keys[0]] 214 | conf := v.Get(strings.Join(keys[1:len(keys)], ".")) 215 | return conf 216 | } 217 | 218 | //获取get配置信息 219 | func GetBoolConf(key string) bool { 220 | keys := strings.Split(key, ".") 221 | if len(keys) < 2 { 222 | return false 223 | } 224 | v := ViperConfMap[keys[0]] 225 | conf := v.GetBool(strings.Join(keys[1:len(keys)], ".")) 226 | return conf 227 | } 228 | 229 | //获取get配置信息 230 | func GetFloat64Conf(key string) float64 { 231 | keys := strings.Split(key, ".") 232 | if len(keys) < 2 { 233 | return 0 234 | } 235 | v := ViperConfMap[keys[0]] 236 | conf := v.GetFloat64(strings.Join(keys[1:len(keys)], ".")) 237 | return conf 238 | } 239 | 240 | //获取get配置信息 241 | func GetIntConf(key string) int { 242 | keys := strings.Split(key, ".") 243 | if len(keys) < 2 { 244 | return 0 245 | } 246 | v := ViperConfMap[keys[0]] 247 | conf := v.GetInt(strings.Join(keys[1:len(keys)], ".")) 248 | return conf 249 | } 250 | 251 | //获取get配置信息 252 | func GetStringMapStringConf(key string) map[string]string { 253 | keys := strings.Split(key, ".") 254 | if len(keys) < 2 { 255 | return nil 256 | } 257 | v := ViperConfMap[keys[0]] 258 | conf := v.GetStringMapString(strings.Join(keys[1:len(keys)], ".")) 259 | return conf 260 | } 261 | 262 | //获取get配置信息 263 | func GetStringSliceConf(key string) []string { 264 | keys := strings.Split(key, ".") 265 | if len(keys) < 2 { 266 | return nil 267 | } 268 | v := ViperConfMap[keys[0]] 269 | conf := v.GetStringSlice(strings.Join(keys[1:len(keys)], ".")) 270 | return conf 271 | } 272 | 273 | //获取get配置信息 274 | func GetTimeConf(key string) time.Time { 275 | keys := strings.Split(key, ".") 276 | if len(keys) < 2 { 277 | return time.Now() 278 | } 279 | v := ViperConfMap[keys[0]] 280 | conf := v.GetTime(strings.Join(keys[1:len(keys)], ".")) 281 | return conf 282 | } 283 | 284 | //获取时间阶段长度 285 | func GetDurationConf(key string) time.Duration { 286 | keys := strings.Split(key, ".") 287 | if len(keys) < 2 { 288 | return 0 289 | } 290 | v := ViperConfMap[keys[0]] 291 | conf := v.GetDuration(strings.Join(keys[1:len(keys)], ".")) 292 | return conf 293 | } 294 | 295 | //是否设置了key 296 | func IsSetConf(key string) bool { 297 | keys := strings.Split(key, ".") 298 | if len(keys) < 2 { 299 | return false 300 | } 301 | v := ViperConfMap[keys[0]] 302 | conf := v.IsSet(strings.Join(keys[1:len(keys)], ".")) 303 | return conf 304 | } 305 | -------------------------------------------------------------------------------- /common/lib/file.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/spf13/viper" 7 | "io/ioutil" 8 | "os" 9 | "strings" 10 | ) 11 | 12 | var ConfEnvPath string //配置文件夹 13 | var ConfEnv string //配置环境名 比如:dev prod test 14 | 15 | // 解析配置文件目录 16 | // 17 | // 配置文件必须放到一个文件夹中 18 | // 如:config=conf/dev/base.json ConfEnvPath=conf/dev ConfEnv=dev 19 | // 如:config=conf/base.json ConfEnvPath=conf ConfEnv=conf 20 | func ParseConfPath(config string) error { 21 | path := strings.Split(config, "/") 22 | prefix := strings.Join(path[:len(path)-1], "/") 23 | ConfEnvPath = prefix 24 | ConfEnv = path[len(path)-2] 25 | return nil 26 | } 27 | 28 | //获取配置环境名 29 | func GetConfEnv() string { 30 | return ConfEnv 31 | } 32 | 33 | func GetConfPath(fileName string) string { 34 | return ConfEnvPath + "/" + fileName + ".toml" 35 | } 36 | 37 | func GetConfFilePath(fileName string) string { 38 | return ConfEnvPath + "/" + fileName 39 | } 40 | 41 | //本地解析文件 42 | func ParseLocalConfig(fileName string, st interface{}) error { 43 | path := GetConfFilePath(fileName) 44 | err := ParseConfig(path, st) 45 | if err != nil { 46 | return err 47 | } 48 | return nil 49 | } 50 | 51 | func ParseConfig(path string, conf interface{}) error { 52 | file, err := os.Open(path) 53 | if err != nil { 54 | return fmt.Errorf("Open config %v fail, %v", path, err) 55 | } 56 | data, err := ioutil.ReadAll(file) 57 | if err != nil { 58 | return fmt.Errorf("Read config fail, %v", err) 59 | } 60 | 61 | v := viper.New() 62 | v.SetConfigType("toml") 63 | v.ReadConfig(bytes.NewBuffer(data)) 64 | if err := v.Unmarshal(conf); err != nil { 65 | return fmt.Errorf("Parse config fail, config:%v, err:%v", string(data), err) 66 | } 67 | return nil 68 | } 69 | -------------------------------------------------------------------------------- /common/lib/func.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "bytes" 5 | "crypto/md5" 6 | "encoding/binary" 7 | "encoding/hex" 8 | dlog "fehu/common/log" 9 | "flag" 10 | "fmt" 11 | "io/ioutil" 12 | "log" 13 | "math/rand" 14 | "net" 15 | "net/http" 16 | "net/url" 17 | "os" 18 | "strings" 19 | "time" 20 | ) 21 | 22 | var TimeLocation *time.Location 23 | var TimeFormat = "2006-01-02 15:04:05" 24 | var DateFormat = "2006-01-02" 25 | var LocalIP = net.ParseIP("127.0.0.1") 26 | 27 | //公共初始化函数:支持两种方式设置配置文件 28 | // 29 | //函数传入配置文件 Init("./conf/dev/") 30 | //如果配置文件为空,会从命令行中读取 -config conf/dev/ 31 | func Init(configPath string) error { 32 | return InitModule(configPath, []string{"base", "mysql", "redis"}) 33 | } 34 | 35 | //模块初始化 36 | func InitModule(configPath string, modules []string) error { 37 | var conf *string 38 | if len(configPath) > 0 { 39 | conf = &configPath 40 | } else { 41 | conf = flag.String("config", "", "input config file like ./conf/dev/") 42 | flag.Parse() 43 | } 44 | if *conf == "" { 45 | flag.Usage() 46 | os.Exit(1) 47 | } 48 | 49 | log.Println("------------------------------------------------------------------------") 50 | log.Printf("[INFO] config=%s\n", *conf) 51 | log.Printf("[INFO] %s\n", " start loading resources.") 52 | 53 | // 设置ip信息,优先设置便于日志打印 54 | ips := GetLocalIPs() 55 | if len(ips) > 0 { 56 | LocalIP = ips[0] 57 | } 58 | 59 | // 解析配置文件目录 60 | if err := ParseConfPath(*conf); err != nil { 61 | return err 62 | } 63 | 64 | //初始化配置文件 65 | if err := InitViperConf(); err != nil { 66 | return err 67 | } 68 | 69 | // 加载base配置 70 | if InArrayString("base", modules) { 71 | if err := InitBaseConf(GetConfPath("base")); err != nil { 72 | fmt.Printf("[ERROR] %s%s\n", time.Now().Format(TimeFormat), " InitBaseConf:"+err.Error()) 73 | } 74 | } 75 | 76 | // 加载redis配置 77 | if InArrayString("redis", modules) { 78 | if err := InitRedisConf(GetConfPath("redis_map")); err != nil { 79 | fmt.Printf("[ERROR] %s%s\n", time.Now().Format(TimeFormat), " InitRedisConf:"+err.Error()) 80 | } 81 | } 82 | 83 | // 加载mysql配置并初始化实例 84 | if InArrayString("mysql", modules) { 85 | if err := InitDBPool(GetConfPath("mysql_map")); err != nil { 86 | fmt.Printf("[ERROR] %s%s\n", time.Now().Format(TimeFormat), " InitDBPool:"+err.Error()) 87 | } 88 | } 89 | 90 | // 设置时区 91 | if location, err := time.LoadLocation(ConfBase.TimeLocation); err != nil { 92 | return err 93 | } else { 94 | TimeLocation = location 95 | } 96 | 97 | log.Printf("[INFO] %s\n", " success loading resources.") 98 | log.Println("------------------------------------------------------------------------") 99 | return nil 100 | } 101 | 102 | //公共销毁函数 103 | func Destroy() { 104 | log.Println("------------------------------------------------------------------------") 105 | log.Printf("[INFO] %s\n", " start destroy resources.") 106 | CloseDB() 107 | dlog.Close() 108 | log.Printf("[INFO] %s\n", " success destroy resources.") 109 | } 110 | 111 | func HttpGET(trace *TraceContext, urlString string, urlParams url.Values, msTimeout int, header http.Header) (*http.Response, []byte, error) { 112 | startTime := time.Now().UnixNano() 113 | client := http.Client{ 114 | Timeout: time.Duration(msTimeout) * time.Millisecond, 115 | } 116 | urlString = AddGetDataToUrl(urlString, urlParams) 117 | req, err := http.NewRequest("GET", urlString, nil) 118 | if err != nil { 119 | Log.TagWarn(trace, DLTagHTTPFailed, map[string]interface{}{ 120 | "url": urlString, 121 | "proc_time": float32(time.Now().UnixNano()-startTime) / 1.0e9, 122 | "method": "GET", 123 | "args": urlParams, 124 | "err": err.Error(), 125 | }) 126 | return nil, nil, err 127 | } 128 | if len(header) > 0 { 129 | req.Header = header 130 | } 131 | req = addTrace2Header(req, trace) 132 | resp, err := client.Do(req) 133 | if err != nil { 134 | Log.TagWarn(trace, DLTagHTTPFailed, map[string]interface{}{ 135 | "url": urlString, 136 | "proc_time": float32(time.Now().UnixNano()-startTime) / 1.0e9, 137 | "method": "GET", 138 | "args": urlParams, 139 | "err": err.Error(), 140 | }) 141 | return nil, nil, err 142 | } 143 | defer resp.Body.Close() 144 | body, err := ioutil.ReadAll(resp.Body) 145 | if err != nil { 146 | Log.TagWarn(trace, DLTagHTTPFailed, map[string]interface{}{ 147 | "url": urlString, 148 | "proc_time": float32(time.Now().UnixNano()-startTime) / 1.0e9, 149 | "method": "GET", 150 | "args": urlParams, 151 | "result": string(body), 152 | "err": err.Error(), 153 | }) 154 | return nil, nil, err 155 | } 156 | Log.TagInfo(trace, DLTagHTTPSuccess, map[string]interface{}{ 157 | "url": urlString, 158 | "proc_time": float32(time.Now().UnixNano()-startTime) / 1.0e9, 159 | "method": "GET", 160 | "args": urlParams, 161 | "result": string(body), 162 | }) 163 | return resp, body, nil 164 | } 165 | 166 | func HttpPOST(trace *TraceContext, urlString string, urlParams url.Values, msTimeout int, header http.Header, contextType string) (*http.Response, []byte, error) { 167 | startTime := time.Now().UnixNano() 168 | client := http.Client{ 169 | Timeout: time.Duration(msTimeout) * time.Millisecond, 170 | } 171 | if contextType == "" { 172 | contextType = "application/x-www-form-urlencoded" 173 | } 174 | req, err := http.NewRequest("POST", urlString, strings.NewReader(urlParams.Encode())) 175 | if len(header) > 0 { 176 | req.Header = header 177 | } 178 | req = addTrace2Header(req, trace) 179 | req.Header.Set("Content-Type", contextType) 180 | resp, err := client.Do(req) 181 | if err != nil { 182 | Log.TagWarn(trace, DLTagHTTPFailed, map[string]interface{}{ 183 | "url": urlString, 184 | "proc_time": float32(time.Now().UnixNano()-startTime) / 1.0e9, 185 | "method": "POST", 186 | "args": urlParams, 187 | "err": err.Error(), 188 | }) 189 | return nil, nil, err 190 | } 191 | defer resp.Body.Close() 192 | body, err := ioutil.ReadAll(resp.Body) 193 | if err != nil { 194 | Log.TagWarn(trace, DLTagHTTPFailed, map[string]interface{}{ 195 | "url": urlString, 196 | "proc_time": float32(time.Now().UnixNano()-startTime) / 1.0e9, 197 | "method": "POST", 198 | "args": urlParams, 199 | "result": string(body), 200 | "err": err.Error(), 201 | }) 202 | return nil, nil, err 203 | } 204 | Log.TagInfo(trace, DLTagHTTPSuccess, map[string]interface{}{ 205 | "url": urlString, 206 | "proc_time": float32(time.Now().UnixNano()-startTime) / 1.0e9, 207 | "method": "POST", 208 | "args": urlParams, 209 | "result": string(body), 210 | }) 211 | return resp, body, nil 212 | } 213 | 214 | func HttpJSON(trace *TraceContext, urlString string, jsonContent string, msTimeout int, header http.Header) (*http.Response, []byte, error) { 215 | startTime := time.Now().UnixNano() 216 | client := http.Client{ 217 | Timeout: time.Duration(msTimeout) * time.Millisecond, 218 | } 219 | req, err := http.NewRequest("POST", urlString, strings.NewReader(jsonContent)) 220 | if len(header) > 0 { 221 | req.Header = header 222 | } 223 | req = addTrace2Header(req, trace) 224 | req.Header.Set("Content-Type", "application/json") 225 | resp, err := client.Do(req) 226 | if err != nil { 227 | Log.TagWarn(trace, DLTagHTTPFailed, map[string]interface{}{ 228 | "url": urlString, 229 | "proc_time": float32(time.Now().UnixNano()-startTime) / 1.0e9, 230 | "method": "POST", 231 | "args": jsonContent, 232 | "err": err.Error(), 233 | }) 234 | return nil, nil, err 235 | } 236 | defer resp.Body.Close() 237 | body, err := ioutil.ReadAll(resp.Body) 238 | if err != nil { 239 | Log.TagWarn(trace, DLTagHTTPFailed, map[string]interface{}{ 240 | "url": urlString, 241 | "proc_time": float32(time.Now().UnixNano()-startTime) / 1.0e9, 242 | "method": "POST", 243 | "args": jsonContent, 244 | "result": string(body), 245 | "err": err.Error(), 246 | }) 247 | return nil, nil, err 248 | } 249 | Log.TagInfo(trace, DLTagHTTPSuccess, map[string]interface{}{ 250 | "url": urlString, 251 | "proc_time": float32(time.Now().UnixNano()-startTime) / 1.0e9, 252 | "method": "POST", 253 | "args": jsonContent, 254 | "result": string(body), 255 | }) 256 | return resp, body, nil 257 | } 258 | 259 | func AddGetDataToUrl(urlString string, data url.Values) string { 260 | if strings.Contains(urlString, "?") { 261 | urlString = urlString + "&" 262 | } else { 263 | urlString = urlString + "?" 264 | } 265 | return fmt.Sprintf("%s%s", urlString, data.Encode()) 266 | } 267 | 268 | func addTrace2Header(request *http.Request, trace *TraceContext) *http.Request { 269 | traceId := trace.TraceId 270 | cSpanId := NewSpanId() 271 | if traceId != "" { 272 | request.Header.Set("header-rid", traceId) 273 | } 274 | if cSpanId != "" { 275 | request.Header.Set("header-spanid", cSpanId) 276 | } 277 | trace.CSpanId = cSpanId 278 | return request 279 | } 280 | 281 | func GetMd5Hash(text string) string { 282 | hasher := md5.New() 283 | hasher.Write([]byte(text)) 284 | return hex.EncodeToString(hasher.Sum(nil)) 285 | } 286 | 287 | func Encode(data string) (string, error) { 288 | h := md5.New() 289 | _, err := h.Write([]byte(data)) 290 | if err != nil { 291 | return "", err 292 | } 293 | return hex.EncodeToString(h.Sum(nil)), nil 294 | } 295 | 296 | func ParseServerAddr(serverAddr string) (host, port string) { 297 | serverInfo := strings.Split(serverAddr, ":") 298 | if len(serverInfo) == 2 { 299 | host = serverInfo[0] 300 | port = serverInfo[1] 301 | } else { 302 | host = serverAddr 303 | port = "" 304 | } 305 | return host, port 306 | } 307 | 308 | func NewTrace() *TraceContext { 309 | trace := &TraceContext{} 310 | trace.TraceId = GetTraceId() 311 | trace.SpanId = NewSpanId() 312 | return trace 313 | } 314 | 315 | func NewSpanId() string { 316 | timestamp := uint32(time.Now().Unix()) 317 | ipToLong := binary.BigEndian.Uint32(LocalIP.To4()) 318 | b := bytes.Buffer{} 319 | b.WriteString(fmt.Sprintf("%08x", ipToLong^timestamp)) 320 | b.WriteString(fmt.Sprintf("%08x", rand.Int31())) 321 | return b.String() 322 | } 323 | 324 | func GetTraceId() (traceId string) { 325 | return calcTraceId(LocalIP.String()) 326 | } 327 | 328 | func calcTraceId(ip string) (traceId string) { 329 | now := time.Now() 330 | timestamp := uint32(now.Unix()) 331 | timeNano := now.UnixNano() 332 | pid := os.Getpid() 333 | 334 | b := bytes.Buffer{} 335 | netIP := net.ParseIP(ip) 336 | if netIP == nil { 337 | b.WriteString("00000000") 338 | } else { 339 | b.WriteString(hex.EncodeToString(netIP.To4())) 340 | } 341 | b.WriteString(fmt.Sprintf("%08x", timestamp&0xffffffff)) 342 | b.WriteString(fmt.Sprintf("%04x", timeNano&0xffff)) 343 | b.WriteString(fmt.Sprintf("%04x", pid&0xffff)) 344 | b.WriteString(fmt.Sprintf("%06x", rand.Int31n(1<<24))) 345 | b.WriteString("b0") // 末两位标记来源,b0为go 346 | 347 | return b.String() 348 | } 349 | 350 | func GetLocalIPs() (ips []net.IP) { 351 | interfaceAddr, err := net.InterfaceAddrs() 352 | if err != nil { 353 | return nil 354 | } 355 | for _, address := range interfaceAddr { 356 | ipNet, isValidIpNet := address.(*net.IPNet) 357 | if isValidIpNet && !ipNet.IP.IsLoopback() { 358 | if ipNet.IP.To4() != nil { 359 | ips = append(ips, ipNet.IP) 360 | } 361 | } 362 | } 363 | return ips 364 | } 365 | 366 | func InArrayString(s string, arr []string) bool { 367 | for _, i := range arr { 368 | if i == s { 369 | return true 370 | } 371 | } 372 | return false 373 | } 374 | -------------------------------------------------------------------------------- /common/lib/log.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "fehu/common/log" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | // 通用DLTag常量定义 10 | const ( 11 | DLTagUndefind = "_undef" 12 | DLTagMySqlFailed = "_com_mysql_failure" 13 | DLTagRedisFailed = "_com_redis_failure" 14 | DLTagMySqlSuccess = "_com_mysql_success" 15 | DLTagRedisSuccess = "_com_redis_success" 16 | DLTagThriftFailed = "_com_thrift_failure" 17 | DLTagThriftSuccess = "_com_thrift_success" 18 | DLTagHTTPSuccess = "_com_http_success" 19 | DLTagHTTPFailed = "_com_http_failure" 20 | DLTagTCPFailed = "_com_tcp_failure" 21 | DLTagRequestIn = "_com_request_in" 22 | DLTagRequestOut = "_com_request_out" 23 | ) 24 | 25 | const ( 26 | _dlTag = "dltag" 27 | _traceId = "traceid" 28 | _spanId = "spanid" 29 | _childSpanId = "cspanid" 30 | _dlTagBizPrefix = "_com_" 31 | _dlTagBizUndef = "_com_undef" 32 | ) 33 | 34 | var Log *Logger 35 | 36 | type Trace struct { 37 | TraceId string 38 | SpanId string 39 | Caller string 40 | SrcMethod string 41 | HintCode int64 42 | HintContent string 43 | } 44 | 45 | type TraceContext struct { 46 | Trace 47 | CSpanId string 48 | } 49 | 50 | type Logger struct { 51 | } 52 | 53 | func (l *Logger) TagInfo(trace *TraceContext, dltag string, m map[string]interface{}) { 54 | m[_dlTag] = checkDLTag(dltag) 55 | m[_traceId] = trace.TraceId 56 | m[_childSpanId] = trace.CSpanId 57 | m[_spanId] = trace.SpanId 58 | log.Info(parseParams(m)) 59 | } 60 | 61 | func (l *Logger) TagWarn(trace *TraceContext, dltag string, m map[string]interface{}) { 62 | m[_dlTag] = checkDLTag(dltag) 63 | m[_traceId] = trace.TraceId 64 | m[_childSpanId] = trace.CSpanId 65 | m[_spanId] = trace.SpanId 66 | log.Warn(parseParams(m)) 67 | } 68 | 69 | func (l *Logger) TagError(trace *TraceContext, dltag string, m map[string]interface{}) { 70 | m[_dlTag] = checkDLTag(dltag) 71 | m[_traceId] = trace.TraceId 72 | m[_childSpanId] = trace.CSpanId 73 | m[_spanId] = trace.SpanId 74 | log.Error(parseParams(m)) 75 | } 76 | 77 | func (l *Logger) TagTrace(trace *TraceContext, dltag string, m map[string]interface{}) { 78 | m[_dlTag] = checkDLTag(dltag) 79 | m[_traceId] = trace.TraceId 80 | m[_childSpanId] = trace.CSpanId 81 | m[_spanId] = trace.SpanId 82 | log.Trace(parseParams(m)) 83 | } 84 | 85 | func (l *Logger) TagDebug(trace *TraceContext, dltag string, m map[string]interface{}) { 86 | m[_dlTag] = checkDLTag(dltag) 87 | m[_traceId] = trace.TraceId 88 | m[_childSpanId] = trace.CSpanId 89 | m[_spanId] = trace.SpanId 90 | log.Debug(parseParams(m)) 91 | } 92 | 93 | func (l *Logger) Close() { 94 | log.Close() 95 | } 96 | 97 | // 生成业务dltag 98 | func CreateBizDLTag(tagName string) string { 99 | if tagName == "" { 100 | return _dlTagBizUndef 101 | } 102 | 103 | return _dlTagBizPrefix + tagName 104 | } 105 | 106 | // 校验dltag合法性 107 | func checkDLTag(dltag string) string { 108 | if strings.HasPrefix(dltag, _dlTagBizPrefix) { 109 | return dltag 110 | } 111 | 112 | if strings.HasPrefix(dltag, "_com_") { 113 | return dltag 114 | } 115 | 116 | if dltag == DLTagUndefind { 117 | return dltag 118 | } 119 | return dltag 120 | } 121 | 122 | //map格式化为string 123 | func parseParams(m map[string]interface{}) string { 124 | var dltag string = "_undef" 125 | if _dltag, _have := m["dltag"]; _have { 126 | if __val, __ok := _dltag.(string); __ok { 127 | dltag = __val 128 | } 129 | } 130 | for _key, _val := range m { 131 | if _key == "dltag" { 132 | continue 133 | } 134 | dltag = dltag + "||" + fmt.Sprintf("%v=%+v", _key, _val) 135 | } 136 | dltag = strings.Trim(fmt.Sprintf("%q", dltag), "\"") 137 | return dltag 138 | } 139 | -------------------------------------------------------------------------------- /common/lib/mysql.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "database/sql" 5 | "database/sql/driver" 6 | "errors" 7 | "fmt" 8 | "github.com/e421083458/gorm" 9 | _ "github.com/e421083458/gorm/dialects/mysql" 10 | "reflect" 11 | "regexp" 12 | "strconv" 13 | "time" 14 | "unicode" 15 | ) 16 | 17 | func InitDBPool(path string) error { 18 | //普通的db方式 19 | DbConfMap := &MysqlMapConf{} 20 | err := ParseConfig(path, DbConfMap) 21 | if err != nil { 22 | return err 23 | } 24 | if len(DbConfMap.List) == 0 { 25 | fmt.Printf("[INFO] %s%s\n", time.Now().Format(TimeFormat), " empty mysql config.") 26 | } 27 | 28 | DBMapPool = map[string]*sql.DB{} 29 | GORMMapPool = map[string]*gorm.DB{} 30 | for confName, DbConf := range DbConfMap.List { 31 | dbpool, err := sql.Open("mysql", DbConf.DataSourceName) 32 | if err != nil { 33 | return err 34 | } 35 | dbpool.SetMaxOpenConns(DbConf.MaxOpenConn) 36 | dbpool.SetMaxIdleConns(DbConf.MaxIdleConn) 37 | dbpool.SetConnMaxLifetime(time.Duration(DbConf.MaxConnLifeTime) * time.Second) 38 | err = dbpool.Ping() 39 | if err != nil { 40 | return err 41 | } 42 | 43 | //gorm连接方式 44 | dbgorm, err := gorm.Open("mysql", DbConf.DataSourceName) 45 | if err != nil { 46 | return err 47 | } 48 | dbgorm.SingularTable(true) 49 | err = dbgorm.DB().Ping() 50 | if err != nil { 51 | return err 52 | } 53 | dbgorm.LogMode(true) 54 | dbgorm.LogCtx(true) 55 | dbgorm.SetLogger(&MysqlGormLogger{Trace: NewTrace()}) 56 | dbgorm.DB().SetMaxIdleConns(DbConf.MaxIdleConn) 57 | dbgorm.DB().SetMaxOpenConns(DbConf.MaxOpenConn) 58 | dbgorm.DB().SetConnMaxLifetime(time.Duration(DbConf.MaxConnLifeTime) * time.Second) 59 | DBMapPool[confName] = dbpool 60 | GORMMapPool[confName] = dbgorm 61 | } 62 | 63 | //手动配置连接 64 | if dbpool, err := GetDBPool("default"); err == nil { 65 | DBDefaultPool = dbpool 66 | } 67 | if dbpool, err := GetGormPool("default"); err == nil { 68 | GORMDefaultPool = dbpool 69 | } 70 | return nil 71 | } 72 | 73 | func GetDBPool(name string) (*sql.DB, error) { 74 | if dbpool, ok := DBMapPool[name]; ok { 75 | return dbpool, nil 76 | } 77 | return nil, errors.New("get pool error") 78 | } 79 | 80 | func GetGormPool(name string) (*gorm.DB, error) { 81 | if dbpool, ok := GORMMapPool[name]; ok { 82 | return dbpool, nil 83 | } 84 | return nil, errors.New("get pool error") 85 | } 86 | 87 | func CloseDB() error { 88 | for _, dbpool := range DBMapPool { 89 | dbpool.Close() 90 | } 91 | for _, dbpool := range GORMMapPool { 92 | dbpool.Close() 93 | } 94 | return nil 95 | } 96 | 97 | func DBPoolLogQuery(trace *TraceContext, sqlDb *sql.DB, query string, args ...interface{}) (*sql.Rows, error) { 98 | startExecTime := time.Now() 99 | rows, err := sqlDb.Query(query, args...) 100 | endExecTime := time.Now() 101 | if err != nil { 102 | Log.TagError(trace, "_com_mysql_success", map[string]interface{}{ 103 | "sql": query, 104 | "bind": args, 105 | "proc_time": fmt.Sprintf("%f", endExecTime.Sub(startExecTime).Seconds()), 106 | }) 107 | } else { 108 | Log.TagInfo(trace, "_com_mysql_success", map[string]interface{}{ 109 | "sql": query, 110 | "bind": args, 111 | "proc_time": fmt.Sprintf("%f", endExecTime.Sub(startExecTime).Seconds()), 112 | }) 113 | } 114 | return rows, err 115 | } 116 | 117 | //mysql日志打印类 118 | // Logger default logger 119 | type MysqlGormLogger struct { 120 | gorm.Logger 121 | Trace *TraceContext 122 | } 123 | 124 | // Print format & print log 125 | func (logger *MysqlGormLogger) Print(values ...interface{}) { 126 | message := logger.LogFormatter(values...) 127 | if message["level"] == "sql" { 128 | Log.TagInfo(logger.Trace, "_com_mysql_success", message) 129 | } else { 130 | Log.TagInfo(logger.Trace, "_com_mysql_failure", message) 131 | } 132 | } 133 | 134 | // LogCtx(true) 时会执行改方法 135 | func (logger *MysqlGormLogger) CtxPrint(s *gorm.DB, values ...interface{}) { 136 | ctx, ok := s.GetCtx() 137 | trace := NewTrace() 138 | if ok { 139 | trace = ctx.(*TraceContext) 140 | } 141 | message := logger.LogFormatter(values...) 142 | if message["level"] == "sql" { 143 | Log.TagInfo(trace, "_com_mysql_success", message) 144 | } else { 145 | Log.TagInfo(trace, "_com_mysql_failure", message) 146 | } 147 | } 148 | 149 | func (logger *MysqlGormLogger) LogFormatter(values ...interface{}) (messages map[string]interface{}) { 150 | if len(values) > 1 { 151 | var ( 152 | sql string 153 | formattedValues []string 154 | level = values[0] 155 | currentTime = logger.NowFunc().Format("2006-01-02 15:04:05") 156 | source = fmt.Sprintf("%v", values[1]) 157 | ) 158 | 159 | messages = map[string]interface{}{"level": level, "source": source, "current_time": currentTime} 160 | 161 | if level == "sql" { 162 | // duration 163 | //messages = append(messages, fmt.Sprintf("%.2fms", float64(values[2].(time.Duration).Nanoseconds() / 1e4) / 100.0)) 164 | messages["proc_time"] = fmt.Sprintf("%.9f", float64(values[2].(time.Duration).Nanoseconds()/1e9)) 165 | // sql 166 | 167 | for _, value := range values[4].([]interface{}) { 168 | indirectValue := reflect.Indirect(reflect.ValueOf(value)) 169 | if indirectValue.IsValid() { 170 | value = indirectValue.Interface() 171 | if t, ok := value.(time.Time); ok { 172 | formattedValues = append(formattedValues, fmt.Sprintf("'%v'", t.Format("2006-01-02 15:04:05"))) 173 | } else if b, ok := value.([]byte); ok { 174 | if str := string(b); logger.isPrintable(str) { 175 | formattedValues = append(formattedValues, fmt.Sprintf("'%v'", str)) 176 | } else { 177 | formattedValues = append(formattedValues, "''") 178 | } 179 | } else if r, ok := value.(driver.Valuer); ok { 180 | if value, err := r.Value(); err == nil && value != nil { 181 | formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value)) 182 | } else { 183 | formattedValues = append(formattedValues, "NULL") 184 | } 185 | } else { 186 | formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value)) 187 | } 188 | } else { 189 | formattedValues = append(formattedValues, "NULL") 190 | } 191 | } 192 | 193 | // differentiate between $n placeholders or else treat like ? 194 | if regexp.MustCompile(`\$\d+`).MatchString(values[3].(string)) { 195 | sql = values[3].(string) 196 | for index, value := range formattedValues { 197 | placeholder := fmt.Sprintf(`\$%d([^\d]|$)`, index+1) 198 | sql = regexp.MustCompile(placeholder).ReplaceAllString(sql, value+"$1") 199 | } 200 | } else { 201 | formattedValuesLength := len(formattedValues) 202 | for index, value := range regexp.MustCompile(`\?`).Split(values[3].(string), -1) { 203 | sql += value 204 | if index < formattedValuesLength { 205 | sql += formattedValues[index] 206 | } 207 | } 208 | } 209 | 210 | messages["sql"] = sql 211 | if len(values) > 5 { 212 | messages["affected_row"] = strconv.FormatInt(values[5].(int64), 10) 213 | } 214 | } else { 215 | messages["ext"] = values 216 | } 217 | } 218 | return 219 | } 220 | 221 | func (logger *MysqlGormLogger) NowFunc() time.Time { 222 | return time.Now() 223 | } 224 | 225 | func (logger *MysqlGormLogger) isPrintable(s string) bool { 226 | for _, r := range s { 227 | if !unicode.IsPrint(r) { 228 | return false 229 | } 230 | } 231 | return true 232 | } 233 | -------------------------------------------------------------------------------- /common/lib/redis_lib/helper.go: -------------------------------------------------------------------------------- 1 | package redis_lib 2 | 3 | import ( 4 | "github.com/gomodule/redigo/redis" 5 | ) 6 | 7 | // Int is a helper that converts a command reply to an integer 8 | func Int(reply interface{}, err error) (int, error) { 9 | return redis.Int(reply, err) 10 | } 11 | 12 | // Int64 is a helper that converts a command reply to 64 bit integer 13 | func Int64(reply interface{}, err error) (int64, error) { 14 | return redis.Int64(reply, err) 15 | } 16 | 17 | // String is a helper that converts a command reply to a string 18 | func String(reply interface{}, err error) (string, error) { 19 | return redis.String(reply, err) 20 | } 21 | 22 | // Bool is a helper that converts a command reply to a boolean 23 | func Bool(reply interface{}, err error) (bool, error) { 24 | return redis.Bool(reply, err) 25 | } 26 | -------------------------------------------------------------------------------- /common/lib/redis_lib/redis.go: -------------------------------------------------------------------------------- 1 | package redis_lib 2 | 3 | import ( 4 | "encoding/json" 5 | "fehu/common/lib" 6 | "fmt" 7 | "github.com/garyburd/redigo/redis" 8 | "math/rand" 9 | "os" 10 | "os/signal" 11 | "syscall" 12 | "time" 13 | ) 14 | 15 | // Cacher 先构建一个Cacher实例,然后将配置参数传入该实例的StartAndGC方法来初始化实例和程序进程退出后的清理工作。 16 | type Cacher struct { 17 | pool *redis.Pool 18 | prefix string 19 | marshal func(v interface{}) ([]byte, error) 20 | unmarshal func(data []byte, v interface{}) error 21 | } 22 | 23 | // Options redis配置参数 24 | type Options struct { 25 | lib.RedisConf 26 | Addr string // redis服务的地址,默认为 127.0.0.1:6379 27 | } 28 | 29 | var redisClusterMap map[string][]*Cacher 30 | 31 | type GlobalRedisUitl int 32 | 33 | func InitRedis() { 34 | redisClusterMap = make(map[string][]*Cacher) 35 | for confName, cfg := range lib.ConfRedisMap.List { 36 | cachers := make([]*Cacher, 0) 37 | for i := 0; i < len(cfg.ProxyList); i++ { 38 | addr := cfg.ProxyList[i] 39 | c := &Cacher{ 40 | prefix: cfg.Prefix, 41 | } 42 | c.StartAndGC(&Options{ 43 | RedisConf: *cfg, 44 | Addr: addr, 45 | }) 46 | cachers = append(cachers, c) 47 | } 48 | redisClusterMap[confName] = cachers 49 | } 50 | } 51 | 52 | //func RedisConnFactory(name string) (redis.Conn, error) { 53 | // for confName, cfg := range _struct.ConfRedisMap.List { 54 | // if name == confName { 55 | // randHost := cfg.ProxyList[rand.Intn(len(cfg.ProxyList))] 56 | // return redis.Dial( 57 | // "tcp", 58 | // randHost, 59 | // redis.DialConnectTimeout(50*time.Millisecond), 60 | // redis.DialReadTimeout(100*time.Millisecond), 61 | // redis.DialWriteTimeout(100*time.Millisecond)) 62 | // } 63 | // } 64 | // return nil, errors.New("create redis conn fail") 65 | //} 66 | 67 | // StartAndGC 使用 Options 初始化redis,并在程序进程退出时关闭连接池。 68 | func (c *Cacher) StartAndGC(opts *Options) error { 69 | 70 | if opts.Network == "" { 71 | opts.Network = "tcp" 72 | } 73 | if opts.Addr == "" { 74 | opts.Addr = "127.0.0.1:6379" 75 | } 76 | if opts.MaxIdle == 0 { 77 | opts.MaxIdle = 3 78 | } 79 | if opts.IdleTimeout == 0 { 80 | opts.IdleTimeout = 300 81 | } 82 | c.marshal = json.Marshal 83 | c.unmarshal = json.Unmarshal 84 | 85 | pool := &redis.Pool{ 86 | MaxActive: opts.MaxActive, 87 | MaxIdle: opts.MaxIdle, 88 | IdleTimeout: time.Duration(opts.IdleTimeout) * time.Second, 89 | 90 | Dial: func() (redis.Conn, error) { 91 | conn, err := redis.Dial(opts.Network, opts.Addr) 92 | if err != nil { 93 | return nil, err 94 | } 95 | if opts.Password != "" { 96 | if _, err := conn.Do("AUTH", opts.Password); err != nil { 97 | conn.Close() 98 | return nil, err 99 | } 100 | } 101 | if _, err := conn.Do("SELECT", opts.Db); err != nil { 102 | conn.Close() 103 | return nil, err 104 | } 105 | return conn, err 106 | }, 107 | 108 | TestOnBorrow: func(conn redis.Conn, t time.Time) error { 109 | _, err := conn.Do("PING") 110 | return err 111 | }, 112 | } 113 | c.pool = pool 114 | c.closePool() 115 | return nil 116 | 117 | } 118 | 119 | // closePool 程序进程退出时关闭连接池 120 | func (c *Cacher) closePool() { 121 | ch := make(chan os.Signal, 1) 122 | signal.Notify(ch, os.Interrupt) 123 | signal.Notify(ch, syscall.SIGTERM) 124 | signal.Notify(ch, syscall.SIGKILL) 125 | go func() { 126 | <-ch 127 | c.pool.Close() 128 | os.Exit(0) 129 | }() 130 | } 131 | func randPool(clusterName string) redis.Conn { 132 | if clusterName == "" { 133 | clusterName = "default" 134 | } 135 | cachers := redisClusterMap["default"] 136 | // 这里简单做一个轮训 137 | c := cachers[rand.Intn(len(cachers))] 138 | return c.pool.Get() 139 | } 140 | 141 | // Do 执行redis命令并返回结果。执行时从连接池获取连接并在执行完命令后关闭连接。 142 | func Do(clusterName string, commandName string, args ...interface{}) (reply interface{}, err error) { 143 | conn := randPool(clusterName) 144 | defer conn.Close() 145 | return conn.Do(commandName, args...) 146 | } 147 | 148 | // Get 获取键值。一般不直接使用该值,而是配合下面的工具类方法获取具体类型的值,或者直接使用github.com/gomodule/redigo/redis包的工具方法。 149 | func Get(key string, clusterName string) (interface{}, error) { 150 | return Do(clusterName, "GET", key) 151 | } 152 | 153 | // GetString 获取string类型的键值 154 | func GetString(key string, clusterName string) (string, error) { 155 | return String(Get(key, clusterName)) 156 | } 157 | 158 | // GetInt 获取int类型的键值 159 | func GetInt(key string, clusterName string) (int, error) { 160 | return Int(Get(key, clusterName)) 161 | } 162 | 163 | // GetInt64 获取int64类型的键值 164 | func GetInt64(key string, clusterName string) (int64, error) { 165 | return Int64(Get(key, clusterName)) 166 | } 167 | 168 | // GetBool 获取bool类型的键值 169 | func GetBool(key string, clusterName string) (bool, error) { 170 | return Bool(Get(key, clusterName)) 171 | } 172 | 173 | // GetObject 获取非基本类型stuct的键值。在实现上,使用json的Marshal和Unmarshal做序列化存取。 174 | func GetObject(key string, val interface{}, clusterName string) error { 175 | reply, err := Get(key, clusterName) 176 | return decode(reply, err, val) 177 | } 178 | 179 | // Set 存并设置有效时长。时长的单位为秒。 180 | // 基础类型直接保存,其他用json.Marshal后转成string保存。 181 | func Set(key string, val interface{}, expire int64, clusterName string) error { 182 | value, err := encode(val) 183 | if err != nil { 184 | return err 185 | } 186 | if expire > 0 { 187 | _, err := Do(clusterName, "SETEX", key, expire, value) 188 | return err 189 | } 190 | _, err = Do(clusterName, "SET", key, value) 191 | return err 192 | } 193 | 194 | // Exists 检查键是否存在 195 | func Exists(key string, clusterName string) (bool, error) { 196 | return Bool(Do(clusterName, "EXISTS", key)) 197 | } 198 | 199 | //Del 删除键 200 | func Del(key string, clusterName string) error { 201 | _, err := Do(clusterName, "DEL", key) 202 | return err 203 | } 204 | 205 | // Flush 清空当前数据库中的所有 key,慎用! 206 | func Flush(clusterName string) error { 207 | //TODO 这里应该删除所有 208 | _, err := Do(clusterName, "db", "FLUSHDB") 209 | return err 210 | } 211 | 212 | // TTL 以秒为单位。当 key 不存在时,返回 -2 。 当 key 存在但没有设置剩余生存时间时,返回 -1 213 | func TTL(key string, clusterName string) (ttl int64, err error) { 214 | return Int64(Do(clusterName, "TTL", key)) 215 | } 216 | 217 | // Expire 设置键过期时间,expire的单位为秒 218 | func Expire(key string, expire int64, clusterName string) error { 219 | _, err := Bool(Do(clusterName, "EXPIRE", key, expire)) 220 | return err 221 | } 222 | 223 | // Incr 将 key 中储存的数字值增一 224 | func Incr(key string, clusterName string) (val int64, err error) { 225 | return Int64(Do(clusterName, "INCR", key)) 226 | } 227 | 228 | // IncrBy 将 key 所储存的值加上给定的增量值(increment)。 229 | func IncrBy(key string, amount int64, clusterName string) (val int64, err error) { 230 | return Int64(Do(clusterName, "INCRBY", key, amount)) 231 | } 232 | 233 | // Decr 将 key 中储存的数字值减一。 234 | func Decr(key string, clusterName string) (val int64, err error) { 235 | return Int64(Do(clusterName, "DECR", key)) 236 | } 237 | 238 | // DecrBy key 所储存的值减去给定的减量值(decrement)。 239 | func DecrBy(key string, amount int64, clusterName string) (val int64, err error) { 240 | return Int64(Do(clusterName, "DECRBY", key, amount)) 241 | } 242 | 243 | // HMSet 将一个map存到Redis hash,同时设置有效期,单位:秒 244 | // Example: 245 | // 246 | // ```golang 247 | // m := make(map[string]interface{}) 248 | // m["name"] = "corel" 249 | // m["age"] = 23 250 | // err := HMSet("user", m, 10) 251 | // ``` 252 | func HMSet(key string, val interface{}, expire int, clusterName string) (err error) { 253 | conn := randPool(clusterName) 254 | defer conn.Close() 255 | err = conn.Send("HMSET", redis.Args{}.Add(key).AddFlat(val)...) 256 | if err != nil { 257 | return 258 | } 259 | if expire > 0 { 260 | err = conn.Send("EXPIRE", key, int64(expire)) 261 | } 262 | if err != nil { 263 | return 264 | } 265 | conn.Flush() 266 | _, err = conn.Receive() 267 | return 268 | } 269 | 270 | /** Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。 **/ 271 | 272 | // HSet 将哈希表 key 中的字段 field 的值设为 val 273 | // Example: 274 | // 275 | // ```golang 276 | // _, err := HSet("user", "age", 23) 277 | // ``` 278 | func HSet(key, field string, val interface{}, clusterName string) (interface{}, error) { 279 | value, err := encode(val) 280 | if err != nil { 281 | return nil, err 282 | } 283 | return Do(clusterName, "HSET", key, field, value) 284 | } 285 | 286 | // HGet 获取存储在哈希表中指定字段的值 287 | // Example: 288 | // 289 | // ```golang 290 | // val, err := HGet("user", "age") 291 | // ``` 292 | func HGet(key, field string, clusterName string) (reply interface{}, err error) { 293 | reply, err = Do(clusterName, "HGET", key, field) 294 | return 295 | } 296 | 297 | // HGetString HGet的工具方法,当字段值为字符串类型时使用 298 | func HGetString(key, field string, clusterName string) (reply string, err error) { 299 | reply, err = String(HGet(clusterName, key, field)) 300 | return 301 | } 302 | 303 | // HGetInt HGet的工具方法,当字段值为int类型时使用 304 | func HGetInt(key, field string, clusterName string) (reply int, err error) { 305 | reply, err = Int(HGet(clusterName, key, field)) 306 | return 307 | } 308 | 309 | // HGetInt64 HGet的工具方法,当字段值为int64类型时使用 310 | func HGetInt64(key, field string, clusterName string) (reply int64, err error) { 311 | reply, err = Int64(HGet(clusterName, key, field)) 312 | return 313 | } 314 | 315 | // HGetBool HGet的工具方法,当字段值为bool类型时使用 316 | func HGetBool(key, field string, clusterName string) (reply bool, err error) { 317 | reply, err = Bool(HGet(clusterName, key, field)) 318 | return 319 | } 320 | 321 | // HGetObject HGet的工具方法,当字段值为非基本类型的stuct时使用 322 | func HGetObject(key, field string, val interface{}, clusterName string) error { 323 | reply, err := HGet(clusterName, key, field) 324 | return decode(reply, err, val) 325 | } 326 | 327 | // HGetAll HGetAll("key", &val) 328 | func HGetAll(key string, val interface{}, clusterName string) error { 329 | v, err := redis.Values(Do("HGETALL", key)) 330 | if err != nil { 331 | return err 332 | } 333 | 334 | if err := redis.ScanStruct(v, val); err != nil { 335 | fmt.Println(err) 336 | } 337 | //fmt.Printf("%+v\n", val) 338 | return err 339 | } 340 | 341 | /** 342 | Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边) 343 | **/ 344 | 345 | // BLPop 它是 LPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BLPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 346 | // 超时参数 timeout 接受一个以秒为单位的数字作为值。超时参数设为 0 表示阻塞时间可以无限期延长(block indefinitely) 。 347 | func BLPop(clusterName string, key string, timeout int) (interface{}, error) { 348 | values, err := redis.Values(Do(clusterName, "BLPOP", key, timeout)) 349 | if err != nil { 350 | return nil, err 351 | } 352 | if len(values) != 2 { 353 | return nil, fmt.Errorf("redisgo: unexpected number of values, got %d", len(values)) 354 | } 355 | return values[1], err 356 | } 357 | 358 | // BLPopInt BLPop的工具方法,元素类型为int时 359 | func BLPopInt(key string, timeout int, clusterName string) (int, error) { 360 | return Int(BLPop(clusterName, key, timeout)) 361 | } 362 | 363 | // BLPopInt64 BLPop的工具方法,元素类型为int64时 364 | func BLPopInt64(key string, timeout int, clusterName string) (int64, error) { 365 | return Int64(BLPop(clusterName, key, timeout)) 366 | } 367 | 368 | // BLPopString BLPop的工具方法,元素类型为string时 369 | func BLPopString(key string, timeout int, clusterName string) (string, error) { 370 | return String(BLPop(clusterName, key, timeout)) 371 | } 372 | 373 | // BLPopBool BLPop的工具方法,元素类型为bool时 374 | func BLPopBool(key string, timeout int, clusterName string) (bool, error) { 375 | return Bool(BLPop(clusterName, key, timeout)) 376 | } 377 | 378 | // BLPopObject BLPop的工具方法,元素类型为object时 379 | func BLPopObject(key string, timeout int, val interface{}, clusterName string) error { 380 | reply, err := BLPop(clusterName, key, timeout) 381 | return decode(reply, err, val) 382 | } 383 | 384 | // BRPop 它是 RPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BRPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 385 | // 超时参数 timeout 接受一个以秒为单位的数字作为值。超时参数设为 0 表示阻塞时间可以无限期延长(block indefinitely) 。 386 | func BRPop(clusterName string, key string, timeout int) (interface{}, error) { 387 | values, err := redis.Values(Do(clusterName, "BRPOP", key, timeout)) 388 | if err != nil { 389 | return nil, err 390 | } 391 | if len(values) != 2 { 392 | return nil, fmt.Errorf("redisgo: unexpected number of values, got %d", len(values)) 393 | } 394 | return values[1], err 395 | } 396 | 397 | // BRPopInt BRPop的工具方法,元素类型为int时 398 | func BRPopInt(key string, timeout int, clusterName string) (int, error) { 399 | return Int(BRPop(clusterName, key, timeout)) 400 | } 401 | 402 | // BRPopInt64 BRPop的工具方法,元素类型为int64时 403 | func BRPopInt64(key string, timeout int, clusterName string) (int64, error) { 404 | return Int64(BRPop(clusterName, key, timeout)) 405 | } 406 | 407 | // BRPopString BRPop的工具方法,元素类型为string时 408 | func BRPopString(key string, timeout int, clusterName string) (string, error) { 409 | return String(BRPop(clusterName, key, timeout)) 410 | } 411 | 412 | // BRPopBool BRPop的工具方法,元素类型为bool时 413 | func BRPopBool(key string, timeout int, clusterName string) (bool, error) { 414 | return Bool(BRPop(clusterName, key, timeout)) 415 | } 416 | 417 | // BRPopObject BRPop的工具方法,元素类型为object时 418 | func BRPopObject(key string, timeout int, val interface{}, clusterName string) error { 419 | reply, err := BRPop(clusterName, key, timeout) 420 | return decode(reply, err, val) 421 | } 422 | 423 | // LPop 移出并获取列表中的第一个元素(表头,左边) 424 | func LPop(key string, clusterName string) (interface{}, error) { 425 | return Do(clusterName, "LPOP", key) 426 | } 427 | 428 | // LPopInt 移出并获取列表中的第一个元素(表头,左边),元素类型为int 429 | func LPopInt(key string, clusterName string) (int, error) { 430 | return Int(LPop(clusterName, key)) 431 | } 432 | 433 | // LPopInt64 移出并获取列表中的第一个元素(表头,左边),元素类型为int64 434 | func LPopInt64(key string, clusterName string) (int64, error) { 435 | return Int64(LPop(clusterName, key)) 436 | } 437 | 438 | // LPopString 移出并获取列表中的第一个元素(表头,左边),元素类型为string 439 | func LPopString(key string, clusterName string) (string, error) { 440 | return String(LPop(clusterName, key)) 441 | } 442 | 443 | // LPopBool 移出并获取列表中的第一个元素(表头,左边),元素类型为bool 444 | func LPopBool(key string, clusterName string) (bool, error) { 445 | return Bool(LPop(clusterName, key)) 446 | } 447 | 448 | // LPopObject 移出并获取列表中的第一个元素(表头,左边),元素类型为非基本类型的struct 449 | func LPopObject(key string, val interface{}, clusterName string) error { 450 | reply, err := LPop(clusterName, key) 451 | return decode(reply, err, val) 452 | } 453 | 454 | // RPop 移出并获取列表中的最后一个元素(表尾,右边) 455 | func RPop(key string, clusterName string) (interface{}, error) { 456 | return Do(clusterName, "RPOP", key) 457 | } 458 | 459 | // RPopInt 移出并获取列表中的最后一个元素(表尾,右边),元素类型为int 460 | func RPopInt(key string, clusterName string) (int, error) { 461 | return Int(RPop(clusterName, key)) 462 | } 463 | 464 | // RPopInt64 移出并获取列表中的最后一个元素(表尾,右边),元素类型为int64 465 | func RPopInt64(key string, clusterName string) (int64, error) { 466 | return Int64(RPop(clusterName, key)) 467 | } 468 | 469 | // RPopString 移出并获取列表中的最后一个元素(表尾,右边),元素类型为string 470 | func RPopString(key string, clusterName string) (string, error) { 471 | return String(RPop(clusterName, key)) 472 | } 473 | 474 | // RPopBool 移出并获取列表中的最后一个元素(表尾,右边),元素类型为bool 475 | func RPopBool(key string, clusterName string) (bool, error) { 476 | return Bool(RPop(clusterName, key)) 477 | } 478 | 479 | // RPopObject 移出并获取列表中的最后一个元素(表尾,右边),元素类型为非基本类型的struct 480 | func RPopObject(key string, val interface{}, clusterName string) error { 481 | reply, err := RPop(clusterName, key) 482 | return decode(reply, err, val) 483 | } 484 | 485 | // LPush 将一个值插入到列表头部 486 | func LPush(key string, member interface{}, clusterName string) error { 487 | value, err := encode(member) 488 | if err != nil { 489 | return err 490 | } 491 | _, err = Do(clusterName, "LPUSH", key, value) 492 | return err 493 | } 494 | 495 | // RPush 将一个值插入到列表尾部 496 | func RPush(key string, member interface{}, clusterName string) error { 497 | value, err := encode(member) 498 | if err != nil { 499 | return err 500 | } 501 | _, err = Do(clusterName, "RPUSH", key, value) 502 | return err 503 | } 504 | 505 | // encode 序列化要保存的值 506 | func encode(val interface{}) (interface{}, error) { 507 | var value interface{} 508 | switch v := val.(type) { 509 | case string, int, uint, int8, int16, int32, int64, float32, float64, bool: 510 | value = v 511 | default: 512 | b, err := json.Marshal(v) 513 | if err != nil { 514 | return nil, err 515 | } 516 | value = string(b) 517 | } 518 | return value, nil 519 | } 520 | 521 | // decode 反序列化保存的struct对象 522 | func decode(reply interface{}, err error, val interface{}) error { 523 | str, err := String(reply, err) 524 | if err != nil { 525 | return err 526 | } 527 | return json.Unmarshal([]byte(str), val) 528 | } 529 | 530 | //func RedisLogDo(trace *TraceContext, c redis.Conn, commandName string, args ...interface{}) (interface{}, error) { 531 | // startExecTime := time.Now() 532 | // reply, err := Do(commandName, args...) 533 | // endExecTime := time.Now() 534 | // if err != nil { 535 | // Log.TagError(trace, "_com_redis_failure", map[string]interface{}{ 536 | // "method": commandName, 537 | // "err": err, 538 | // "bind": args, 539 | // "proc_time": fmt.Sprintf("%fms", endExecTime.Sub(startExecTime).Seconds()), 540 | // }) 541 | // } else { 542 | // replyStr, _ := redis.String(reply, nil) 543 | // Log.TagInfo(trace, "_com_redis_success", map[string]interface{}{ 544 | // "method": commandName, 545 | // "bind": args, 546 | // "reply": replyStr, 547 | // "proc_time": fmt.Sprintf("%fms", endExecTime.Sub(startExecTime).Seconds()), 548 | // }) 549 | // } 550 | // return reply, err 551 | //} 552 | // 553 | ////通过配置 执行redis 554 | //func RedisConfDo(trace *TraceContext, name string, commandName string, args ...interface{}) (interface{}, error) { 555 | // c, err := RedisConnFactory(name) 556 | // if err != nil { 557 | // Log.TagError(trace, "_com_redis_failure", map[string]interface{}{ 558 | // "method": commandName, 559 | // "err": errors.New("RedisConnFactory_error:" + name), 560 | // "bind": args, 561 | // }) 562 | // return nil, err 563 | // } 564 | // defer Close() 565 | // 566 | // startExecTime := time.Now() 567 | // reply, err := Do(commandName, args...) 568 | // endExecTime := time.Now() 569 | // if err != nil { 570 | // Log.TagError(trace, "_com_redis_failure", map[string]interface{}{ 571 | // "method": commandName, 572 | // "err": err, 573 | // "bind": args, 574 | // "proc_time": fmt.Sprintf("%fms", endExecTime.Sub(startExecTime).Seconds()), 575 | // }) 576 | // } else { 577 | // replyStr, _ := redis.String(reply, nil) 578 | // Log.TagInfo(trace, "_com_redis_success", map[string]interface{}{ 579 | // "method": commandName, 580 | // "bind": args, 581 | // "reply": replyStr, 582 | // "proc_time": fmt.Sprintf("%fms", endExecTime.Sub(startExecTime).Seconds()), 583 | // }) 584 | // } 585 | // return reply, err 586 | //} 587 | -------------------------------------------------------------------------------- /common/log/config.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | type ConfFileWriter struct { 8 | On bool `toml:"On"` 9 | LogPath string `toml:"LogPath"` 10 | RotateLogPath string `toml:"RotateLogPath"` 11 | WfLogPath string `toml:"WfLogPath"` 12 | RotateWfLogPath string `toml:"RotateWfLogPath"` 13 | } 14 | 15 | type ConfConsoleWriter struct { 16 | On bool `toml:"On"` 17 | Color bool `toml:"Color"` 18 | } 19 | 20 | type LogConfig struct { 21 | Level string `toml:"LogLevel"` 22 | FW ConfFileWriter `toml:"FileWriter"` 23 | CW ConfConsoleWriter `toml:"ConsoleWriter"` 24 | } 25 | 26 | func SetupLogInstanceWithConf(lc LogConfig, logger *Logger) (err error) { 27 | if lc.FW.On { 28 | if len(lc.FW.LogPath) > 0 { 29 | w := NewFileWriter() 30 | w.SetFileName(lc.FW.LogPath) 31 | w.SetPathPattern(lc.FW.RotateLogPath) 32 | w.SetLogLevelFloor(TRACE) 33 | if len(lc.FW.WfLogPath) > 0 { 34 | w.SetLogLevelCeil(INFO) 35 | } else { 36 | w.SetLogLevelCeil(ERROR) 37 | } 38 | logger.Register(w) 39 | } 40 | 41 | if len(lc.FW.WfLogPath) > 0 { 42 | wfw := NewFileWriter() 43 | wfw.SetFileName(lc.FW.WfLogPath) 44 | wfw.SetPathPattern(lc.FW.RotateWfLogPath) 45 | wfw.SetLogLevelFloor(WARNING) 46 | wfw.SetLogLevelCeil(ERROR) 47 | logger.Register(wfw) 48 | } 49 | } 50 | 51 | if lc.CW.On { 52 | w := NewConsoleWriter() 53 | w.SetColor(lc.CW.Color) 54 | logger.Register(w) 55 | } 56 | switch lc.Level { 57 | case "trace": 58 | logger.SetLevel(TRACE) 59 | 60 | case "debug": 61 | logger.SetLevel(DEBUG) 62 | 63 | case "info": 64 | logger.SetLevel(INFO) 65 | 66 | case "warning": 67 | logger.SetLevel(WARNING) 68 | 69 | case "error": 70 | logger.SetLevel(ERROR) 71 | 72 | case "fatal": 73 | logger.SetLevel(FATAL) 74 | 75 | default: 76 | err = errors.New("Invalid log level") 77 | } 78 | return 79 | } 80 | 81 | func SetupDefaultLogWithConf(lc LogConfig) (err error) { 82 | defaultLoggerInit() 83 | return SetupLogInstanceWithConf(lc, logger_default) 84 | } 85 | -------------------------------------------------------------------------------- /common/log/console_writer.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | type colorRecord Record 9 | 10 | func (r *colorRecord) String() string { 11 | switch r.level { 12 | case TRACE: 13 | return fmt.Sprintf("\033[36m%s\033[0m [\033[34m%s\033[0m] \033[47;30m%s\033[0m %s\n", 14 | r.time, LEVEL_FLAGS[r.level], r.code, r.info) 15 | case DEBUG: 16 | return fmt.Sprintf("\033[36m%s\033[0m [\033[34m%s\033[0m] \033[47;30m%s\033[0m %s\n", 17 | r.time, LEVEL_FLAGS[r.level], r.code, r.info) 18 | 19 | case INFO: 20 | return fmt.Sprintf("\033[36m%s\033[0m [\033[32m%s\033[0m] \033[47;30m%s\033[0m %s\n", 21 | r.time, LEVEL_FLAGS[r.level], r.code, r.info) 22 | 23 | case WARNING: 24 | return fmt.Sprintf("\033[36m%s\033[0m [\033[33m%s\033[0m] \033[47;30m%s\033[0m %s\n", 25 | r.time, LEVEL_FLAGS[r.level], r.code, r.info) 26 | 27 | case ERROR: 28 | return fmt.Sprintf("\033[36m%s\033[0m [\033[31m%s\033[0m] \033[47;30m%s\033[0m %s\n", 29 | r.time, LEVEL_FLAGS[r.level], r.code, r.info) 30 | 31 | case FATAL: 32 | return fmt.Sprintf("\033[36m%s\033[0m [\033[35m%s\033[0m] \033[47;30m%s\033[0m %s\n", 33 | r.time, LEVEL_FLAGS[r.level], r.code, r.info) 34 | } 35 | 36 | return "" 37 | } 38 | 39 | type ConsoleWriter struct { 40 | color bool 41 | } 42 | 43 | func NewConsoleWriter() *ConsoleWriter { 44 | return &ConsoleWriter{} 45 | } 46 | 47 | func (w *ConsoleWriter) Write(r *Record) error { 48 | if w.color { 49 | fmt.Fprint(os.Stdout, ((*colorRecord)(r)).String()) 50 | } else { 51 | fmt.Fprint(os.Stdout, r.String()) 52 | } 53 | return nil 54 | } 55 | 56 | func (w *ConsoleWriter) Init() error { 57 | return nil 58 | } 59 | 60 | func (w *ConsoleWriter) SetColor(c bool) { 61 | w.color = c 62 | } 63 | -------------------------------------------------------------------------------- /common/log/file_writer.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "errors" 7 | "fmt" 8 | "os" 9 | "path" 10 | "time" 11 | ) 12 | 13 | var pathVariableTable map[byte]func(*time.Time) int 14 | 15 | type FileWriter struct { 16 | logLevelFloor int 17 | logLevelCeil int 18 | filename string 19 | pathFmt string 20 | file *os.File 21 | fileBufWriter *bufio.Writer 22 | actions []func(*time.Time) int 23 | variables []interface{} 24 | } 25 | 26 | func NewFileWriter() *FileWriter { 27 | return &FileWriter{} 28 | } 29 | 30 | func (w *FileWriter) Init() error { 31 | return w.CreateLogFile() 32 | } 33 | 34 | func (w *FileWriter) SetFileName(filename string) { 35 | w.filename = filename 36 | } 37 | 38 | func (w *FileWriter) SetLogLevelFloor(floor int) { 39 | w.logLevelFloor = floor 40 | } 41 | 42 | func (w *FileWriter) SetLogLevelCeil(ceil int) { 43 | w.logLevelCeil = ceil 44 | } 45 | 46 | func (w *FileWriter) SetPathPattern(pattern string) error { 47 | n := 0 48 | for _, c := range pattern { 49 | if c == '%' { 50 | n++ 51 | } 52 | } 53 | 54 | if n == 0 { 55 | w.pathFmt = pattern 56 | return nil 57 | } 58 | 59 | w.actions = make([]func(*time.Time) int, 0, n) 60 | w.variables = make([]interface{}, n, n) 61 | tmp := []byte(pattern) 62 | 63 | variable := 0 64 | for _, c := range tmp { 65 | if variable == 1 { 66 | act, ok := pathVariableTable[c] 67 | if !ok { 68 | return errors.New("Invalid rotate pattern (" + pattern + ")") 69 | } 70 | w.actions = append(w.actions, act) 71 | variable = 0 72 | continue 73 | } 74 | if c == '%' { 75 | variable = 1 76 | } 77 | } 78 | 79 | for i, act := range w.actions { 80 | now := time.Now() 81 | w.variables[i] = act(&now) 82 | } 83 | //fmt.Printf("%v\n", w.variables) 84 | 85 | w.pathFmt = convertPatternToFmt(tmp) 86 | 87 | return nil 88 | } 89 | 90 | func (w *FileWriter) Write(r *Record) error { 91 | if r.level < w.logLevelFloor || r.level > w.logLevelCeil { 92 | return nil 93 | } 94 | if w.fileBufWriter == nil { 95 | return errors.New("no opened file") 96 | } 97 | if _, err := w.fileBufWriter.WriteString(r.String()); err != nil { 98 | return err 99 | } 100 | return nil 101 | } 102 | 103 | func (w *FileWriter) CreateLogFile() error { 104 | if err := os.MkdirAll(path.Dir(w.filename), 0755); err != nil { 105 | if !os.IsExist(err) { 106 | return err 107 | } 108 | } 109 | 110 | if file, err := os.OpenFile(w.filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644); err != nil { 111 | return err 112 | } else { 113 | w.file = file 114 | } 115 | 116 | if w.fileBufWriter = bufio.NewWriterSize(w.file, 8192); w.fileBufWriter == nil { 117 | return errors.New("new fileBufWriter failed.") 118 | } 119 | 120 | return nil 121 | } 122 | 123 | func (w *FileWriter) Rotate() error { 124 | 125 | now := time.Now() 126 | v := 0 127 | rotate := false 128 | old_variables := make([]interface{}, len(w.variables)) 129 | copy(old_variables, w.variables) 130 | 131 | for i, act := range w.actions { 132 | v = act(&now) 133 | if v != w.variables[i] { 134 | w.variables[i] = v 135 | rotate = true 136 | } 137 | } 138 | //fmt.Printf("%v\n", w.variables) 139 | 140 | if rotate == false { 141 | return nil 142 | } 143 | 144 | if w.fileBufWriter != nil { 145 | if err := w.fileBufWriter.Flush(); err != nil { 146 | return err 147 | } 148 | } 149 | 150 | if w.file != nil { 151 | // 将文件以pattern形式改名并关闭 152 | filePath := fmt.Sprintf(w.pathFmt, old_variables...) 153 | 154 | if err := os.Rename(w.filename, filePath); err != nil { 155 | return err 156 | } 157 | 158 | if err := w.file.Close(); err != nil { 159 | return err 160 | } 161 | } 162 | 163 | return w.CreateLogFile() 164 | } 165 | 166 | func (w *FileWriter) Flush() error { 167 | if w.fileBufWriter != nil { 168 | return w.fileBufWriter.Flush() 169 | } 170 | return nil 171 | } 172 | 173 | func getYear(now *time.Time) int { 174 | return now.Year() 175 | } 176 | 177 | func getMonth(now *time.Time) int { 178 | return int(now.Month()) 179 | } 180 | 181 | func getDay(now *time.Time) int { 182 | return now.Day() 183 | } 184 | 185 | func getHour(now *time.Time) int { 186 | return now.Hour() 187 | } 188 | 189 | func getMin(now *time.Time) int { 190 | return now.Minute() 191 | } 192 | 193 | func convertPatternToFmt(pattern []byte) string { 194 | pattern = bytes.Replace(pattern, []byte("%Y"), []byte("%d"), -1) 195 | pattern = bytes.Replace(pattern, []byte("%M"), []byte("%02d"), -1) 196 | pattern = bytes.Replace(pattern, []byte("%D"), []byte("%02d"), -1) 197 | pattern = bytes.Replace(pattern, []byte("%H"), []byte("%02d"), -1) 198 | pattern = bytes.Replace(pattern, []byte("%m"), []byte("%02d"), -1) 199 | return string(pattern) 200 | } 201 | 202 | func init() { 203 | pathVariableTable = make(map[byte]func(*time.Time) int, 5) 204 | pathVariableTable['Y'] = getYear 205 | pathVariableTable['M'] = getMonth 206 | pathVariableTable['D'] = getDay 207 | pathVariableTable['H'] = getHour 208 | pathVariableTable['m'] = getMin 209 | } 210 | -------------------------------------------------------------------------------- /common/log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "path" 7 | "runtime" 8 | "strconv" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | var ( 14 | LEVEL_FLAGS = [...]string{"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"} 15 | ) 16 | 17 | const ( 18 | TRACE = iota 19 | DEBUG 20 | INFO 21 | WARNING 22 | ERROR 23 | FATAL 24 | ) 25 | 26 | const tunnel_size_default = 1024 27 | 28 | type Record struct { 29 | time string 30 | code string 31 | info string 32 | level int 33 | } 34 | 35 | func (r *Record) String() string { 36 | return fmt.Sprintf("[%s][%s][%s] %s\n", LEVEL_FLAGS[r.level], r.time, r.code, r.info) 37 | } 38 | 39 | type Writer interface { 40 | Init() error 41 | Write(*Record) error 42 | } 43 | 44 | type Rotater interface { 45 | Rotate() error 46 | SetPathPattern(string) error 47 | } 48 | 49 | type Flusher interface { 50 | Flush() error 51 | } 52 | 53 | type Logger struct { 54 | writers []Writer 55 | tunnel chan *Record 56 | level int 57 | lastTime int64 58 | lastTimeStr string 59 | c chan bool 60 | layout string 61 | recordPool *sync.Pool 62 | } 63 | 64 | func NewLogger() *Logger { 65 | if logger_default != nil && takeup == false { 66 | takeup = true //默认启动标志 67 | return logger_default 68 | } 69 | l := new(Logger) 70 | l.writers = []Writer{} 71 | l.tunnel = make(chan *Record, tunnel_size_default) 72 | l.c = make(chan bool, 2) 73 | l.level = DEBUG 74 | l.layout = "2006/01/02 15:04:05" 75 | l.recordPool = &sync.Pool{New: func() interface{} { 76 | return &Record{} 77 | }} 78 | go boostrapLogWriter(l) 79 | 80 | return l 81 | } 82 | 83 | func (l *Logger) Register(w Writer) { 84 | if err := w.Init(); err != nil { 85 | panic(err) 86 | } 87 | l.writers = append(l.writers, w) 88 | } 89 | 90 | func (l *Logger) SetLevel(lvl int) { 91 | l.level = lvl 92 | } 93 | 94 | func (l *Logger) SetLayout(layout string) { 95 | l.layout = layout 96 | } 97 | 98 | func (l *Logger) Trace(fmt string, args ...interface{}) { 99 | l.deliverRecordToWriter(TRACE, fmt, args...) 100 | } 101 | 102 | func (l *Logger) Debug(fmt string, args ...interface{}) { 103 | l.deliverRecordToWriter(DEBUG, fmt, args...) 104 | } 105 | 106 | func (l *Logger) Warn(fmt string, args ...interface{}) { 107 | l.deliverRecordToWriter(WARNING, fmt, args...) 108 | } 109 | 110 | func (l *Logger) Info(fmt string, args ...interface{}) { 111 | l.deliverRecordToWriter(INFO, fmt, args...) 112 | } 113 | 114 | func (l *Logger) Error(fmt string, args ...interface{}) { 115 | l.deliverRecordToWriter(ERROR, fmt, args...) 116 | } 117 | 118 | func (l *Logger) Fatal(fmt string, args ...interface{}) { 119 | l.deliverRecordToWriter(FATAL, fmt, args...) 120 | } 121 | 122 | func (l *Logger) Close() { 123 | close(l.tunnel) 124 | <-l.c 125 | for _, w := range l.writers { 126 | if f, ok := w.(Flusher); ok { 127 | if err := f.Flush(); err != nil { 128 | log.Println(err) 129 | } 130 | } 131 | } 132 | } 133 | 134 | func (l *Logger) deliverRecordToWriter(level int, format string, args ...interface{}) { 135 | var inf, code string 136 | 137 | if level < l.level { 138 | return 139 | } 140 | 141 | if format != "" { 142 | inf = fmt.Sprintf(format, args...) 143 | } else { 144 | inf = fmt.Sprint(args...) 145 | } 146 | 147 | // source code, file and line num 148 | _, file, line, ok := runtime.Caller(2) 149 | if ok { 150 | code = path.Base(file) + ":" + strconv.Itoa(line) 151 | } 152 | 153 | // format time 154 | now := time.Now() 155 | if now.Unix() != l.lastTime { 156 | l.lastTime = now.Unix() 157 | l.lastTimeStr = now.Format(l.layout) 158 | } 159 | r := l.recordPool.Get().(*Record) 160 | r.info = inf 161 | r.code = code 162 | r.time = l.lastTimeStr 163 | r.level = level 164 | 165 | l.tunnel <- r 166 | } 167 | 168 | func boostrapLogWriter(logger *Logger) { 169 | if logger == nil { 170 | panic("logger is nil") 171 | } 172 | 173 | var ( 174 | r *Record 175 | ok bool 176 | ) 177 | 178 | if r, ok = <-logger.tunnel; !ok { 179 | logger.c <- true 180 | return 181 | } 182 | 183 | for _, w := range logger.writers { 184 | if err := w.Write(r); err != nil { 185 | log.Println(err) 186 | } 187 | } 188 | 189 | flushTimer := time.NewTimer(time.Millisecond * 500) 190 | rotateTimer := time.NewTimer(time.Second * 10) 191 | 192 | for { 193 | select { 194 | case r, ok = <-logger.tunnel: 195 | if !ok { 196 | logger.c <- true 197 | return 198 | } 199 | for _, w := range logger.writers { 200 | if err := w.Write(r); err != nil { 201 | log.Println(err) 202 | } 203 | } 204 | 205 | logger.recordPool.Put(r) 206 | 207 | case <-flushTimer.C: 208 | for _, w := range logger.writers { 209 | if f, ok := w.(Flusher); ok { 210 | if err := f.Flush(); err != nil { 211 | log.Println(err) 212 | } 213 | } 214 | } 215 | flushTimer.Reset(time.Millisecond * 1000) 216 | 217 | case <-rotateTimer.C: 218 | for _, w := range logger.writers { 219 | if r, ok := w.(Rotater); ok { 220 | if err := r.Rotate(); err != nil { 221 | log.Println(err) 222 | } 223 | } 224 | } 225 | rotateTimer.Reset(time.Second * 10) 226 | } 227 | } 228 | } 229 | 230 | // default logger 231 | var ( 232 | logger_default *Logger 233 | takeup = false 234 | ) 235 | 236 | func SetLevel(lvl int) { 237 | defaultLoggerInit() 238 | logger_default.level = lvl 239 | } 240 | 241 | func SetLayout(layout string) { 242 | defaultLoggerInit() 243 | logger_default.layout = layout 244 | } 245 | 246 | func Trace(fmt string, args ...interface{}) { 247 | defaultLoggerInit() 248 | logger_default.deliverRecordToWriter(TRACE, fmt, args...) 249 | } 250 | 251 | func Debug(fmt string, args ...interface{}) { 252 | defaultLoggerInit() 253 | logger_default.deliverRecordToWriter(DEBUG, fmt, args...) 254 | } 255 | 256 | func Warn(fmt string, args ...interface{}) { 257 | defaultLoggerInit() 258 | logger_default.deliverRecordToWriter(WARNING, fmt, args...) 259 | } 260 | 261 | func Info(fmt string, args ...interface{}) { 262 | defaultLoggerInit() 263 | logger_default.deliverRecordToWriter(INFO, fmt, args...) 264 | } 265 | 266 | func Error(fmt string, args ...interface{}) { 267 | defaultLoggerInit() 268 | logger_default.deliverRecordToWriter(ERROR, fmt, args...) 269 | } 270 | 271 | func Fatal(fmt string, args ...interface{}) { 272 | defaultLoggerInit() 273 | logger_default.deliverRecordToWriter(FATAL, fmt, args...) 274 | } 275 | 276 | func Register(w Writer) { 277 | defaultLoggerInit() 278 | logger_default.Register(w) 279 | } 280 | 281 | func Close() { 282 | defaultLoggerInit() 283 | logger_default.Close() 284 | logger_default = nil 285 | takeup = false 286 | } 287 | 288 | func defaultLoggerInit() { 289 | if takeup == false { 290 | logger_default = NewLogger() 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /common/log/log_test.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | //测试日志实例打点 9 | func TestLogInstance(t *testing.T) { 10 | nlog := NewLogger() 11 | logConf := LogConfig{ 12 | Level: "trace", 13 | FW: ConfFileWriter{ 14 | On: true, 15 | LogPath: "./log_test.log", 16 | RotateLogPath: "./log_test.log", 17 | WfLogPath: "./log_test.wf.log", 18 | RotateWfLogPath: "./log_test.wf.log", 19 | }, 20 | CW: ConfConsoleWriter{ 21 | On: true, 22 | Color: true, 23 | }, 24 | } 25 | SetupLogInstanceWithConf(logConf, nlog) 26 | nlog.Info("test message") 27 | nlog.Close() 28 | time.Sleep(time.Second) 29 | } 30 | -------------------------------------------------------------------------------- /conf/dev/base.toml: -------------------------------------------------------------------------------- 1 | # This is base config 2 | 3 | [base] 4 | debug_mode="debug" 5 | time_location="Asia/Chongqing" 6 | 7 | [http] 8 | addr =":20150" # 监听地址, default ":8700" 9 | read_timeout = 10 # 读取超时时长 10 | write_timeout = 10 # 写入超时时长 11 | max_header_bytes = 20 # 最大的header大小,二进制位长度 12 | limitStatus = 0 # 限流状态 0 关闭,1开启 13 | limitRequestCountPerSecond = 1 # 每秒限流的请求个数 14 | crpyStatus = 1 # 加密状态 15 | allow_ip = [ # 白名单ip列表 16 | "127.0.0.1", 17 | "192.168.1.1", 18 | "::1", 19 | ] 20 | deny_ip = [ # 黑单ip列表 21 | "127.0.0.1", 22 | "192.168.1.1", 23 | "::1", 24 | ] 25 | 26 | [log] 27 | log_level = "trace" #日志打印最低级别 28 | [log.file_writer] #文件写入配置 29 | on = true 30 | log_path = "./logs/gin_scaffold.inf.log" 31 | rotate_log_path = "./logs/gin_scaffold.inf.log.%Y%M%D%H" 32 | wf_log_path = "./logs/gin_scaffold.wf.log" 33 | rotate_wf_log_path = "./logs/gin_scaffold.wf.log.%Y%M%D%H" 34 | [log.console_writer] #工作台输出 35 | on = false 36 | color = false 37 | 38 | [swagger] 39 | title="gin_scaffold swagger API" 40 | desc="This is a sample server celler server." 41 | host="127.0.0.1:8880" 42 | base_path="" 43 | -------------------------------------------------------------------------------- /conf/dev/mysql_map.toml: -------------------------------------------------------------------------------- 1 | # this is mysql config 2 | [list] 3 | [list.default] 4 | driver_name = "mysql" 5 | data_source_name = "root:123qwe!@#@tcp(10.10.10.33:3306)/test?charset=utf8&parseTime=true&loc=Asia%2FChongqing" 6 | max_open_conn = 20 7 | max_idle_conn = 10 8 | max_conn_life_time = 100 -------------------------------------------------------------------------------- /conf/dev/redis_map.toml: -------------------------------------------------------------------------------- 1 | # this is redis config file 2 | [list] 3 | [list.default] 4 | proxy_list = ["10.10.10.33:6379"] 5 | max_active = 100 6 | max_idle = 100 7 | down_grade = false -------------------------------------------------------------------------------- /constant/constants.go: -------------------------------------------------------------------------------- 1 | package constant 2 | 3 | // 加解密使用的http请求头里的签名 4 | const RequestHeaderSignature = "signature" 5 | 6 | // 确认登录里面的token 7 | const RequestLoginToken = "token" 8 | 9 | // ctx里面的用户 10 | const CtxUser = "ctxUser" 11 | 12 | // 生成jwt和用户密码时使用的盐 13 | const Salt = "@#1$%^&*3(_)opg#2$%^&*(5HG~|?/-+%;6." 14 | 15 | // redis 中rsa公钥匙 16 | const RedisRsaPublicKey = "rsa_private_key" 17 | 18 | // redis 中rsa私钥 19 | const RedisRsaPrivateKey = "rsa_public_key" 20 | 21 | // ctx中的对称加密密钥对 22 | const CtxAesKey = "ctxAesKey" 23 | 24 | // 用户token超时时间 (20分钟) 25 | const UserLoginToKenExpireTime = 1200 26 | 27 | // 未登录token超时时间 28 | const UnLoginToKenExpireTime = UserLoginToKenExpireTime 29 | 30 | // 响应体是否加密 31 | const ReponseBodyCrypto = "reponseBodyCrypto" 32 | -------------------------------------------------------------------------------- /controller/api.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "fehu/constant" 5 | "fehu/middleware" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | type ApiController struct { 10 | } 11 | 12 | func ApiRegister(router *gin.RouterGroup) { 13 | api := ApiController{} 14 | router.POST("/needLogin", api.NeedLogin) 15 | } 16 | 17 | //登录,获取jwt token 18 | func (a *ApiController) NeedLogin(c *gin.Context) { 19 | value, _ := c.Get(constant.CtxUser) 20 | middleware.Success(c, value) 21 | } 22 | -------------------------------------------------------------------------------- /controller/base/base.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "encoding/json" 5 | "fehu/common/lib/redis_lib" 6 | "fehu/constant" 7 | "fehu/middleware" 8 | "fehu/model/param" 9 | "fehu/util/cryp" 10 | "github.com/gin-gonic/gin" 11 | "strings" 12 | ) 13 | 14 | type BaseController struct { 15 | } 16 | 17 | func BaseRegister(router *gin.RouterGroup) { 18 | base := BaseController{} 19 | router.GET("/img", base.Img) 20 | router.POST("/sayHi", base.SayHi) 21 | } 22 | 23 | /** 24 | 25 | */ 26 | func (b *BaseController) Img(c *gin.Context) { 27 | hex := c.Query("hex") 28 | if hex != "" { 29 | // 判断是否和原有hex一致,不要重复发送公钥匙 30 | if strings.Trim(hex, " ") == "1234" { 31 | c.Data(201, "image/png", []byte{}) 32 | return 33 | } 34 | } 35 | // hex 为空,或者不想等代表客户端公钥已经过期,重新发送公钥 36 | publickKey, e := redis_lib.GetString(constant.RedisRsaPublicKey, "") 37 | if e != nil || publickKey == "" { 38 | privateKeyBytes, publicKeyBytes := cryp.GenerateRsaKey(2048) 39 | e = redis_lib.Set(constant.RedisRsaPublicKey, string(publicKeyBytes), 3600*24, "") 40 | if e != nil { 41 | c.Data(500, "image/png", []byte{}) 42 | return 43 | } 44 | e = redis_lib.Set(constant.RedisRsaPrivateKey, string(privateKeyBytes), 3600*24, "") 45 | if e != nil { 46 | c.Data(500, "image/png", []byte{}) 47 | return 48 | } 49 | c.Header("content-disposition", `attachment; filename=app01.png`) 50 | s := cryp.Base64EncodeByteCount(publicKeyBytes, 5) 51 | c.Data(200, "image/png", []byte(s)) 52 | } else { 53 | c.Header("content-disposition", `attachment; filename=app01.png`) 54 | s := cryp.Base64EncodeByteCount([]byte(publickKey), 5) 55 | c.Data(200, "image/png", []byte(s)) 56 | } 57 | 58 | } 59 | 60 | func (b *BaseController) SayHi(c *gin.Context) { 61 | request := c.Request 62 | data, err := c.GetRawData() 63 | if err != nil { 64 | middleware.EncryptionError(c, "[1]解密失败!") 65 | return 66 | } 67 | privateKey, err := redis_lib.GetString(constant.RedisRsaPrivateKey, "") 68 | if err != nil { 69 | middleware.EncryptionError(c, "[2]解密失败!") 70 | return 71 | } 72 | jsonbytes, err := cryp.RsaDecrypt(data, privateKey) 73 | if err != nil { 74 | middleware.EncryptionError(c, "[3]解密失败!") 75 | return 76 | } 77 | // 解密成功,做签名校验 78 | signature := request.Header.Get("signature") 79 | sha256Hex, err := cryp.RsaDecrypt([]byte(signature), privateKey) 80 | if err != nil { 81 | middleware.SignatureError(c, "[1]签名错误!") 82 | return 83 | } 84 | if code := cryp.GetSHA256HashCode(jsonbytes); code != string(sha256Hex) { 85 | middleware.SignatureError(c, "[2]签名错误!") 86 | return 87 | } 88 | 89 | // 转换json做业务逻辑规则校验 90 | var param param.BaseParam 91 | err = json.Unmarshal(jsonbytes, ¶m) 92 | if err != nil { 93 | middleware.ParamVerifyError(c, "参数解析失败!") 94 | return 95 | } 96 | // 1.版本号 97 | if param.Version == "" { 98 | middleware.ParamRequireError(c) 99 | return 100 | } 101 | // 生成对称加密密钥,发送给前端 102 | uuId := cryp.GenUUID() 103 | //claims := &jwt.JWTClaims{ 104 | // TokenId: cryp.GenUUID(), 105 | //} 106 | //claims.IssuedAt = time.Now().Unix() 107 | //claims.ExpiresAt = time.Now().Add(time.Second * time.Duration(jwt.ExpireTime)).Unix() 108 | //singedToken, err := jwt.GenToken(claims) 109 | //if err != nil { 110 | // middleware.ErrorMsg(c, "口令生成失败!") 111 | // return 112 | //} 113 | //// uuid 放到 redis中 114 | //userForm := form.UserForm{ 115 | // AesKey: c.GetString(constant.CtxAesKey), 116 | //} 117 | //err = redis_lib.Set(claims.TokenId, userForm, constant.UserLoginToKenExpireTime, "") 118 | //if err != nil { 119 | // middleware.ErrorMsg(c, "设置缓存失败!") 120 | // return 121 | //} 122 | bytes, err := cryp.PriKeyENCTYPT([]byte(uuId), privateKey) 123 | // 发送对称加密密钥 124 | middleware.Success(c, map[string]string{"key": cryp.Base64EncodeByte(bytes)}) 125 | 126 | } 127 | -------------------------------------------------------------------------------- /controller/demo.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "fehu/common/lib" 5 | "fehu/common/lib/redis_lib" 6 | "fehu/middleware" 7 | "fehu/model" 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | type DemoController struct { 12 | } 13 | 14 | func DemoRegister(router *gin.RouterGroup) { 15 | demo := DemoController{} 16 | router.GET("/index", demo.Index) 17 | router.POST("/index2", demo.Index) 18 | //router.Any("/bind", demo.Bind) 19 | router.GET("/dao", demo.Dao) 20 | router.GET("/redis", demo.Redis) 21 | } 22 | 23 | func (demo *DemoController) Index(c *gin.Context) { 24 | middleware.Success(c, "") 25 | return 26 | } 27 | 28 | func (demo *DemoController) Dao(c *gin.Context) { 29 | tx, err := lib.GetGormPool("default") 30 | if err != nil { 31 | middleware.ResponseError(c, 2000, err) 32 | return 33 | } 34 | area, err := (&model.Area{}).Find(c, tx, c.DefaultQuery("id", "1")) 35 | if err != nil { 36 | middleware.ResponseError(c, 2001, err) 37 | return 38 | } 39 | middleware.Success(c, area) 40 | return 41 | } 42 | 43 | func (demo *DemoController) Redis(c *gin.Context) { 44 | redisKey := "redis_key" 45 | err := redis_lib.Set(redisKey, "test redis value 2143refwrgew", -1, "") 46 | if err != nil { 47 | middleware.ResponseError(c, 2001, err) 48 | return 49 | } 50 | s, err := redis_lib.GetString(redisKey, "") 51 | if err != nil { 52 | middleware.ResponseError(c, 2001, err) 53 | return 54 | } 55 | middleware.Success(c, s) 56 | return 57 | } 58 | 59 | // ListPage godoc 60 | // @Summary 测试数据绑定 61 | // @Description 测试数据绑定 62 | // @Tags 用户 63 | // @ID /demo/bind 64 | // @Accept json 65 | // @Produce json 66 | // @Param polygon body dto.DemoInput true "body" 67 | // @Success 200 {object} middleware.Response{data=dto.DemoInput} "success" 68 | // @Router /demo/bind [post] 69 | //func (demo *DemoController) Bind(c *gin.Context) { 70 | // params := &dto.DemoInput{} 71 | // if err := params.BindingValidParams(c); err != nil { 72 | // middleware.ResponseError(c, 2001, err) 73 | // return 74 | // } 75 | // middleware.ResponseSuccess(c, params) 76 | // return 77 | //} 78 | -------------------------------------------------------------------------------- /controller/login.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "fehu/common/lib/redis_lib" 5 | "fehu/constant" 6 | "fehu/middleware" 7 | "fehu/model/form" 8 | "fehu/model/param" 9 | "fehu/util/cryp" 10 | "fehu/util/jwt" 11 | "fehu/util/map_builder" 12 | "fmt" 13 | "github.com/gin-gonic/gin" 14 | "time" 15 | ) 16 | 17 | type LoginController struct { 18 | } 19 | 20 | func NoLoginRequiredRegister(router *gin.RouterGroup) { 21 | l := LoginController{} 22 | router.POST("/login", l.login) 23 | } 24 | 25 | //登录,获取jwt token 26 | func (l *LoginController) login(c *gin.Context) { 27 | var loginParam param.LoginParam 28 | err := c.BindJSON(&loginParam) 29 | if err != nil { 30 | fmt.Println(err) 31 | } 32 | if true { 33 | claims := &jwt.JWTClaims{ 34 | //UserID: 1, 35 | //Username: loginParam.Username, 36 | //Password: loginParam.Password, 37 | TokenId: cryp.GenUUID(), 38 | } 39 | claims.IssuedAt = time.Now().Unix() 40 | claims.ExpiresAt = time.Now().Add(time.Second * time.Duration(jwt.ExpireTime)).Unix() 41 | fmt.Println(claims.ExpiresAt) 42 | singedToken, err := jwt.GenToken(claims) 43 | if err != nil { 44 | middleware.ErrorMsg(c, "口令生成失败!") 45 | return 46 | } 47 | //map[string]interface{}{"token": singedToken} 48 | // 将用户信息放到redis中 49 | userForm := form.UserForm{ 50 | Username: loginParam.Username, 51 | UserId: cryp.GenUUID(), 52 | AesKey: c.GetString(constant.CtxAesKey), 53 | } 54 | err = redis_lib.Set(claims.TokenId, userForm, constant.UnLoginToKenExpireTime, "") 55 | if err != nil { 56 | middleware.ErrorMsg(c, "登录失败!") 57 | return 58 | } 59 | middleware.Success(c, map_builder.BuilderMap( 60 | "token", singedToken, 61 | "test", 1234)) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /gin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func main() { 6 | r := gin.Default() 7 | r.GET("/ping", func(c *gin.Context) { 8 | c.JSON(200, gin.H{ 9 | "message": "pong", 10 | }) 11 | }) 12 | r.Run() // listen and serve on 0.0.0.0:8080 13 | } 14 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module fehu 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect 7 | github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 // indirect 8 | github.com/dgrijalva/jwt-go v3.2.0+incompatible 9 | github.com/e421083458/gorm v1.0.1 10 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect 11 | github.com/garyburd/redigo v1.6.0 12 | github.com/gin-gonic/contrib v0.0.0-20190526021735-7fb7810ed2a0 13 | github.com/gin-gonic/gin v1.6.3 14 | github.com/go-playground/locales v0.13.0 15 | github.com/go-playground/universal-translator v0.17.0 16 | github.com/go-sql-driver/mysql v1.4.1 // indirect 17 | github.com/golang/protobuf v1.4.2 // indirect 18 | github.com/gomodule/redigo v2.0.0+incompatible 19 | github.com/google/uuid v1.1.1 20 | github.com/gorilla/sessions v1.1.3 // indirect 21 | github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a // indirect 22 | github.com/jinzhu/now v1.0.0 // indirect 23 | github.com/juju/ratelimit v1.0.1 24 | github.com/lib/pq v1.1.1 // indirect 25 | github.com/mattn/go-sqlite3 v1.10.0 // indirect 26 | github.com/pkg/errors v0.8.1 27 | github.com/spf13/viper v1.3.2 28 | github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 29 | github.com/swaggo/gin-swagger v1.2.0 30 | github.com/swaggo/swag v1.6.5 31 | github.com/xinliangnote/go-util v0.0.0-20200323134426-527984dc34bf 32 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 // indirect 33 | google.golang.org/appengine v1.6.0 // indirect 34 | gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect 35 | gopkg.in/go-playground/validator.v9 v9.29.0 36 | gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df 37 | gopkg.in/yaml.v2 v2.3.0 // indirect 38 | ) 39 | -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log: -------------------------------------------------------------------------------- 1 | [INFO][2020-05-20T08:32:02.715][log.go:58] [request log]||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=||request_method=GET||request_client_ip=127.0.0.1||response=||cost_time=0ms||request_proto=HTTP/1.1||traceid=c0a82b205ec47a82c418439d104dc7b0||cspanid=||spanid=9e6c51a2380704bb||request_time=1589934722715||request_uri=/swagger/index.html||request_raw_data=||response_time=1589934722715 2 | [INFO][2020-05-20T08:32:02.715][log.go:58] [request log]||request_time=1589934722877||request_method=GET||request_uri=/swagger/swagger-ui.css||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||cspanid=||request_proto=HTTP/1.1||response_time=1589934722877||cost_time=0ms||traceid=c0a82b205ec47a826ab0439d5a8581b0||request_referer=http://127.0.0.1:20150/swagger/index.html||request_raw_data=||request_client_ip=127.0.0.1||response=||spanid=9e6c51a257e9d186 3 | [INFO][2020-05-20T08:32:02.715][log.go:58] [request log]||request_time=1589934722883||traceid=c0a82b205ec47a82c1c8439d66cb39b0||cspanid=||request_method=GET||spanid=9e6c51a21408d2ac||request_client_ip=127.0.0.1||response_time=1589934722895||cost_time=12ms||request_proto=HTTP/1.1||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=http://127.0.0.1:20150/swagger/index.html||request_uri=/swagger/swagger-ui-bundle.js||request_raw_data=||response= 4 | [INFO][2020-05-20T08:32:02.715][log.go:58] [request log]||request_method=GET||request_referer=http://127.0.0.1:20150/swagger/index.html||response_time=1589934722922||response=||request_proto=HTTP/1.1||request_raw_data=||request_client_ip=127.0.0.1||request_uri=/swagger/swagger-ui-standalone-preset.js||cost_time=1ms||spanid=9e6c51a2268447a4||request_time=1589934722921||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||traceid=c0a82b205ec47a82e4d8439d697f48b0||cspanid= 5 | [INFO][2020-05-20T08:32:03.555][log.go:58] [request log]||request_referer=http://127.0.0.1:20150/swagger/index.html||request_raw_data=||request_time=1589934723555||request_proto=HTTP/1.1||spanid=9e6c51a368255aaf||request_uri=/swagger/doc.json||response=||cspanid=||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_client_ip=127.0.0.1||cost_time=0ms||traceid=c0a82b205ec47a838b60439df27cc6b0||request_method=GET||response_time=1589934723555 6 | [INFO][2020-05-20T08:32:03.555][log.go:58] [request log]||request_time=1589934723829||request_raw_data=||request_client_ip=127.0.0.1||cost_time=11ms||request_proto=HTTP/1.1||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=http://127.0.0.1:20150/swagger/index.html||request_method=GET||response=||traceid=c0a82b205ec47a837400439d6cffa2b0||spanid=9e6c51a330b95ff1||request_uri=/swagger/favicon-32x32.png||response_time=1589934723840||cspanid= 7 | [INFO][2020-05-20T08:38:19.244][log.go:58] [request log]||request_uri=/swagger/index.html||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_raw_data=||request_method=GET||request_client_ip=127.0.0.1||traceid=c0a82b205ec47bfb9e0843ee104dc7b0||cspanid=||request_proto=HTTP/1.1||request_referer=||request_time=1589935099242||response_time=1589935099243||response=||cost_time=1ms||spanid=9e6c50db380704bb 8 | [INFO][2020-05-20T08:38:19.244][log.go:58] [request log]||request_time=1589935099408||request_uri=/swagger/swagger-ui.css||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_client_ip=127.0.0.1||response_time=1589935099409||traceid=c0a82b205ec47bfb6ba043ee5a8581b0||request_method=GET||request_proto=HTTP/1.1||spanid=9e6c50db57e9d186||request_referer=http://127.0.0.1:20150/swagger/index.html||request_raw_data=||response=||cspanid=||cost_time=1ms 9 | [INFO][2020-05-20T08:38:19.244][log.go:58] [request log]||request_referer=http://127.0.0.1:20150/swagger/index.html||traceid=c0a82b205ec47bfb5a0043ee66cb39b0||request_client_ip=127.0.0.1||response=||cost_time=0ms||request_time=1589935099411||request_proto=HTTP/1.1||request_raw_data=||request_method=GET||cspanid=||spanid=9e6c50db1408d2ac||request_uri=/swagger/swagger-ui-standalone-preset.js||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||response_time=1589935099411 10 | [INFO][2020-05-20T08:38:19.244][log.go:58] [request log]||request_referer=http://127.0.0.1:20150/swagger/index.html||request_raw_data=||request_uri=/swagger/swagger-ui-bundle.js||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||traceid=c0a82b205ec47bfbc20843ee697f48b0||spanid=9e6c50db268447a4||request_proto=HTTP/1.1||response_time=1589935099459||response=||cost_time=37ms||cspanid=||request_time=1589935099422||request_method=GET||request_client_ip=127.0.0.1 11 | [INFO][2020-05-20T08:38:20.262][log.go:58] [request log]||request_client_ip=127.0.0.1||cost_time=1ms||request_time=1589935100261||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=http://127.0.0.1:20150/swagger/index.html||request_uri=/swagger/doc.json||response=||traceid=c0a82b205ec47bfcf64843eef27cc6b0||cspanid=||spanid=9e6c50dc68255aaf||request_method=GET||request_proto=HTTP/1.1||request_raw_data=||response_time=1589935100262 12 | [INFO][2020-05-20T08:39:10.476][log.go:58] [request log]||response_time=1589935150476||request_uri=/swagger/swagger-ui.css||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||cost_time=1ms||cspanid=||request_proto=HTTP/1.1||request_client_ip=127.0.0.1||traceid=c0a82b205ec47c2ea2a043ee6cffa2b0||spanid=9e6c570e30b95ff1||request_referer=http://127.0.0.1:20150/swagger/index.html||request_raw_data=||response=||request_time=1589935150475||request_method=GET 13 | [INFO][2020-05-20T08:39:10.476][log.go:58] [request log]||cost_time=41ms||traceid=c0a82b205ec47c2efa8043ee3a768bb0||request_method=GET||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=||request_client_ip=127.0.0.1||response=||request_uri=/swagger/swagger-ui-standalone-preset.js.map||request_proto=HTTP/1.1||request_raw_data=||request_time=1589935150566||spanid=9e6c570e25845c95||response_time=1589935150607||cspanid= 14 | [INFO][2020-05-20T08:39:10.476][log.go:58] [request log]||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_client_ip=127.0.0.1||cspanid=||request_method=GET||request_uri=/swagger/swagger-ui.css.map||cost_time=0ms||spanid=9e6c570e1bf98be2||request_time=1589935150624||request_proto=HTTP/1.1||request_referer=||response=||request_raw_data=||response_time=1589935150624||traceid=c0a82b205ec47c2e30a043eeec3f25b0 15 | [INFO][2020-05-20T08:39:10.476][log.go:58] [request log]||request_referer=||response=||traceid=c0a82b205ec47c2e86e843eeb62158b0||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_client_ip=127.0.0.1||response_time=1589935150739||request_time=1589935150563||request_raw_data=||cspanid=||spanid=9e6c570e3c04951a||request_method=GET||request_uri=/swagger/swagger-ui-bundle.js.map||request_proto=HTTP/1.1||cost_time=176ms 16 | [INFO][2020-05-20T08:42:33.160][log.go:58] [request log]||response_time=1589935353160||cost_time=0ms||request_client_ip=127.0.0.1||traceid=c0a82b205ec47cf95ff843ee02070fb0||request_time=1589935353160||request_uri=/pinglocale=zh||request_referer=||response=||spanid=9e6c57d92e3108da||request_method=GET||request_proto=HTTP/1.1||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_raw_data=||cspanid= 17 | [INFO][2020-05-20T08:42:33.160][log.go:58] [request log]||request_uri=/favicon.ico||cost_time=0ms||cspanid=||response_time=1589935353848||traceid=c0a82b205ec47cf9cfb843ee0bd268b0||request_method=GET||request_proto=HTTP/1.1||request_client_ip=127.0.0.1||response=||spanid=9e6c57d96e661e92||request_time=1589935353848||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=http://127.0.0.1:20150/pinglocale=zh||request_raw_data= 18 | [INFO][2020-05-20T08:42:38.434][log.go:58] [request log]||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_raw_data=||request_client_ip=127.0.0.1||response=||spanid=9e6c57de2606cd2b||request_time=1589935358433||cost_time=0ms||traceid=c0a82b205ec47cfe57c843ee84c47fb0||cspanid=||request_method=GET||request_uri=/ping?locale=zh||request_proto=HTTP/1.1||request_referer=||response_time=1589935358433 19 | [INFO][2020-05-20T08:42:42.702][log.go:58] [request log]||request_referer=||request_client_ip=127.0.0.1||request_time=1589935362701||request_method=GET||response=||traceid=c0a82b205ec47d0237f843ee54502fb0||request_proto=HTTP/1.1||request_raw_data=||spanid=9e6c56221a714cf8||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||cspanid=||cost_time=0ms||request_uri=/ping?locale=zh||response_time=1589935362701 20 | -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020051810: -------------------------------------------------------------------------------- 1 | [INFO][2020-05-18T10:26:38.683][log.go:58] [request log]||request_referer=||response={\"errno\":0,\"errmsg\":\"\",\"data\":{\"key\":\"93676cee-3f50-4b75-bfc4-096376d2f7c5\"},\"trace_id\":\"c0a82b205ec1f25e22c026bb104dc7b0\"}||cost_time=0ms||cspanid=||request_uri=/sayHi||traceid=c0a82b205ec1f25e22c026bb104dc7b0||spanid=9e69d97e380704bb||request_time=1589768798682||request_method=POST||request_proto=HTTP/1.1||request_raw_data=1234||request_client_ip=::1||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||response_time=1589768798682 2 | [INFO][2020-05-18T10:49:13.732][log.go:58] _com_redis_success||bind=[redis_key redis_value]||reply=OK||proc_time=0.000995ms||traceid=c0a82b205ec1f7a96cf82732104dc7b0||cspanid=||spanid=9e69dc89380704bb||method=SET 3 | [INFO][2020-05-18T10:49:13.732][log.go:58] _com_redis_success||reply=redis_value||proc_time=0.000439ms||traceid=c0a82b205ec1f7a96cf82732104dc7b0||cspanid=||spanid=9e69dc89380704bb||method=GET||bind=[redis_key] 4 | [INFO][2020-05-18T10:49:13.732][log.go:58] [request log]||cost_time=5ms||cspanid=||request_time=1589770153729||request_proto=HTTP/1.1||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||response={\"errno\":0,\"errmsg\":\"\",\"data\":\"redis_value\",\"trace_id\":\"c0a82b205ec1f7a96cf82732104dc7b0\"}||request_method=GET||request_uri=/demo/redis||response_time=1589770153734||traceid=c0a82b205ec1f7a96cf82732104dc7b0||spanid=9e69dc89380704bb||request_referer=||request_raw_data=||request_client_ip=::1 5 | [INFO][2020-05-18T10:53:56.260][log.go:58] [request log]||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_raw_data=1234||cost_time=0ms||traceid=c0a82b205ec1f8c42ef0273266cb39b0||request_time=1589770436260||request_proto=HTTP/1.1||request_referer=||response={\"errno\":0,\"errmsg\":\"\",\"data\":{\"key\":\"462f7b3e-c040-4d94-88e5-17d914a905e2\"},\"trace_id\":\"c0a82b205ec1f8c42ef0273266cb39b0\"}||cspanid=||request_method=POST||request_uri=/sayHi||request_client_ip=::1||response_time=1589770436260||spanid=9e69d3e41408d2ac 6 | -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020051811: -------------------------------------------------------------------------------- 1 | [INFO][2020-05-18T11:03:06.484][log.go:58] [request log]||request_raw_data=1234||request_client_ip=::1||cost_time=0ms||cspanid=||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=||response={\"errno\":0,\"errmsg\":\"\",\"data\":{\"key\":\"725017f8-4ca9-478a-a33f-126b5d61b01f\"},\"trace_id\":\"c0a82b205ec1faeafc282732697f48b0\"}||request_time=1589770986484||request_proto=HTTP/1.1||response_time=1589770986484||request_method=POST||request_uri=/sayHi||traceid=c0a82b205ec1faeafc282732697f48b0||spanid=9e69d1ca268447a4 2 | [INFO][2020-05-18T11:27:28.028][log.go:58] [request log]||request_uri=/sayHi||request_proto=HTTP/1.1||spanid=9e6a2b80380704bb||request_raw_data=||request_client_ip=::1||response={\"errno\":0,\"errmsg\":\"\",\"data\":{\"key\":\"c9be3ee8-8ac7-4a5a-a9d2-93b5cdce74ee\"},\"trace_id\":\"c0a82b205ec200a0c48028e1104dc7b0\"}||traceid=c0a82b205ec200a0c48028e1104dc7b0||request_time=1589772448018||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=||cspanid=||request_method=POST||response_time=1589772448019||cost_time=1ms 3 | [INFO][2020-05-18T11:27:37.288][log.go:58] [request log]||request_method=POST||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||response_time=1589772457288||request_uri=/api/login||traceid=c0a82b205ec200a9479028e15a8581b0||request_proto=HTTP/1.1||response={\"errno\":0,\"errmsg\":\"\",\"data\":null,\"trace_id\":\"c0a82b205ec200a9479028e15a8581b0\"}||cost_time=0ms||request_client_ip=::1||cspanid=||spanid=9e6a2b8957e9d186||request_time=1589772457288||request_referer=||request_raw_data= 4 | [INFO][2020-05-18T11:30:19.559][log.go:58] _com_redis_success||proc_time=0.000415ms||traceid=c0a82b205ec2014b32e02904104dc7b0||cspanid=||spanid=9e6a2a6b380704bb||method=SET||bind=[redis_key redis_value]||reply=OK 5 | [INFO][2020-05-18T11:30:19.559][log.go:58] _com_redis_success||traceid=c0a82b205ec2014b32e02904104dc7b0||cspanid=||spanid=9e6a2a6b380704bb||method=GET||bind=[redis_key]||reply=redis_value||proc_time=0.000861ms 6 | [INFO][2020-05-18T11:30:19.559][log.go:58] [request log]||request_client_ip=::1||cost_time=3ms||cspanid=||request_uri=/demo/redis||request_raw_data=||response_time=1589772619560||spanid=9e6a2a6b380704bb||request_time=1589772619557||request_referer=||request_method=GET||request_proto=HTTP/1.1||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||response={\"errno\":0,\"errmsg\":\"\",\"data\":\"redis_value\",\"trace_id\":\"c0a82b205ec2014b32e02904104dc7b0\"}||traceid=c0a82b205ec2014b32e02904104dc7b0 7 | [INFO][2020-05-18T11:30:24.456][log.go:58] [request log]||request_raw_data=||request_client_ip=::1||response={\"errno\":0,\"errmsg\":\"\",\"data\":null,\"trace_id\":\"c0a82b205ec201506ba8290466cb39b0\"}||traceid=c0a82b205ec201506ba8290466cb39b0||cspanid=||request_uri=/api/login||request_proto=HTTP/1.1||cost_time=0ms||request_referer=||request_time=1589772624456||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||spanid=9e6a2a701408d2ac||request_method=POST||response_time=1589772624456 8 | [INFO][2020-05-18T11:35:47.530][log.go:58] [request log]||request_uri=/api/login||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||traceid=c0a82b205ec2029308382904697f48b0||cspanid=||spanid=9e6a29b3268447a4||request_client_ip=::1||request_time=1589772947530||request_method=POST||request_raw_data=||response_time=1589772947530||response={\"errno\":0,\"errmsg\":\"\",\"data\":null,\"trace_id\":\"c0a82b205ec2029308382904697f48b0\"}||request_proto=HTTP/1.1||request_referer=||cost_time=0ms 9 | [INFO][2020-05-18T11:38:06.700][log.go:58] [request log]||request_referer=||cspanid=||request_time=1589773086699||request_proto=HTTP/1.1||request_client_ip=::1||response_time=1589773086700||cost_time=1ms||request_method=POST||spanid=9e6a283e380704bb||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_raw_data={\"paramString\":\"1234\"}||response={\"errno\":0,\"errmsg\":\"\",\"data\":{\"key\":\"8ead3bc4-86a0-4a7e-868b-b8adb26fb553\"},\"trace_id\":\"c0a82b205ec2031e7f282941104dc7b0\"}||traceid=c0a82b205ec2031e7f282941104dc7b0||request_uri=/sayHi 10 | [INFO][2020-05-18T11:38:11.567][log.go:58] [request log]||response={\"errno\":0,\"errmsg\":\"\",\"data\":null,\"trace_id\":\"c0a82b205ec20323c12029415a8581b0\"}||traceid=c0a82b205ec20323c12029415a8581b0||request_time=1589773091567||request_method=POST||request_client_ip=::1||cost_time=0ms||response_time=1589773091567||cspanid=||request_uri=/api/login||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_raw_data={\"Username\":\"zhangsan\",\"Password\":\"456\"}||request_proto=HTTP/1.1||request_referer=||spanid=9e6a280357e9d186 11 | -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020051812: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fehu-asia/Archimedes/5c16214e442ad417da32b75bd2efbf76dea0f66d/logs/gin_scaffold.inf.log.2020051812 -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020051813: -------------------------------------------------------------------------------- 1 | [INFO][2020-05-18T13:37:33.823][log.go:58] [request log]||request_time=1589780253823||request_raw_data={\"Username\":\"zhangsan\",\"Password\":\"456\"}||response_time=1589780253823||response={\"code\":1,\"msg\":\"\",\"data\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk3ODM4NTMsImlhdCI6MTU4OTc4MDI1MywidXNlcl9pZCI6MSwicGFzc3dvcmQiOiI0NTYiLCJ1c2VybmFtZSI6InpoYW5nc2FuIn0.48cpxdQTyVYXgvd5viU9ZkoQITuDyNzMHpbKzRP4m5c\",\"traceId\":\"c0a82b205ec21f1d68002a0a104dc7b0\"}||request_referer=||spanid=9e6a343d380704bb||request_client_ip=::1||cost_time=0ms||traceid=c0a82b205ec21f1d68002a0a104dc7b0||cspanid=||request_method=POST||request_uri=/api/login||request_proto=HTTP/1.1||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36 2 | [INFO][2020-05-18T13:38:16.064][log.go:58] [request log]||response={\"code\":1,\"msg\":\"\",\"data\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk3ODM4OTYsImlhdCI6MTU4OTc4MDI5NiwidXNlcl9pZCI6MSwicGFzc3dvcmQiOiI0NTYiLCJ1c2VybmFtZSI6InpoYW5nc2FuIn0.F6j0Q3tT7nhVJa-8wG4YGCYtKCfWpRg0VvtLjfWdnPA\",\"traceId\":\"c0a82b205ec21f48c6b02a22104dc7b0\"}||cspanid=||request_uri=/api/login||request_proto=HTTP/1.1||request_referer=||request_raw_data={\"Username\":\"zhangsan\",\"Password\":\"456\"}||response_time=1589780296063||request_time=1589780296063||request_method=POST||traceid=c0a82b205ec21f48c6b02a22104dc7b0||cost_time=0ms||spanid=9e6a3468380704bb||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_client_ip=::1 3 | [INFO][2020-05-18T13:52:28.442][log.go:58] [request log]||request_referer=||response={\"code\":1,\"msg\":\"\",\"data\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk3ODQ3NDgsImlhdCI6MTU4OTc4MTE0OCwidXNlcl9pZCI6MSwicGFzc3dvcmQiOiI0NTYiLCJ1c2VybmFtZSI6InpoYW5nc2FuIn0.RgSpFgKYGXRQoZ3HkelatC9hhiPaPVPmjQleI7za-s0\",\"traceId\":\"c0a82b205ec2229c25402a225a8581b0\"}||traceid=c0a82b205ec2229c25402a225a8581b0||request_time=1589781148442||request_method=POST||request_proto=HTTP/1.1||request_raw_data={\"Username\":\"zhangsan\",\"Password\":\"456\"}||request_client_ip=::1||cost_time=0ms||request_uri=/api/login||response_time=1589781148442||cspanid=||spanid=9e6a09bc57e9d186||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36 4 | -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020051814: -------------------------------------------------------------------------------- 1 | [INFO][2020-05-18T14:07:02.232][log.go:58] _com_panic||stack=goroutine 9 [running]:\nruntime/debug.Stack(0x18793e0, 0xc0000b3048, 0x196aa79)\n\t/usr/local/go/src/runtime/debug/stack.go:24 +0x9d\nfehu/middleware.RecoveryMiddleware.func1.1(0xc001178000)\n\t/Users/silence/Downloads/goProject/src/fehu/middleware/recovery.go:21 +0x213\npanic(0x1866fe0, 0xc0003f6d20)\n\t/usr/local/go/src/runtime/panic.go:967 +0x15d\ngithub.com/gin-gonic/gin/render.JSON.Render(...)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/render/json.go:59\ngithub.com/gin-gonic/gin.(*Context).Render(0xc001178000, 0xc8, 0x1abc200, 0xc0003f6c60)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:841 +0x146\ngithub.com/gin-gonic/gin.(*Context).JSON(...)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:884\nfehu/middleware.Success(0xc001178000, 0xc0000b3490, 0x1, 0x1)\n\t/Users/silence/Downloads/goProject/src/fehu/middleware/response.go:117 +0x17e\nfehu/controller.(*LoginController).login(0x2981b38, 0xc001178000)\n\t/Users/silence/Downloads/goProject/src/fehu/controller/login.go:45 +0x411\ngithub.com/gin-gonic/gin.(*Context).Next(0xc001178000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 +0x3b\nfehu/middleware.IPAuthMiddleware.func1(0xc001178000)\n\t/Users/silence/Downloads/goProject/src/fehu/middleware/ip_auth.go:23 +0x202\ngithub.com/gin-gonic/gin.(*Context).Next(0xc001178000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 +0x3b\nfehu/middleware.RequestLog.func1(0xc001178000)\n\t/Users/silence/Downloads/goProject/src/fehu/middleware/requestlog.go:56 +0x229\ngithub.com/gin-gonic/gin.(*Context).Next(0xc001178000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 +0x3b\nfehu/middleware.Crpy.func1(0xc001178000)\n\t/Users/silence/Downloads/goProject/src/fehu/middleware/crpy.go:49 +0x45\ngithub.com/gin-gonic/gin.(*Context).Next(0xc001178000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 +0x3b\nfehu/middleware.RecoveryMiddleware.func1(0xc001178000)\n\t/Users/silence/Downloads/goProject/src/fehu/middleware/recovery.go:33 +0x4e\ngithub.com/gin-gonic/gin.(*Context).Next(0xc001178000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 +0x3b\ngithub.com/gin-gonic/gin.RecoveryWithWriter.func1(0xc001178000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/recovery.go:83 +0x60\ngithub.com/gin-gonic/gin.(*Context).Next(0xc001178000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 +0x3b\ngithub.com/gin-gonic/gin.LoggerWithConfig.func1(0xc001178000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/logger.go:241 +0xe1\ngithub.com/gin-gonic/gin.(*Context).Next(0xc001178000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 +0x3b\ngithub.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc00025c000, 0xc001178000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/gin.go:409 +0x666\ngithub.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc00025c000, 0x1ac32c0, 0xc0011322a0, 0xc0002dce00)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/gin.go:367 +0x14d\nnet/http.serverHandler.ServeHTTP(0xc0001bc0e0, 0x1ac32c0, 0xc0011322a0, 0xc0002dce00)\n\t/usr/local/go/src/net/http/server.go:2807 +0xa3\nnet/http.(*conn).serve(0xc000257680, 0x1ac5c40, 0xc0002e4c40)\n\t/usr/local/go/src/net/http/server.go:1895 +0x86c\ncreated by net/http.(*Server).Serve\n\t/usr/local/go/src/net/http/server.go:2933 +0x35c\n||traceid=c0a82b205ec22606abf02af9104dc7b0||cspanid=||spanid=9e6a0d26380704bb||error=json: unsupported type: map_builder.MapBulder 2 | [INFO][2020-05-18T14:07:42.975][log.go:58] _com_panic||error=json: unsupported type: map[interface {}]interface {}||stack=goroutine 50 [running]:\nruntime/debug.Stack(0x1879340, 0xc001169048, 0x196a959)\n\t/usr/local/go/src/runtime/debug/stack.go:24 +0x9d\nfehu/middleware.RecoveryMiddleware.func1.1(0xc0003de000)\n\t/Users/silence/Downloads/goProject/src/fehu/middleware/recovery.go:21 +0x213\npanic(0x1866fa0, 0xc000312b60)\n\t/usr/local/go/src/runtime/panic.go:967 +0x15d\ngithub.com/gin-gonic/gin/render.JSON.Render(...)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/render/json.go:59\ngithub.com/gin-gonic/gin.(*Context).Render(0xc0003de000, 0xc8, 0x1abc0e0, 0xc000312ab0)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:841 +0x146\ngithub.com/gin-gonic/gin.(*Context).JSON(...)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:884\nfehu/middleware.Success(0xc0003de000, 0xc001169490, 0x1, 0x1)\n\t/Users/silence/Downloads/goProject/src/fehu/middleware/response.go:117 +0x17e\nfehu/controller.(*LoginController).login(0x2981b38, 0xc0003de000)\n\t/Users/silence/Downloads/goProject/src/fehu/controller/login.go:45 +0x411\ngithub.com/gin-gonic/gin.(*Context).Next(0xc0003de000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 +0x3b\nfehu/middleware.IPAuthMiddleware.func1(0xc0003de000)\n\t/Users/silence/Downloads/goProject/src/fehu/middleware/ip_auth.go:23 +0x202\ngithub.com/gin-gonic/gin.(*Context).Next(0xc0003de000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 +0x3b\nfehu/middleware.RequestLog.func1(0xc0003de000)\n\t/Users/silence/Downloads/goProject/src/fehu/middleware/requestlog.go:56 +0x229\ngithub.com/gin-gonic/gin.(*Context).Next(0xc0003de000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 +0x3b\nfehu/middleware.Crpy.func1(0xc0003de000)\n\t/Users/silence/Downloads/goProject/src/fehu/middleware/crpy.go:49 +0x45\ngithub.com/gin-gonic/gin.(*Context).Next(0xc0003de000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 +0x3b\nfehu/middleware.RecoveryMiddleware.func1(0xc0003de000)\n\t/Users/silence/Downloads/goProject/src/fehu/middleware/recovery.go:33 +0x4e\ngithub.com/gin-gonic/gin.(*Context).Next(0xc0003de000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 +0x3b\ngithub.com/gin-gonic/gin.RecoveryWithWriter.func1(0xc0003de000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/recovery.go:83 +0x60\ngithub.com/gin-gonic/gin.(*Context).Next(0xc0003de000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 +0x3b\ngithub.com/gin-gonic/gin.LoggerWithConfig.func1(0xc0003de000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/logger.go:241 +0xe1\ngithub.com/gin-gonic/gin.(*Context).Next(0xc0003de000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 +0x3b\ngithub.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc0000d6000, 0xc0003de000)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/gin.go:409 +0x666\ngithub.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc0000d6000, 0x1ac31a0, 0xc0003d80e0, 0xc000410100)\n\t/Users/silence/Downloads/goProject/pkg/mod/github.com/gin-gonic/gin@v1.6.3/gin.go:367 +0x14d\nnet/http.serverHandler.ServeHTTP(0xc0011742a0, 0x1ac31a0, 0xc0003d80e0, 0xc000410100)\n\t/usr/local/go/src/net/http/server.go:2807 +0xa3\nnet/http.(*conn).serve(0xc0000f0000, 0x1ac5b20, 0xc0003d0040)\n\t/usr/local/go/src/net/http/server.go:1895 +0x86c\ncreated by net/http.(*Server).Serve\n\t/usr/local/go/src/net/http/server.go:2933 +0x35c\n||traceid=c0a82b205ec2262ea0682b0e104dc7b0||cspanid=||spanid=9e6a0d0e380704bb 3 | [INFO][2020-05-18T14:08:15.503][log.go:58] [request log]||request_time=1589782095502||cspanid=||spanid=9e6a0d6f380704bb||request_method=POST||request_proto=HTTP/1.1||request_referer=||request_client_ip=::1||response={\"code\":1,\"msg\":\"\",\"data\":{\"test\":1234,\"token\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk3ODU2OTUsImlhdCI6MTU4OTc4MjA5NSwidXNlcl9pZCI6MSwicGFzc3dvcmQiOiI0NTYiLCJ1c2VybmFtZSI6InpoYW5nc2FuIn0.-qQBvWI_XwIbPd4boE_4cMPj-e7Fvj2uqt2xQH95ld8\"},\"traceId\":\"c0a82b205ec2264f7d702b26104dc7b0\"}||request_uri=/api/login||request_raw_data={\"Username\":\"zhangsan\",\"Password\":\"456\"}||response_time=1589782095502||cost_time=0ms||traceid=c0a82b205ec2264f7d702b26104dc7b0||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36 4 | -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020051817: -------------------------------------------------------------------------------- 1 | [INFO][2020-05-18T17:02:56.629][log.go:58] [request log]||request_proto=HTTP/1.1||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=||request_raw_data={\"Username\":\"zhangsan\",\"Password\":\"456\"}||traceid=c0a82b205ec24f409ab83148104dc7b0||cspanid=||request_uri=/api/login||cost_time=2ms||spanid=9e6a6460380704bb||request_time=1589792576626||request_method=POST||request_client_ip=::1||response_time=1589792576628||response={\"code\":1,\"msg\":\"\",\"data\":{\"test\":1234,\"token\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk3OTYxNzYsImlhdCI6MTU4OTc5MjU3NiwidG9rZW5JZCI6ImIyZmE0ZjhiLTJhOGItNGQ3YS1iMGQ4LTNlNzEwNjZhYWU0NyJ9.WkX9dLh8K9w5p_IrV-DivZp0jgfYkWMQG0ePT-cvSqo\"},\"traceId\":\"c0a82b205ec24f409ab83148104dc7b0\"} 2 | [INFO][2020-05-18T17:15:05.898][log.go:58] [request log]||request_method=POST||request_proto=HTTP/1.1||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=||cost_time=1ms||traceid=c0a82b205ec252194fc031cb104dc7b0||request_time=1589793305897||request_uri=/api/needLogin||request_client_ip=::1||response_time=1589793305898||request_raw_data=||response={\"code\":-100,\"msg\":\"您需要登录! err = server is busy\",\"data\":\"\",\"traceId\":\"c0a82b205ec252194fc031cb104dc7b0\",\"stack\":\"您需要登录! err = server is busy\"}||cspanid=||spanid=9e6a7939380704bb 3 | [INFO][2020-05-18T17:15:21.006][log.go:58] [request log]||response={\"code\":1,\"msg\":\"\",\"data\":{\"test\":1234,\"token\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk3OTY5MjEsImlhdCI6MTU4OTc5MzMyMSwidG9rZW5JZCI6IjhjMjU3NjgyLTQxMzYtNGM0NC1iZGM1LTMzNjRkNjRkZTI5ZSJ9.KOg6aur5icueNO9Q8tn9WcPwO00eLHYZ44Ta3HoTcE0\"},\"traceId\":\"c0a82b205ec25229a04031cb5a8581b0\"}||request_time=1589793321003||request_uri=/api/login||request_proto=HTTP/1.1||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||response_time=1589793321005||cspanid=||request_referer=||request_raw_data={\"Username\":\"zhangsan\",\"Password\":\"456\"}||request_client_ip=::1||traceid=c0a82b205ec25229a04031cb5a8581b0||request_method=POST||cost_time=2ms||spanid=9e6a790957e9d186 4 | [INFO][2020-05-18T17:15:28.057][log.go:58] [request log]||request_proto=HTTP/1.1||request_raw_data=||traceid=c0a82b205ec25230c2b831cb08d2acb0||cspanid=||request_method=POST||cost_time=1ms||response={\"code\":1,\"msg\":\"\",\"data\":\"ok\",\"traceId\":\"c0a82b205ec25230c2b831cb08d2acb0\"}||request_referer=||response_time=1589793328057||spanid=9e6a79100c697f48||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_uri=/api/needLogin||request_client_ip=::1||request_time=1589793328056 5 | [INFO][2020-05-18T17:16:27.461][log.go:58] [request log]||response_time=1589793387461||request_referer=||spanid=9e6a794b68255aaf||request_time=1589793387461||request_proto=HTTP/1.1||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_client_ip=::1||response={\"code\":-100,\"msg\":\"您需要登录! err = server is busy\",\"data\":\"\",\"traceId\":\"c0a82b205ec2526b943031cbf27cc6b0\",\"stack\":\"您需要登录! err = server is busy\"}||cspanid=||request_method=POST||request_uri=/api/needLogin||request_raw_data=||cost_time=0ms||traceid=c0a82b205ec2526b943031cbf27cc6b0 6 | [INFO][2020-05-18T17:19:09.748][log.go:58] [request log]||request_raw_data=||request_client_ip=::1||response_time=1589793549748||response={\"code\":1,\"msg\":\"\",\"data\":{\"Username\":\"zhangsan\",\"Password\":\"\",\"UserId\":\"4945d5c5-487d-41ca-8c6c-4a9ef34c1202\"},\"traceId\":\"c0a82b205ec2530d57903215104dc7b0\"}||cspanid=||cost_time=24ms||traceid=c0a82b205ec2530d57903215104dc7b0||spanid=9e6a782d380704bb||request_time=1589793549724||request_proto=HTTP/1.1||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=||request_method=POST||request_uri=/api/needLogin 7 | [INFO][2020-05-18T17:21:29.299][log.go:58] [request log]||response={\"code\":1,\"msg\":\"\",\"data\":{\"Username\":\"zhangsan\",\"Password\":\"\",\"UserId\":\"4945d5c5-487d-41ca-8c6c-4a9ef34c1202\"},\"traceId\":\"c0a82b205ec2539905303232104dc7b0\"}||request_time=1589793689295||request_uri=/api/needLogin||response_time=1589793689298||traceid=c0a82b205ec2539905303232104dc7b0||request_method=POST||request_proto=HTTP/1.1||cost_time=3ms||cspanid=||spanid=9e6a78b9380704bb||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=||request_raw_data=||request_client_ip=::1 8 | -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020051909: -------------------------------------------------------------------------------- 1 | [INFO][2020-05-19T09:22:51.706][log.go:58] [request log]||request_time=1589851371705||request_method=GET||request_raw_data=||response_time=1589851371705||traceid=c0a82b205ec334ebd1e03910104dc7b0||cspanid=||response=||request_uri=/login/Img||request_proto=HTTP/1.1||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=||request_client_ip=::1||cost_time=0ms||spanid=9e6b1fcb380704bb 2 | [INFO][2020-05-19T09:22:59.691][log.go:58] [request log]||request_time=1589851379390||request_method=GET||request_uri=/img||request_proto=HTTP/1.1||request_client_ip=::1||response=||traceid=c0a82b205ec334f3163839105a8581b0||request_referer=||spanid=9e6b1fd357e9d186||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_raw_data=||response_time=1589851379691||cost_time=301ms||cspanid= 3 | [INFO][2020-05-19T09:23:09.991][log.go:58] [request log]||request_raw_data=||request_client_ip=::1||response_time=1589851389991||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=||request_proto=HTTP/1.1||response=||cspanid=||spanid=9e6b1fdd268447a4||request_time=1589851389811||request_uri=/img||traceid=c0a82b205ec334fd4fb03910697f48b0||request_method=GET||cost_time=180ms 4 | [INFO][2020-05-19T09:23:20.336][log.go:58] [request log]||request_uri=/img||request_proto=HTTP/1.1||request_raw_data=||request_client_ip=::1||response=||cost_time=612ms||request_referer=http://localhost:20150/img||request_time=1589851399724||request_method=GET||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||response_time=1589851400336||traceid=c0a82b205ec335077ff039106cffa2b0||cspanid=||spanid=9e6b1e2730b95ff1 5 | [INFO][2020-05-19T09:26:22.051][log.go:58] [request log]||request_uri=/img||request_raw_data=||spanid=9e6b1e9d380704bb||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=||request_client_ip=::1||cspanid=||request_time=1589851581882||request_method=GET||request_proto=HTTP/1.1||response_time=1589851582050||response=||cost_time=168ms||traceid=c0a82b205ec335bd74383940104dc7b0 6 | [INFO][2020-05-19T09:35:57.029][log.go:58] [request log]||request_time=1589852157019||request_method=GET||request_referer=||request_client_ip=::1||cost_time=9ms||traceid=c0a82b205ec337fde09839b3104dc7b0||request_uri=/img||response=||cspanid=||spanid=9e6b1cdd380704bb||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_proto=HTTP/1.1||request_raw_data=||response_time=1589852157028 7 | [INFO][2020-05-19T09:36:03.816][log.go:58] [request log]||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||traceid=c0a82b205ec33803420839b3e9d186b0||spanid=9e6b13230866cb39||request_uri=/img||request_client_ip=::1||request_time=1589852163814||request_method=GET||request_proto=HTTP/1.1||response=||cost_time=2ms||cspanid=||request_referer=||request_raw_data=||response_time=1589852163816 8 | [INFO][2020-05-19T09:37:07.573][log.go:58] [request log]||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||cspanid=||traceid=c0a82b205ec3384325a839cd104dc7b0||request_uri=/img||request_client_ip=::1||request_proto=HTTP/1.1||response_time=1589852227573||spanid=9e6b1363380704bb||request_raw_data=||response=||cost_time=5ms||request_time=1589852227568||request_method=GET||request_referer= 9 | [INFO][2020-05-19T09:37:28.689][log.go:58] [request log]||cspanid=||spanid=9e6b13780866cb39||response=||cost_time=3ms||traceid=c0a82b205ec33858cc6839cde9d186b0||request_client_ip=::1||response_time=1589852248688||request_method=GET||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=||request_raw_data=||request_uri=/img||request_time=1589852248685||request_proto=HTTP/1.1 10 | -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020051910: -------------------------------------------------------------------------------- 1 | [INFO][2020-05-19T10:31:10.937][log.go:58] [request log]||request_method=POST||request_uri=/base/sayHi||cost_time=6ms||spanid=9e6b6fce380704bb||response={\"code\":-99,\"msg\":\"[3]解密失败!\",\"data\":\"\",\"traceId\":\"c0a82b205ec344ee3a403b6a104dc7b0\"}||request_time=1589855470931||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=||request_raw_data={\"paramString\":\"1234\"}||request_client_ip=::1||cspanid=||request_proto=HTTP/1.1||response_time=1589855470937||traceid=c0a82b205ec344ee3a403b6a104dc7b0 2 | -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020051911: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fehu-asia/Archimedes/5c16214e442ad417da32b75bd2efbf76dea0f66d/logs/gin_scaffold.inf.log.2020051911 -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020051912: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fehu-asia/Archimedes/5c16214e442ad417da32b75bd2efbf76dea0f66d/logs/gin_scaffold.inf.log.2020051912 -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020051913: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fehu-asia/Archimedes/5c16214e442ad417da32b75bd2efbf76dea0f66d/logs/gin_scaffold.inf.log.2020051913 -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020051914: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fehu-asia/Archimedes/5c16214e442ad417da32b75bd2efbf76dea0f66d/logs/gin_scaffold.inf.log.2020051914 -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020051915: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fehu-asia/Archimedes/5c16214e442ad417da32b75bd2efbf76dea0f66d/logs/gin_scaffold.inf.log.2020051915 -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020051916: -------------------------------------------------------------------------------- 1 | [INFO][2020-05-19T16:01:33.021][log.go:58] [request log]||request_method=POST||request_referer=||spanid=9e6bb97d380704bb||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_client_ip=::1||response_time=1589875293021||traceid=c0a82b205ec3925df9103f5a104dc7b0||request_time=1589875293017||request_uri=/base/sayHi||request_proto=HTTP/1.1||request_raw_data={\"paramString\":\"1234\"}||cost_time=4ms||response={\"code\":-99,\"msg\":\"[3]解密失败!\",\"data\":\"\",\"traceId\":\"c0a82b205ec3925df9103f5a104dc7b0\"}||cspanid= 2 | [INFO][2020-05-19T16:01:46.910][log.go:58] [request log]||request_time=1589875306909||request_method=POST||request_proto=HTTP/1.1||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_uri=/login/login||cost_time=1ms||spanid=9e6bb94a0866cb39||request_raw_data={\"Username\":\"zhangsan\",\"Password\":\"456\"}||request_client_ip=::1||cspanid=||traceid=c0a82b205ec3926aac183f5ae9d186b0||request_referer=||response_time=1589875306910||response={\"code\":1,\"msg\":\"\",\"data\":{\"test\":1234,\"token\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk4ODYxMDYsImlhdCI6MTU4OTg3NTMwNiwidG9rZW5JZCI6ImIzMGNmNmJlLWVkMGMtNGVjOC04ZmI1LTEzMGU2YzFkZTUyNiJ9._BO3cIMgznKuiV2PPQNYB_NLZQQWl6B1hNSk7xgDlCc\"},\"traceId\":\"c0a82b205ec3926aac183f5ae9d186b0\"} 3 | [INFO][2020-05-19T16:02:43.936][log.go:58] [request log]||request_time=1589875363933||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_raw_data={\"Username\":\"zhangsan\",\"Password\":\"456\"}||response_time=1589875363936||cspanid=||request_uri=/login/login||request_proto=HTTP/1.1||request_referer=||cost_time=3ms||request_method=POST||request_client_ip=::1||response={\"code\":1,\"msg\":\"\",\"data\":{\"test\":1234,\"token\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk4ODYxNjMsImlhdCI6MTU4OTg3NTM2MywidG9rZW5JZCI6IjkwNTVkN2M4LWMyN2ItNGI3OS1hZTk1LWE4YWNkNDIzZTQ2NiJ9.I3k3b4XBnGxA4UFOZUK9BAbrG18DyB0kwAsyy0nHpW8\"},\"traceId\":\"c0a82b205ec392a3d7583f75104dc7b0\"}||traceid=c0a82b205ec392a3d7583f75104dc7b0||spanid=9e6bb983380704bb 4 | [INFO][2020-05-19T16:03:38.212][log.go:58] [request log]||request_referer=||request_raw_data={\"Username\":\"zhangsan\",\"Password\":\"456\"}||response={\"code\":1,\"msg\":\"\",\"data\":{\"test\":1234,\"token\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk4ODYyMTgsImlhdCI6MTU4OTg3NTQxOCwidG9rZW5JZCI6IjUyMmY3NGRhLTM3OWQtNDIyNi1hZTE2LWY4NWE2ZDE2ODk1NSJ9.mMIonPwba6O5V4_bzJ0rFI0_ZloW9EHSMSGYs3oDccE\"},\"traceId\":\"c0a82b205ec392dafd603f8d104dc7b0\"}||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_method=POST||request_uri=/login/login||request_proto=HTTP/1.1||cost_time=3ms||spanid=9e6bb9fa380704bb||request_time=1589875418208||cspanid=||traceid=c0a82b205ec392dafd603f8d104dc7b0||response_time=1589875418211||request_client_ip=::1 5 | [INFO][2020-05-19T16:14:09.322][log.go:58] [request log]||request_method=POST||request_uri=/login/login||request_proto=HTTP/1.1||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||request_referer=||request_client_ip=::1||response_time=1589876049322||request_raw_data={\"Username\":\"zhangsan\",\"Password\":\"456\"}||cost_time=4ms||traceid=c0a82b205ec3955117c84023104dc7b0||spanid=9e6bbe71380704bb||request_time=1589876049318||response={\"code\":1,\"msg\":\"\",\"data\":{\"test\":1234,\"token\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk4ODY4NDksImlhdCI6MTU4OTg3NjA0OSwidG9rZW5JZCI6IjBmNGRkZjhhLTlmZDMtNDQ3YS1iNzE2LWUxOGI0OTM5MDRhNiJ9.O_xGCZt-ZvHMywipWnvhP4nJe9xJbrF3kDzHG6qlBn0\"},\"traceId\":\"c0a82b205ec3955117c84023104dc7b0\"}||cspanid= 6 | [INFO][2020-05-19T16:15:05.089][log.go:58] [request log]||request_proto=HTTP/1.1||traceid=c0a82b205ec395897908402308d2acb0||spanid=9e6bbea90c697f48||request_time=1589876105088||request_method=POST||request_referer=||request_raw_data=||cost_time=0ms||request_ua=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36||cspanid=||request_uri=/login/needLogin||request_client_ip=::1||response_time=1589876105088||response={\"code\":1,\"msg\":\"\",\"data\":{\"Username\":\"zhangsan\",\"Password\":\"\",\"UserId\":\"b6be2136-811e-43e0-a4c4-b4183a2fa6be\",\"AesKey\":\"\"},\"traceId\":\"c0a82b205ec395897908402308d2acb0\"} 7 | -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020051917: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fehu-asia/Archimedes/5c16214e442ad417da32b75bd2efbf76dea0f66d/logs/gin_scaffold.inf.log.2020051917 -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020051918: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fehu-asia/Archimedes/5c16214e442ad417da32b75bd2efbf76dea0f66d/logs/gin_scaffold.inf.log.2020051918 -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020051919: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fehu-asia/Archimedes/5c16214e442ad417da32b75bd2efbf76dea0f66d/logs/gin_scaffold.inf.log.2020051919 -------------------------------------------------------------------------------- /logs/gin_scaffold.inf.log.2020052006: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fehu-asia/Archimedes/5c16214e442ad417da32b75bd2efbf76dea0f66d/logs/gin_scaffold.inf.log.2020052006 -------------------------------------------------------------------------------- /logs/gin_scaffold.wf.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fehu-asia/Archimedes/5c16214e442ad417da32b75bd2efbf76dea0f66d/logs/gin_scaffold.wf.log -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fehu/common/lib" 5 | "fehu/common/lib/redis_lib" 6 | "fehu/router" 7 | "os" 8 | "os/signal" 9 | "syscall" 10 | ) 11 | 12 | func main() { 13 | lib.InitModule("./conf/dev/", []string{"base", "mysql", "redis"}) 14 | redis_lib.InitRedis() 15 | defer lib.Destroy() 16 | router.HttpServerRun() 17 | quit := make(chan os.Signal) 18 | signal.Notify(quit, syscall.SIGKILL, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGTERM) 19 | <-quit 20 | router.HttpServerStop() 21 | } 22 | -------------------------------------------------------------------------------- /middleware/crpy.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "bytes" 5 | "fehu/common/lib" 6 | "fehu/common/lib/redis_lib" 7 | "fehu/constant" 8 | "fehu/util/cryp" 9 | "fmt" 10 | "github.com/gin-gonic/gin" 11 | "io/ioutil" 12 | "net/http" 13 | "strings" 14 | ) 15 | 16 | func CrpyBefore(c *gin.Context) { 17 | request := c.Request 18 | data, err := c.GetRawData() 19 | if err != nil { 20 | ParamVerifyError(c) 21 | c.Abort() 22 | return 23 | } 24 | if strings.HasPrefix(request.RequestURI, "/base") || 25 | strings.HasPrefix(request.RequestURI, "/login") || 26 | request.RequestURI == "/ping" { 27 | c.Set("requestData", string(data)) 28 | request.Body = ioutil.NopCloser(bytes.NewBuffer(data)) // 关键点 29 | return 30 | } 31 | // 开启了加密 32 | if lib.GetIntConf("base.http.crpyStatus") == 1 { 33 | // 签名的加密方式 = rsa(sha256(秘文),publicKey) 34 | signature := request.Header.Get(constant.RequestHeaderSignature) 35 | if signature == "" { 36 | SignatureError(c, "签名为空!") 37 | c.Abort() 38 | return 39 | } 40 | hex := cryp.GetSHA256HashCode(data) 41 | privateKey, err := redis_lib.GetString(constant.RedisRsaPrivateKey, "") 42 | if err != nil { 43 | SignatureError(c, "签名错误,没有私钥!") 44 | c.Abort() 45 | return 46 | } 47 | decrypt, err := cryp.RsaDecrypt(cryp.Base64DecodeByte(signature), privateKey) 48 | if err != nil { 49 | SignatureError(c, "签名错误!") 50 | c.Abort() 51 | return 52 | } 53 | // 私钥解密被公钥加密的签名和发送过来的签名进行对比,如果这一步出问题: 54 | // 1. 对称加密密钥是否一致 55 | // 2. 公钥匙和私钥是否能对上 56 | if hex != string(decrypt) { 57 | SignatureError(c, "参数签名不一致!") 58 | c.Abort() 59 | return 60 | } 61 | // 解包 62 | key := c.GetString(constant.CtxAesKey) 63 | if key == "" { 64 | EncryptionError(c, "no key!") 65 | c.Abort() 66 | return 67 | } 68 | data, err = cryp.AesEncryptByte(data, key) 69 | if err != nil { 70 | EncryptionError(c, "key error!") 71 | c.Abort() 72 | return 73 | } 74 | // 响应体是否加密,这个值不为空,就加密 75 | c.Set(constant.ReponseBodyCrypto, constant.ReponseBodyCrypto) 76 | 77 | } 78 | // 如果不加密,原封不动放进去,如果开启加密,把解密的数据放进去 79 | c.Set("requestData", string(data)) 80 | request.Body = ioutil.NopCloser(bytes.NewBuffer(data)) // 关键点 81 | } 82 | func after(c *gin.Context) { 83 | 84 | fmt.Println("加密后") 85 | } 86 | func Crpy() gin.HandlerFunc { 87 | return func(c *gin.Context) { 88 | // 不是post请求 89 | requestUri := c.Request.RequestURI 90 | fmt.Println(requestUri) 91 | if c.Request.Method == http.MethodPost { 92 | CrpyBefore(c) 93 | } 94 | c.Next() 95 | after(c) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /middleware/ip_auth.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "errors" 5 | "fehu/common/lib" 6 | "fmt" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | func IPAuthMiddleware() gin.HandlerFunc { 11 | return func(c *gin.Context) { 12 | isMatched := false 13 | for _, host := range lib.GetStringSliceConf("base.http.allow_ip") { 14 | if c.ClientIP() == host { 15 | isMatched = true 16 | } 17 | } 18 | if !isMatched { 19 | ResponseError(c, InternalErrorCode, errors.New(fmt.Sprintf("%v, not in iplist", c.ClientIP()))) 20 | c.Abort() 21 | return 22 | } 23 | c.Next() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /middleware/jwt_auth.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "errors" 5 | "fehu/common/lib/redis_lib" 6 | "fehu/constant" 7 | "fehu/model/form" 8 | jwt2 "fehu/util/jwt" 9 | "fmt" 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | func JwtAuthMiddleware() gin.HandlerFunc { 14 | return func(c *gin.Context) { 15 | value, exists := c.Get(constant.CtxUser) 16 | userForm := value.(*form.UserForm) 17 | // 如果ctx不存在,从redis中获取 18 | if exists == false || userForm == nil { 19 | strToken := c.Request.Header.Get(constant.RequestLoginToken) 20 | claims, e := jwt2.VerifyAction(strToken) 21 | if e != nil { 22 | ResponseError(c, NoLoginErrorCode, errors.New(fmt.Sprintf("您需要登录!%s", e.Error()))) 23 | c.Abort() 24 | return 25 | } else { 26 | tokenId := claims.TokenId 27 | userForm := &form.UserForm{} 28 | e := redis_lib.GetObject(tokenId, userForm, "") 29 | if e != nil { 30 | ResponseError(c, NoLoginErrorCode, errors.New(fmt.Sprintf("会话超时,请重新登录!"))) 31 | c.Abort() 32 | return 33 | } 34 | fmt.Println("获取到当前用户:", userForm, e) 35 | c.Set(constant.CtxUser, userForm) 36 | // 当用户访问过以后,更新token 37 | defer redis_lib.Set(claims.TokenId, userForm, constant.UserLoginToKenExpireTime, "") 38 | } 39 | 40 | } else { 41 | if userForm.UserId == "" { 42 | ResponseError(c, NoLoginErrorCode, errors.New(fmt.Sprintf("您需要登录!"))) 43 | c.Abort() 44 | return 45 | } 46 | } 47 | c.Next() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /middleware/limiter.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "errors" 5 | "fehu/common/lib" 6 | "fmt" 7 | "github.com/gin-gonic/gin" 8 | "github.com/juju/ratelimit" 9 | "log" 10 | "time" 11 | ) 12 | 13 | var ( 14 | bucket *ratelimit.Bucket 15 | ) 16 | 17 | //func init() { 18 | // conf := lib.GetIntConf("base.http.limitRequestCountPerSecond") 19 | // log.Printf(" [INFO] HttpServerRun:%s\n", lib.GetIntConf("base.http.limitRequestCountPerSecond")) 20 | // bucket = ratelimit.NewBucket(time.Duration(int(time.Second)/conf), int64(conf)) 21 | //} 22 | 23 | func Limiter() gin.HandlerFunc { 24 | conf := lib.GetIntConf("base.http.limitRequestCountPerSecond") 25 | log.Printf(" [INFO] limiterCount:%d\n", lib.GetIntConf("base.http.limitRequestCountPerSecond")) 26 | bucket = ratelimit.NewBucket(time.Duration(int(time.Second)/conf), int64(conf)) 27 | 28 | return func(c *gin.Context) { 29 | fmt.Println("tackAvailable", bucket.Available()) 30 | if bucket.TakeAvailable(1) == 0 { 31 | ResponseError(c, LimiterErrorCode, errors.New(fmt.Sprintf("%v, limiter error !", c.ClientIP()))) 32 | c.Abort() 33 | return 34 | } 35 | c.Next() 36 | } 37 | } 38 | 39 | //func LimitHandler(lmt *limiter.Limiter) gin.HandlerFunc { 40 | // 41 | // return func(c *gin.Context) { 42 | // 43 | // httpError := tollbooth.LimitByRequest(lmt, c.Writer, c.Request) 44 | // 45 | // if httpError != nil { 46 | // 47 | // c.Data(httpError.StatusCode, lmt.GetMessageContentType(), []byte(httpError.Message)) 48 | // 49 | // c.Abort() 50 | // 51 | // } else { 52 | // 53 | // c.Next() 54 | // 55 | // } 56 | // 57 | // } 58 | // 59 | //} 60 | -------------------------------------------------------------------------------- /middleware/logger.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | "time" 7 | ) 8 | 9 | func CustomLogger(param gin.LogFormatterParams) string { 10 | // your custom format 11 | return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n", 12 | param.ClientIP, 13 | param.TimeStamp.Format(time.RFC1123), 14 | param.Method, 15 | param.Path, 16 | param.Request.Proto, 17 | param.StatusCode, 18 | param.Latency, 19 | param.Request.UserAgent(), 20 | param.ErrorMessage, 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /middleware/recovery.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "errors" 5 | "fehu/common/lib" 6 | "fehu/public" 7 | "fmt" 8 | "github.com/gin-gonic/gin" 9 | "runtime/debug" 10 | ) 11 | 12 | // RecoveryMiddleware捕获所有panic,并且返回错误信息 13 | func RecoveryMiddleware() gin.HandlerFunc { 14 | return func(c *gin.Context) { 15 | defer func() { 16 | if err := recover(); err != nil { 17 | //先做一下日志记录 18 | fmt.Println(string(debug.Stack())) 19 | public.ComLogNotice(c, "_com_panic", map[string]interface{}{ 20 | "error": fmt.Sprint(err), 21 | "stack": string(debug.Stack()), 22 | }) 23 | 24 | if lib.ConfBase.DebugMode != "debug" { 25 | ResponseError(c, 500, errors.New("内部错误")) 26 | return 27 | } else { 28 | ResponseError(c, 500, errors.New(fmt.Sprint(err))) 29 | return 30 | } 31 | } 32 | }() 33 | c.Next() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /middleware/refresh_token.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "errors" 5 | "fehu/common/lib/redis_lib" 6 | "fehu/constant" 7 | "fehu/model/form" 8 | jwt2 "fehu/util/jwt" 9 | "fmt" 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | func RefreshToken() gin.HandlerFunc { 14 | return func(c *gin.Context) { 15 | // 拦截所有请求函数,只要前端有token就去尝试读取并更新token 16 | strToken := c.Request.Header.Get(constant.RequestLoginToken) 17 | if strToken == "" { 18 | c.Next() 19 | return 20 | } 21 | claims, e := jwt2.VerifyAction(strToken) 22 | if e != nil { 23 | ResponseError(c, NoSessionErrorCode, errors.New(fmt.Sprintf("no session!%s", e.Error()))) 24 | c.Abort() 25 | return 26 | } else { 27 | tokenId := claims.TokenId 28 | userForm := &form.UserForm{} 29 | e := redis_lib.GetObject(tokenId, userForm, "") 30 | if e != nil { 31 | ResponseError(c, NoSessionErrorCode, errors.New(fmt.Sprintf("会话超时,请重新发起会话!"))) 32 | c.Abort() 33 | return 34 | } 35 | c.Set(constant.CtxUser, userForm) 36 | c.Set(constant.CtxAesKey, tokenId) 37 | // 当用户访问过以后,更新token 38 | defer redis_lib.Set(claims.TokenId, userForm, constant.UserLoginToKenExpireTime, "") 39 | } 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /middleware/request_log.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | // 4 | //import ( 5 | // "bytes" 6 | // "fehu/common/lib" 7 | // "fehu/public" 8 | // "github.com/gin-gonic/gin" 9 | // "io/ioutil" 10 | // "time" 11 | //) 12 | // 13 | //// 请求进入日志 14 | //func RequestInLog(c *gin.Context) { 15 | // traceContext := lib.NewTrace() 16 | // if traceId := c.Request.Header.Get("com-header-rid"); traceId != "" { 17 | // traceContext.TraceId = traceId 18 | // } 19 | // if spanId := c.Request.Header.Get("com-header-spanid"); spanId != "" { 20 | // traceContext.SpanId = spanId 21 | // } 22 | // 23 | // c.Set("startExecTime", time.Now()) 24 | // c.Set("trace", traceContext) 25 | // 26 | // bodyBytes, _ := ioutil.ReadAll(c.Request.Body) 27 | // c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) // Write body back 28 | // 29 | // lib.Log.TagInfo(traceContext, "_com_request_in", map[string]interface{}{ 30 | // "uri": c.Request.RequestURI, 31 | // "method": c.Request.Method, 32 | // "args": c.Request.PostForm, 33 | // "body": string(bodyBytes), 34 | // "from": c.ClientIP(), 35 | // }) 36 | //} 37 | // 38 | //// 请求输出日志 39 | //func RequestOutLog(c *gin.Context) { 40 | // // after request 41 | // endExecTime := time.Now() 42 | // response, _ := c.Get("response") 43 | // st, _ := c.Get("startExecTime") 44 | // 45 | // startExecTime, _ := st.(time.Time) 46 | // public.ComLogNotice(c, "_com_request_out", map[string]interface{}{ 47 | // "uri": c.Request.RequestURI, 48 | // "method": c.Request.Method, 49 | // "args": c.Request.PostForm, 50 | // "from": c.ClientIP(), 51 | // "response": response, 52 | // "proc_time": endExecTime.Sub(startExecTime).Seconds(), 53 | // }) 54 | //} 55 | // 56 | //func RequestLog() gin.HandlerFunc { 57 | // return func(c *gin.Context) { 58 | // RequestInLog(c) 59 | // defer RequestOutLog(c) 60 | // c.Next() 61 | // } 62 | //} 63 | -------------------------------------------------------------------------------- /middleware/requestlog.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "bytes" 5 | "fehu/common/lib" 6 | "fmt" 7 | "github.com/gin-gonic/gin" 8 | "github.com/xinliangnote/go-util/time" 9 | "log" 10 | ) 11 | 12 | type bodyLogWriter struct { 13 | gin.ResponseWriter 14 | body *bytes.Buffer 15 | } 16 | 17 | /** 18 | 写往通道中的日志 19 | */ 20 | type ChanLog struct { 21 | log map[string]interface{} 22 | c *gin.Context 23 | t *lib.TraceContext 24 | } 25 | 26 | var accessChannel = make(chan *ChanLog, 100) 27 | 28 | //func RequestLog() gin.HandlerFunc { 29 | // 30 | // return func(c *gin.Context) { 31 | // SetUp(c) 32 | // //defer RequestOutLog(c) 33 | // //c.Next() 34 | // } 35 | //} 36 | 37 | func RequestLog() gin.HandlerFunc { 38 | go handleAccessChannel() 39 | 40 | return func(c *gin.Context) { 41 | traceContext := lib.NewTrace() 42 | if traceId := c.Request.Header.Get("com-header-rid"); traceId != "" { 43 | traceContext.TraceId = traceId 44 | } 45 | if spanId := c.Request.Header.Get("com-header-spanid"); spanId != "" { 46 | traceContext.SpanId = spanId 47 | } 48 | c.Set("trace", traceContext) 49 | bodyLogWriter := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer} 50 | c.Writer = bodyLogWriter 51 | 52 | // 开始时间 53 | startTime := time.GetCurrentMilliUnix() 54 | 55 | // 处理请求 56 | c.Next() 57 | 58 | responseBody, _ := c.Get("response") 59 | 60 | // 结束时间 61 | endTime := time.GetCurrentMilliUnix() 62 | 63 | // 日志格式 64 | accessLogMap := make(map[string]interface{}) 65 | 66 | accessLogMap["request_time"] = startTime 67 | accessLogMap["request_method"] = c.Request.Method 68 | accessLogMap["request_uri"] = c.Request.RequestURI 69 | accessLogMap["request_proto"] = c.Request.Proto 70 | accessLogMap["request_ua"] = c.Request.UserAgent() 71 | accessLogMap["request_referer"] = c.Request.Referer() 72 | data, _ := c.Get("requestData") 73 | accessLogMap["request_raw_data"] = data 74 | accessLogMap["request_client_ip"] = c.ClientIP() 75 | 76 | accessLogMap["response_time"] = endTime 77 | accessLogMap["response"] = responseBody 78 | 79 | accessLogMap["cost_time"] = fmt.Sprintf("%vms", endTime-startTime) 80 | 81 | //accessLogJson, _ := jsonUtil.Encode(accessLogMap) 82 | accessChannel <- &ChanLog{log: accessLogMap, c: c, t: traceContext} 83 | } 84 | } 85 | 86 | func handleAccessChannel() { 87 | //if f, err := os.OpenFile(config.AppAccessLogName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666); err != nil { 88 | // log.Println(err) 89 | //} else { 90 | // for accessLog := range accessChannel { 91 | // _, _ = f.WriteString(accessLog + "\n") 92 | // } 93 | //} 94 | //return 95 | for chanLog := range accessChannel { 96 | log.Println(chanLog.log) 97 | lib.Log.TagInfo(chanLog.t, "[request log]", chanLog.log) 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /middleware/response.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "encoding/json" 5 | "fehu/common/lib" 6 | "fehu/constant" 7 | "fehu/util/cryp" 8 | "fmt" 9 | "github.com/gin-gonic/gin" 10 | "strings" 11 | ) 12 | 13 | type ResponseCode int 14 | 15 | //1000以下为通用码,1000以上为用户自定义码 16 | const ( 17 | ErrorCode ResponseCode = iota 18 | SuccessCode 19 | UndefErrorCode 20 | ValidErrorCode 21 | InternalErrorCode 22 | 23 | NoLoginErrorCode ResponseCode = -10 24 | NoSessionErrorCode ResponseCode = -20 25 | SignatureErrorCode ResponseCode = -88 26 | EncryptionErrorCode ResponseCode = -99 27 | LimiterErrorCode ResponseCode = 999 28 | ParamVerifyErrorCode ResponseCode = 190 29 | ParamRequireErrorCode ResponseCode = 199 30 | InvalidRequestErrorCode ResponseCode = 401 31 | CustomizeCode ResponseCode = 1000 32 | 33 | GROUPALL_SAVE_FLOWERROR ResponseCode = 2001 34 | ) 35 | 36 | type Response struct { 37 | ErrorCode ResponseCode `json:"code"` 38 | ErrorMsg string `json:"msg"` 39 | Data interface{} `json:"data"` 40 | TraceId interface{} `json:"traceId"` 41 | Stack interface{} `json:"stack,omitempty"` 42 | } 43 | 44 | func SignatureError(c *gin.Context, msg ...string) { 45 | finalMsg := "签名错误 !" 46 | if len(msg) > 0 { 47 | finalMsg = msg[0] 48 | } 49 | Error(c, SignatureErrorCode, finalMsg) 50 | } 51 | func EncryptionError(c *gin.Context, msg ...string) { 52 | finalMsg := "encryption err !" 53 | if len(msg) > 0 { 54 | finalMsg = msg[0] 55 | } 56 | Error(c, EncryptionErrorCode, finalMsg) 57 | } 58 | func ParamRequireError(c *gin.Context, msg ...string) { 59 | finalMsg := "param required !" 60 | if len(msg) > 0 { 61 | finalMsg = msg[0] 62 | } 63 | Error(c, ParamRequireErrorCode, finalMsg) 64 | } 65 | func ParamVerifyError(c *gin.Context, msg ...string) { 66 | finalMsg := "param verify err !" 67 | if len(msg) > 0 { 68 | finalMsg = msg[0] 69 | } 70 | Error(c, ParamRequireErrorCode, finalMsg) 71 | } 72 | func ErrorMsg(c *gin.Context, msg string) { 73 | Error(c, ErrorCode, msg) 74 | } 75 | 76 | func Error(c *gin.Context, code ResponseCode, msg string) { 77 | trace, _ := c.Get("trace") 78 | traceContext, _ := trace.(*lib.TraceContext) 79 | traceId := "" 80 | if traceContext != nil { 81 | traceId = traceContext.TraceId 82 | } 83 | resp := &Response{ErrorCode: code, ErrorMsg: msg, Data: "", TraceId: traceId} 84 | 85 | SerializeJSON(c, resp, 200, nil) 86 | } 87 | 88 | func Success(c *gin.Context, datas ...interface{}) { 89 | var data interface{} 90 | if len(datas) > 0 { 91 | data = datas[0] 92 | } 93 | trace, _ := c.Get("trace") 94 | traceContext, _ := trace.(*lib.TraceContext) 95 | traceId := "" 96 | if traceContext != nil { 97 | traceId = traceContext.TraceId 98 | } 99 | resp := &Response{ErrorCode: SuccessCode, ErrorMsg: "", Data: data, TraceId: traceId} 100 | 101 | SerializeJSON(c, resp, 200, nil) 102 | } 103 | 104 | func ResponseError(c *gin.Context, code ResponseCode, err error) { 105 | trace, _ := c.Get("trace") 106 | traceContext, _ := trace.(*lib.TraceContext) 107 | traceId := "" 108 | if traceContext != nil { 109 | traceId = traceContext.TraceId 110 | } 111 | stack := "" 112 | if c.Query("is_debug") == "1" || lib.GetConfEnv() == "dev" { 113 | stack = strings.Replace(fmt.Sprintf("%+v", err), err.Error()+"\n", "", -1) 114 | } 115 | 116 | resp := &Response{ErrorCode: code, ErrorMsg: err.Error(), Data: "", TraceId: traceId, Stack: stack} 117 | SerializeJSON(c, resp, 200, err) 118 | } 119 | func SerializeJSON(c *gin.Context, res *Response, httpCode int, err error) { 120 | response, _ := json.Marshal(res) 121 | resJson := string(response) 122 | c.Set("response", resJson) 123 | 124 | if c.GetString(constant.ReponseBodyCrypto) != "" { 125 | key := c.GetString(constant.CtxAesKey) 126 | if key != "" { 127 | encrypt, _ := cryp.AesEncrypt(resJson, key) 128 | c.JSON(httpCode, encrypt) 129 | } else { 130 | c.JSON(httpCode, "0") 131 | } 132 | } else { 133 | c.JSON(httpCode, res) 134 | } 135 | 136 | if err != nil { 137 | c.AbortWithError(httpCode, err) 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /middleware/session_auth.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "errors" 5 | "github.com/gin-gonic/contrib/sessions" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func SessionAuthMiddleware() gin.HandlerFunc { 10 | return func(c *gin.Context) { 11 | session := sessions.Default(c) 12 | if name, ok := session.Get("user").(string); !ok || name == "" { 13 | ResponseError(c, InternalErrorCode, errors.New("user not login")) 14 | c.Abort() 15 | return 16 | } 17 | c.Next() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /middleware/translation.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "fehu/public" 5 | "github.com/gin-gonic/gin" 6 | "github.com/go-playground/locales/en" 7 | "github.com/go-playground/locales/zh" 8 | "github.com/go-playground/universal-translator" 9 | "gopkg.in/go-playground/validator.v9" 10 | en_translations "gopkg.in/go-playground/validator.v9/translations/en" 11 | zh_translations "gopkg.in/go-playground/validator.v9/translations/zh" 12 | "reflect" 13 | ) 14 | 15 | //设置Translation 16 | func TranslationMiddleware() gin.HandlerFunc { 17 | return func(c *gin.Context) { 18 | //参照:https://github.com/go-playground/validator/blob/v9/_examples/translations/main.go 19 | 20 | //设置支持语言 21 | en := en.New() 22 | zh := zh.New() 23 | 24 | //设置国际化翻译器 25 | uni := ut.New(zh, zh, en) 26 | val := validator.New() 27 | 28 | //根据参数取翻译器实例 29 | locale := c.DefaultQuery("locale", "zh") 30 | trans, _ := uni.GetTranslator(locale) 31 | 32 | //翻译器注册到validator 33 | switch locale { 34 | case "en": 35 | en_translations.RegisterDefaultTranslations(val, trans) 36 | val.RegisterTagNameFunc(func(fld reflect.StructField) string { 37 | return fld.Tag.Get("en_comment") 38 | }) 39 | break 40 | default: 41 | zh_translations.RegisterDefaultTranslations(val, trans) 42 | val.RegisterTagNameFunc(func(fld reflect.StructField) string { 43 | return fld.Tag.Get("comment") 44 | }) 45 | 46 | //自定义验证方法 47 | //https://github.com/go-playground/validator/blob/v9/_examples/custom-validation/main.go 48 | val.RegisterValidation("is-validuser", func(fl validator.FieldLevel) bool { 49 | return fl.Field().String() == "admin" 50 | }) 51 | 52 | //自定义验证器 53 | //https://github.com/go-playground/validator/blob/v9/_examples/translations/main.go 54 | val.RegisterTranslation("is-validuser", trans, func(ut ut.Translator) error { 55 | return ut.Add("is-validuser", "{0} 填写不正确哦", true) 56 | }, func(ut ut.Translator, fe validator.FieldError) string { 57 | t, _ := ut.T("is-validuser", fe.Field()) 58 | return t 59 | }) 60 | break 61 | } 62 | c.Set(public.TranslatorKey, trans) 63 | c.Set(public.ValidatorKey, val) 64 | c.Next() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /model/demo.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "fehu/public" 5 | "github.com/e421083458/gorm" 6 | "github.com/gin-gonic/gin" 7 | "time" 8 | ) 9 | 10 | type Area struct { 11 | Id int `json:"id" gorm:"primary_key" description:"自增主键"` 12 | AreaName string `json:"area_name" gorm:"column:area_name" description:"区域名称"` 13 | CityId int `json:"city_id" gorm:"column:city_id" description:"城市id"` 14 | UserId int64 `json:"user_id" gorm:"column:user_id" description:"操作人"` 15 | UpdatedAt time.Time `json:"update_at" gorm:"column:update_at" description:"更新时间"` 16 | CreatedAt time.Time `json:"create_at" gorm:"column:create_at" description:"创建时间"` 17 | } 18 | 19 | func (t *Area) TableName() string { 20 | return "area" 21 | } 22 | 23 | func (t *Area) Find(c *gin.Context, tx *gorm.DB, id string) (*Area, error) { 24 | area := &Area{} 25 | err := tx.SetCtx(public.GetGinTraceContext(c)).Where("id = ?", id).Find(area).Error 26 | if err != nil { 27 | return nil, err 28 | } 29 | return area, nil 30 | } 31 | -------------------------------------------------------------------------------- /model/form/User.go: -------------------------------------------------------------------------------- 1 | package form 2 | 3 | type UserForm struct { 4 | Username string 5 | Password string 6 | UserId string 7 | AesKey string 8 | } 9 | -------------------------------------------------------------------------------- /model/param/base_param.go: -------------------------------------------------------------------------------- 1 | package param 2 | 3 | /** 4 | 基础参数 5 | */ 6 | type BaseParam struct { 7 | Version string // 版本号 8 | DevNo string // 设备号 9 | } 10 | -------------------------------------------------------------------------------- /model/param/login_param.go: -------------------------------------------------------------------------------- 1 | package param 2 | 3 | type LoginParam struct { 4 | BaseParam 5 | Username string 6 | Password string 7 | } 8 | -------------------------------------------------------------------------------- /public/const.go: -------------------------------------------------------------------------------- 1 | package public 2 | 3 | const ( 4 | ValidatorKey = "ValidatorKey" 5 | TranslatorKey = "TranslatorKey" 6 | ) 7 | -------------------------------------------------------------------------------- /public/log.go: -------------------------------------------------------------------------------- 1 | package public 2 | 3 | import ( 4 | "context" 5 | "fehu/common/lib" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | //错误日志 10 | func ContextWarning(c context.Context, dltag string, m map[string]interface{}) { 11 | v := c.Value("trace") 12 | traceContext, ok := v.(*lib.TraceContext) 13 | if !ok { 14 | traceContext = lib.NewTrace() 15 | } 16 | lib.Log.TagWarn(traceContext, dltag, m) 17 | } 18 | 19 | //错误日志 20 | func ContextError(c context.Context, dltag string, m map[string]interface{}) { 21 | v := c.Value("trace") 22 | traceContext, ok := v.(*lib.TraceContext) 23 | if !ok { 24 | traceContext = lib.NewTrace() 25 | } 26 | lib.Log.TagError(traceContext, dltag, m) 27 | } 28 | 29 | //普通日志 30 | func ContextNotice(c context.Context, dltag string, m map[string]interface{}) { 31 | v := c.Value("trace") 32 | traceContext, ok := v.(*lib.TraceContext) 33 | if !ok { 34 | traceContext = lib.NewTrace() 35 | } 36 | lib.Log.TagInfo(traceContext, dltag, m) 37 | } 38 | 39 | //错误日志 40 | func ComLogWarning(c *gin.Context, dltag string, m map[string]interface{}) { 41 | traceContext := GetGinTraceContext(c) 42 | lib.Log.TagError(traceContext, dltag, m) 43 | } 44 | 45 | //普通日志 46 | func ComLogNotice(c *gin.Context, dltag string, m map[string]interface{}) { 47 | traceContext := GetGinTraceContext(c) 48 | lib.Log.TagInfo(traceContext, dltag, m) 49 | } 50 | 51 | // 从gin的Context中获取数据 52 | func GetGinTraceContext(c *gin.Context) *lib.TraceContext { 53 | // 防御 54 | if c == nil { 55 | return lib.NewTrace() 56 | } 57 | traceContext, exists := c.Get("trace") 58 | if exists { 59 | if tc, ok := traceContext.(*lib.TraceContext); ok { 60 | return tc 61 | } 62 | } 63 | return lib.NewTrace() 64 | } 65 | 66 | // 从Context中获取数据 67 | func GetTraceContext(c context.Context) *lib.TraceContext { 68 | if c == nil { 69 | return lib.NewTrace() 70 | } 71 | traceContext := c.Value("trace") 72 | if tc, ok := traceContext.(*lib.TraceContext); ok { 73 | return tc 74 | } 75 | return lib.NewTrace() 76 | } 77 | -------------------------------------------------------------------------------- /public/params.go: -------------------------------------------------------------------------------- 1 | package public 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/go-playground/universal-translator" 6 | "github.com/pkg/errors" 7 | "gopkg.in/go-playground/validator.v9" 8 | "strings" 9 | ) 10 | 11 | func DefaultGetValidParams(c *gin.Context, params interface{}) error { 12 | if err := c.ShouldBind(params); err != nil { 13 | return err 14 | } 15 | //获取验证器 16 | valid, err := GetValidator(c) 17 | if err != nil { 18 | return err 19 | } 20 | //获取翻译器 21 | trans, err := GetTranslation(c) 22 | if err != nil { 23 | return err 24 | } 25 | err = valid.Struct(params) 26 | if err != nil { 27 | errs := err.(validator.ValidationErrors) 28 | sliceErrs := []string{} 29 | for _, e := range errs { 30 | sliceErrs = append(sliceErrs, e.Translate(trans)) 31 | } 32 | return errors.New(strings.Join(sliceErrs, ",")) 33 | } 34 | return nil 35 | } 36 | 37 | func GetValidator(c *gin.Context) (*validator.Validate, error) { 38 | val, ok := c.Get(ValidatorKey) 39 | if !ok { 40 | return nil, errors.New("未设置验证器") 41 | } 42 | validator, ok := val.(*validator.Validate) 43 | if !ok { 44 | return nil, errors.New("获取验证器失败") 45 | } 46 | return validator, nil 47 | } 48 | 49 | func GetTranslation(c *gin.Context) (ut.Translator, error) { 50 | trans, ok := c.Get(TranslatorKey) 51 | if !ok { 52 | return nil, errors.New("未设置翻译器") 53 | } 54 | translator, ok := trans.(ut.Translator) 55 | if !ok { 56 | return nil, errors.New("获取翻译器失败") 57 | } 58 | return translator, nil 59 | } 60 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Archimedes 2 | ## go 企业级开发手脚架 3 | 4 | gin构建企业级脚手架,代码简洁易读,可快速进行高效web开发。 5 | 主要功能有: 6 | 1. 请求链路日志打印,涵盖mysql/redis/request 7 | 2. 支持多语言错误信息提示及自定义错误提示。 8 | 3. 支持了多配置环境 9 | 4. 封装了 log/redis/mysql/http.client 常用方法 10 | 5. 请求响应加解密 11 | 6. IP限制 12 | 7. jwt认证 13 | 8. 限流 14 | 9. token自动刷新 15 | 10. 参数校验 16 | 11. 微服务(待完成) 17 | 11. 容器化部署(待完成) 18 | 19 | 项目地址:https://github.com/fehu-asia/Archimedes 20 | ### 现在开始 21 | - 安装软件依赖 22 | 23 | ``` 24 | git clone git@github.com:fehu-asia/Archimedes.git 25 | cd Archimedes 26 | go mod tidy 27 | ``` 28 | - 确保正确配置了 conf/mysql_map.toml、conf/redis_map.toml: 29 | 30 | - 运行脚本 31 | 32 | ``` 33 | go run main.go 34 | ``` 35 | - 测试mysql与请求链路 36 | 37 | 创建测试表: 38 | ``` 39 | CREATE TABLE `area` ( 40 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 41 | `area_name` varchar(255) NOT NULL, 42 | `city_id` int(11) NOT NULL, 43 | `user_id` int(11) NOT NULL, 44 | `update_at` datetime NOT NULL, 45 | `create_at` datetime NOT NULL, 46 | `delete_at` datetime NOT NULL, 47 | PRIMARY KEY (`id`) 48 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='area'; 49 | INSERT INTO `area` (`id`, `area_name`, `city_id`, `user_id`, `update_at`, `create_at`, `delete_at`) VALUES (NULL, 'area_name', '1', '2', '2019-06-15 00:00:00', '2019-06-15 00:00:00', '2019-06-15 00:00:00'); 50 | ``` 51 | 52 | ``` 53 | curl 'http://127.0.0.1:8880/base/dao?id=1' 54 | { 55 | "errno": 0, 56 | "errmsg": "", 57 | "data": "[{\"id\":1,\"area_name\":\"area_name\",\"city_id\":1,\"user_id\":2,\"update_at\":\"2019-06-15T00:00:00+08:00\",\"create_at\":\"2019-06-15T00:00:00+08:00\",\"delete_at\":\"2019-06-15T00:00:00+08:00\"}]", 58 | "trace_id": "c0a8fe445d05b9eeee780f9f5a8581b0" 59 | } 60 | 61 | 查看链路日志(确认是不是一次请求查询,都带有相同trace_id): 62 | tail -f gin_scaffold.inf.log 63 | 64 | ``` 65 | - 测试参数绑定与多语言验证(目前不可与加解密共用) 66 | 67 | ``` 68 | curl 'http://127.0.0.1:8880/demo/bind?name=name&locale=zh' 69 | { 70 | "errno": 500, 71 | "errmsg": "年龄为必填字段,密码为必填字段", 72 | "data": "", 73 | "trace_id": "c0a8fe445d05badae8c00f9fb62158b0" 74 | } 75 | 76 | curl 'http://127.0.0.1:8880/demo/bind?name=name&locale=en' 77 | { 78 | "errno": 500, 79 | "errmsg": "Age is a required field,Passwd is a required field", 80 | "data": "", 81 | "trace_id": "c0a8fe445d05bb4cd3b00f9f3a768bb0" 82 | } 83 | ``` 84 | 85 | ### 文件分层 86 | ``` 87 | ├── README.md 88 | ├── conf 配置文件夹 89 | ├── controller 控制器 90 | │ └── demo.go 91 | ├── constant 常量 92 | ├── model 输入输出结构层 93 | │ └── form 输出 94 | │ └── param.go 请求参数 95 | ├── go.mod 96 | ├── go.sum 97 | ├── main.go 入口文件 98 | ├── middleware 中间件层 99 | ├── public 公共文件 100 | └── router 路由层 101 | │ ├── httpserver.go 102 | │ └── route.go 103 | ├── util 工具包 104 | ``` 105 | 106 | -------------------------------------------------------------------------------- /router/httpserver.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "context" 5 | "fehu/common/lib" 6 | "github.com/gin-gonic/gin" 7 | "log" 8 | "net/http" 9 | "time" 10 | ) 11 | 12 | var ( 13 | HttpSrvHandler *http.Server 14 | ) 15 | 16 | func HttpServerRun() { 17 | gin.SetMode(lib.ConfBase.DebugMode) 18 | r := InitRouter() 19 | HttpSrvHandler = &http.Server{ 20 | Addr: lib.GetStringConf("base.http.addr"), 21 | Handler: r, 22 | ReadTimeout: time.Duration(lib.GetIntConf("base.http.read_timeout")) * time.Second, 23 | WriteTimeout: time.Duration(lib.GetIntConf("base.http.write_timeout")) * time.Second, 24 | MaxHeaderBytes: 1 << uint(lib.GetIntConf("base.http.max_header_bytes")), 25 | } 26 | go func() { 27 | log.Printf(" [INFO] HttpServerRun:%s\n", lib.GetStringConf("base.http.addr")) 28 | if err := HttpSrvHandler.ListenAndServe(); err != nil { 29 | log.Fatalf(" [ERROR] HttpServerRun:%s err:%v\n", lib.GetStringConf("base.http.addr"), err) 30 | } 31 | }() 32 | } 33 | 34 | func HttpServerStop() { 35 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 36 | defer cancel() 37 | if err := HttpSrvHandler.Shutdown(ctx); err != nil { 38 | log.Fatalf(" [ERROR] HttpServerStop err:%v\n", err) 39 | } 40 | log.Printf(" [INFO] HttpServerStop stopped\n") 41 | } 42 | -------------------------------------------------------------------------------- /router/route.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "fehu/common/lib" 5 | "fehu/controller" 6 | "fehu/controller/base" 7 | "fehu/middleware" 8 | "github.com/gin-gonic/gin" 9 | "github.com/swaggo/files" 10 | "github.com/swaggo/gin-swagger" 11 | "github.com/swaggo/swag/example/basic/docs" 12 | ) 13 | 14 | // @title Swagger Example API 15 | // @version 1.0 16 | // @description This is a sample server celler server. 17 | // @termsOfService http://swagger.io/terms/ 18 | 19 | // @contact.name API Support 20 | // @contact.url http://www.swagger.io/support 21 | // @contact.email support@swagger.io 22 | 23 | // @license.name Apache 2.0 24 | // @license.url http://www.apache.org/licenses/LICENSE-2.0.html 25 | 26 | // @host localhost:8080 27 | // @BasePath /api/v1 28 | // @query.collection.format multi 29 | 30 | // @securityDefinitions.basic BasicAuth 31 | 32 | // @securityDefinitions.apikey ApiKeyAuth 33 | // @in header 34 | // @name Authorization 35 | 36 | // @securitydefinitions.oauth2.application OAuth2Application 37 | // @tokenUrl https://example.com/oauth/token 38 | // @scope.write Grants write access 39 | // @scope.admin Grants read and write access to administrative information 40 | 41 | // @securitydefinitions.oauth2.implicit OAuth2Implicit 42 | // @authorizationurl https://example.com/oauth/authorize 43 | // @scope.write Grants write access 44 | // @scope.admin Grants read and write access to administrative information 45 | 46 | // @securitydefinitions.oauth2.password OAuth2Password 47 | // @tokenUrl https://example.com/oauth/token 48 | // @scope.read Grants read access 49 | // @scope.write Grants write access 50 | // @scope.admin Grants read and write access to administrative information 51 | 52 | // @securitydefinitions.oauth2.accessCode OAuth2AccessCode 53 | // @tokenUrl https://example.com/oauth/token 54 | // @authorizationurl https://example.com/oauth/authorize 55 | // @scope.admin Grants read and write access to administrative information 56 | 57 | // @x-extension-openapi {"example": "value on a json format"} 58 | 59 | func InitRouter(middlewares ...gin.HandlerFunc) *gin.Engine { 60 | //programatically set swagger info 61 | docs.SwaggerInfo.Title = lib.GetStringConf("base.swagger.title") 62 | docs.SwaggerInfo.Description = lib.GetStringConf("base.swagger.desc") 63 | docs.SwaggerInfo.Version = "1.0" 64 | docs.SwaggerInfo.Host = lib.GetStringConf("base.swagger.host") 65 | docs.SwaggerInfo.BasePath = lib.GetStringConf("base.swagger.base_path") 66 | docs.SwaggerInfo.Schemes = []string{"http", "https"} 67 | 68 | router := gin.Default() 69 | // LoggerWithFormatter middleware will write the logs to gin.DefaultWriter 70 | // By default gin.DefaultWriter = os.Stdout 71 | //router.Use(gin.LoggerWithFormatter(middleware.CustomLogger)) 72 | //gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) { 73 | // log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers) 74 | //} 75 | 76 | // 错误中间件 77 | router.Use(middleware.RecoveryMiddleware()) 78 | // 如果开启了限流,使用限流中间件 79 | if lib.GetIntConf("base.http.limitStatus") == 1 { 80 | router.Use(middleware.Limiter()) 81 | } 82 | // 刷新token 83 | router.Use(middleware.RefreshToken()) 84 | // 加解密中间件 85 | router.Use(middleware.Crpy()) 86 | // 请求日志 87 | router.Use(middleware.RequestLog()) 88 | // IP限制 89 | router.Use(middleware.IPAuthMiddleware()) 90 | router.Use(middlewares...) 91 | //router.Use(func(context *gin.Context) { 92 | //}) 93 | 94 | router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) 95 | router.GET("/ping", func(c *gin.Context) { 96 | c.JSON(200, gin.H{ 97 | "message": "pong", 98 | }) 99 | }) 100 | //base 不需要加密的接口 101 | v1 := router.Group("/base") 102 | v1.Use(middleware.TranslationMiddleware()) 103 | { 104 | base.BaseRegister(v1) 105 | controller.DemoRegister(v1) 106 | } 107 | // 108 | ////非登陆接口 109 | //store := sessions.NewCookieStore([]byte("secret")) 110 | apiNormalGroup := router.Group("/login") 111 | apiNormalGroup.Use() 112 | { 113 | 114 | controller.NoLoginRequiredRegister(apiNormalGroup) 115 | } 116 | // 需要登录的接口 117 | apiNormalGroup.Use(middleware.JwtAuthMiddleware()) 118 | { 119 | controller.ApiRegister(apiNormalGroup) 120 | } 121 | // 122 | ////登陆接口 123 | //apiAuthGroup := router.Group("/api") 124 | //apiAuthGroup.Use( 125 | // sessions.Sessions("mysession", store), 126 | // middleware.RecoveryMiddleware(), 127 | // middleware.RequestLog(), 128 | // middleware.SessionAuthMiddleware(), 129 | // middleware.TranslationMiddleware()) 130 | //{ 131 | // controller.ApiLoginRegister(apiAuthGroup) 132 | //} 133 | return router 134 | } 135 | -------------------------------------------------------------------------------- /util/bind/bind.go: -------------------------------------------------------------------------------- 1 | package bind 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/gin-gonic/gin/binding" 6 | ) 7 | 8 | func Bind(s interface{}, c *gin.Context) (interface{}, error) { 9 | b := binding.Default(c.Request.Method, c.ContentType()) 10 | if err := c.ShouldBindWith(s, b); err != nil { 11 | return nil, err 12 | } 13 | return s, nil 14 | } 15 | -------------------------------------------------------------------------------- /util/convert/convert.go: -------------------------------------------------------------------------------- 1 | package convert 2 | 3 | import "strconv" 4 | 5 | /** 6 | string 转换 7 | */ 8 | func StrToInt64(s string) (i int64, err error) { 9 | i, err = strconv.ParseInt(s, 10, 64) 10 | return i, err 11 | } 12 | 13 | func StrToInt32(s string) (i int64, err error) { 14 | i, err = strconv.ParseInt(s, 10, 32) 15 | return i, err 16 | } 17 | 18 | func StrToInt(s string) (i int, err error) { 19 | i, err = strconv.Atoi(s) 20 | return i, err 21 | } 22 | 23 | func StrToFloat64(s string) (f float64, err error) { 24 | f, err = strconv.ParseFloat(s, 64) 25 | return f, err 26 | } 27 | 28 | func StrToByte(s string) []byte { 29 | return []byte(s) 30 | } 31 | 32 | /** 33 | int 转换 34 | */ 35 | func IntToStr(i int) string { 36 | return strconv.Itoa(i) 37 | } 38 | 39 | func IntToInt32(i int) int32 { 40 | return int32(i) 41 | } 42 | 43 | func IntToInt64(i int) int64 { 44 | return int64(i) 45 | } 46 | 47 | /** 48 | int32 转换 49 | */ 50 | func Int32ToInt(i int32) int { 51 | return int(i) 52 | } 53 | 54 | func Int32ToInt64(i int32) int64 { 55 | return int64(i) 56 | } 57 | 58 | /** 59 | int64 转换 60 | */ 61 | func Int64ToInt(i int64) int { 62 | return int(i) 63 | } 64 | 65 | func Int64ToInt32(i int64) int32 { 66 | return int32(i) 67 | } 68 | 69 | func Int64ToStr(i int64) string { 70 | return strconv.FormatInt(i, 10) 71 | } 72 | -------------------------------------------------------------------------------- /util/cryp/aes.go: -------------------------------------------------------------------------------- 1 | package cryp 2 | 3 | import ( 4 | "bytes" 5 | "crypto/aes" 6 | "crypto/cipher" 7 | "encoding/base64" 8 | ) 9 | 10 | // AES 加解密 11 | // 这个工具类采用的是CBC分组模式 12 | 13 | func AesEncryptByte(origData []byte, key string) ([]byte, error) { 14 | k := []byte(key) 15 | // 分组秘钥 16 | block, err := aes.NewCipher(k) 17 | if err != nil { 18 | return []byte(""), err 19 | } 20 | // 获取秘钥块的长度 21 | blockSize := block.BlockSize() 22 | // 补全码 23 | origData = PKCS7Padding(origData, blockSize) 24 | // 加密模式 25 | blockMode := cipher.NewCBCEncrypter(block, k[:blockSize]) 26 | // 创建数组 27 | cryted := make([]byte, len(origData)) 28 | // 加密 29 | blockMode.CryptBlocks(cryted, origData) 30 | return cryted, nil 31 | } 32 | func AesEncrypt(plaintext string, key string) (string, error) { 33 | encryptByte, err := AesEncryptByte([]byte(plaintext), key) 34 | if err != nil { 35 | return "", err 36 | } 37 | return string(encryptByte), nil 38 | } 39 | func AesDecryptByte(crytedByte []byte, key string) ([]byte, error) { 40 | // 转成字节数组 41 | k := []byte(key) 42 | // 分组秘钥 43 | block, err := aes.NewCipher(k) 44 | if err != nil { 45 | return []byte(""), err 46 | } 47 | // 获取秘钥块的长度 48 | blockSize := block.BlockSize() 49 | // 加密模式 50 | blockMode := cipher.NewCBCDecrypter(block, k[:blockSize]) 51 | // 创建数组 52 | orig := make([]byte, len(crytedByte)) 53 | // 解密 54 | blockMode.CryptBlocks(orig, crytedByte) 55 | // 去补全码 56 | orig = PKCS7UnPadding(orig) 57 | return orig, nil 58 | } 59 | func AesDecrypt(cryted string, key string) (string, error) { 60 | crytedByte, err := base64.StdEncoding.DecodeString(cryted) 61 | if err != nil { 62 | return "", err 63 | } 64 | decryptByte, err := AesDecryptByte(crytedByte, key) 65 | if err != nil { 66 | return "", err 67 | } 68 | return string(decryptByte), nil 69 | } 70 | 71 | //补码 72 | func PKCS7Padding(ciphertext []byte, blocksize int) []byte { 73 | padding := blocksize - len(ciphertext)%blocksize 74 | padtext := bytes.Repeat([]byte{byte(padding)}, padding) 75 | return append(ciphertext, padtext...) 76 | } 77 | 78 | //去码 79 | func PKCS7UnPadding(origData []byte) []byte { 80 | length := len(origData) 81 | unpadding := int(origData[length-1]) 82 | return origData[:(length - unpadding)] 83 | } 84 | -------------------------------------------------------------------------------- /util/cryp/ase_test.go: -------------------------------------------------------------------------------- 1 | package cryp 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestAesDecrypt(t *testing.T) { 9 | orig := "hello world" 10 | //key := "123456781234567812345678" 11 | key := "9871267812345mn812345xyz" 12 | fmt.Println("原文:", orig) 13 | encryptCode, _ := AesEncrypt(orig, key) 14 | fmt.Println("密文:", encryptCode) 15 | decryptCode, _ := AesDecrypt(encryptCode, key+"2345t") 16 | fmt.Println("解密结果:", decryptCode) 17 | } 18 | -------------------------------------------------------------------------------- /util/cryp/base64.go: -------------------------------------------------------------------------------- 1 | package cryp 2 | 3 | import ( 4 | b64 "encoding/base64" 5 | ) 6 | 7 | func Base64Encode(s string) string { 8 | return b64.StdEncoding.EncodeToString([]byte(s)) 9 | } 10 | func Base64EncodeByte(bs []byte) string { 11 | return b64.StdEncoding.EncodeToString(bs) 12 | } 13 | func Base64Decode(s string) (string, error) { 14 | ds, err := b64.StdEncoding.DecodeString(s) 15 | return string(ds), err 16 | } 17 | func Base64DecodeByte(s string) []byte { 18 | ds, _ := b64.StdEncoding.DecodeString(s) 19 | return ds 20 | } 21 | func Base64EncodeByteCount(bs []byte, count int) string { 22 | s := Base64EncodeByte(bs) 23 | if count < 2 { 24 | return s 25 | } 26 | for i := 0; i < count-1; i++ { 27 | s = Base64Encode(s) 28 | } 29 | return s 30 | } 31 | -------------------------------------------------------------------------------- /util/cryp/hmac.go: -------------------------------------------------------------------------------- 1 | package cryp 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/sha256" 6 | ) 7 | 8 | // 注意:计算hamc_sha256时,是否需要转成十六进制,取决于自己的需要,代码中注释掉该行代码: 9 | //hex.EncodeToString(h.Sum(nil)) 10 | //func ComputeHmacSha256(message string, secret string) string { 11 | // key := []byte(secret) 12 | // h := hmac.New(sha256.New, key) 13 | // h.Write([]byte(message)) 14 | // // fmt.Println(h.Sum(nil)) 15 | // sha := hex.EncodeToString(h.Sum(nil)) 16 | // // fmt.Println(sha) 17 | // //return hex.EncodeToString(h.Sum(nil)) 18 | // return base64.StdEncoding.EncodeToString([]byte(sha)) 19 | //} 20 | 21 | // 生成消息认证码 22 | func GenerateHamc(plainText, key string) []byte { 23 | // 1.创建哈希接口, 需要指定使用的哈希算法, 和秘钥 24 | myhash := hmac.New(sha256.New, []byte(key)) 25 | // 2. 给哈希对象添加数据 26 | myhash.Write([]byte(plainText)) 27 | // 3. 计算散列值 28 | hashText := myhash.Sum(nil) 29 | return hashText 30 | } 31 | 32 | // 验证消息认证码 33 | func VerifyHamc(plainText, key, hashText string) bool { 34 | // 1.创建哈希接口, 需要指定使用的哈希算法, 和秘钥 35 | myhash := hmac.New(sha256.New, []byte(key)) 36 | // 2. 给哈希对象添加数据 37 | myhash.Write([]byte(plainText)) 38 | // 3. 计算散列值 39 | hamc1 := myhash.Sum(nil) 40 | // 4. 两个散列值比较 41 | return hmac.Equal([]byte(hashText), hamc1) 42 | } 43 | -------------------------------------------------------------------------------- /util/cryp/hmac_test.go: -------------------------------------------------------------------------------- 1 | package cryp 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestComputeHmacSha256(t *testing.T) { 9 | message := "hello world!" 10 | secret := "0933e54e76b24731a2d84b6b463ec04c" 11 | fmt.Println(GenerateHamc(message, secret)) 12 | } 13 | func TestVerifyHamc(t *testing.T) { 14 | //src := []byte("在消息认证码中,需要发送者和接收者之间共享密钥,而这个密钥不能被主动攻击者Mallory获取。" + 15 | // "如果这个密钥落入Mallory手中,则Mallory也可以计算出MAC值,从而就能够自由地进行篡改和伪装攻击," + 16 | // "这样一来消息认证码就无法发挥作用了。") 17 | //key := []byte("helloworld") 18 | 19 | message := "hello world!" 20 | secret := "0933e54e76b24731a2d84b6b463ec04c" 21 | hamc1 := GenerateHamc(message, secret) 22 | bl := VerifyHamc(message, secret, string(hamc1)) 23 | //fmt.Printf("校验结果: %t\n", bl) 24 | fmt.Println(bl) 25 | } 26 | -------------------------------------------------------------------------------- /util/cryp/md5.go: -------------------------------------------------------------------------------- 1 | package cryp 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | ) 7 | 8 | func MD5(str string) string { 9 | s := md5.New() 10 | s.Write([]byte(str)) 11 | return hex.EncodeToString(s.Sum(nil)) 12 | } 13 | -------------------------------------------------------------------------------- /util/cryp/myrsa.go: -------------------------------------------------------------------------------- 1 | package cryp 2 | 3 | // 4 | //import ( 5 | // "bytes" 6 | // "crypto/rand" 7 | // "crypto/rsa" 8 | // "crypto/sha256" 9 | // "crypto/x509" 10 | // "encoding/hex" 11 | // "encoding/pem" 12 | // "fmt" 13 | // "os" 14 | //) 15 | // 16 | //// 生成rsa的密钥对, 并且保存到磁盘文件中 17 | //func GenerateRsaKey(keySize int) { 18 | // // 1. 使用rsa中的GenerateKey方法生成私钥 19 | // privateKey, err := rsa.GenerateKey(rand.Reader, keySize) 20 | // if err != nil { 21 | // panic(err) 22 | // } 23 | // // 2. 通过x509标准将得到的ras私钥序列化为ASN.1 的 DER编码字符串 24 | // derText := x509.MarshalPKCS1PrivateKey(privateKey) 25 | // // 3. 要组织一个pem.Block(base64编码) 26 | // block := pem.Block{ 27 | // Type: "rsa private key", // 这个地方写个字符串就行 28 | // Bytes: derText, 29 | // } 30 | // // 4. pem编码 31 | // file, err := os.Create("private.pem") 32 | // if err != nil { 33 | // panic(err) 34 | // } 35 | // pem.Encode(file, &block) 36 | // file.Close() 37 | // 38 | // // ============ 公钥 ========== 39 | // // 1. 从私钥中取出公钥 40 | // publicKey := privateKey.PublicKey 41 | // // 2. 使用x509标准序列化 42 | // derstream, err := x509.MarshalPKIXPublicKey(&publicKey) 43 | // if err != nil { 44 | // panic(err) 45 | // } 46 | // // 3. 将得到的数据放到pem.Block中 47 | // block = pem.Block{ 48 | // Type: "rsa public key", 49 | // Bytes: derstream, 50 | // } 51 | // // 4. pem编码 52 | // file, err = os.Create("public.pem") 53 | // if err != nil { 54 | // panic(err) 55 | // } 56 | // pem.Encode(file, &block) 57 | // file.Close() 58 | //} 59 | // 60 | //// 生成rsa的密钥对, 返回字节切片 61 | //func GenerateRsaKey2(keySize int) (privateKeyStr, publicKeyStr []byte) { 62 | // // 1. 使用rsa中的GenerateKey方法生成私钥 63 | // privateKey, err := rsa.GenerateKey(rand.Reader, keySize) 64 | // if err != nil { 65 | // panic(err) 66 | // } 67 | // // 2. 通过x509标准将得到的ras私钥序列化为ASN.1 的 DER编码字符串 68 | // derText := x509.MarshalPKCS1PrivateKey(privateKey) 69 | // // 3. 要组织一个pem.Block(base64编码) 70 | // block := pem.Block{ 71 | // Type: "rsa private key", // 这个地方写个字符串就行 72 | // Bytes: derText, 73 | // } 74 | // // 4. pem编码 75 | // // 定义一个字节缓冲, 快速地连接字符串 76 | // buffer := new(bytes.Buffer) 77 | // pem.Encode(buffer, &block) 78 | // // 将连接好的字节数组转换为字符串并输出 79 | // privateKeyStr = buffer.Bytes() 80 | // // ============ 公钥 ========== 81 | // // 1. 从私钥中取出公钥 82 | // publicKey := privateKey.PublicKey 83 | // // 2. 使用x509标准序列化 84 | // derstream, err := x509.MarshalPKIXPublicKey(&publicKey) 85 | // if err != nil { 86 | // panic(err) 87 | // } 88 | // // 3. 将得到的数据放到pem.Block中 89 | // block = pem.Block{ 90 | // Type: "rsa public key", 91 | // Bytes: derstream, 92 | // } 93 | // 94 | // buffer = new(bytes.Buffer) 95 | // pem.Encode(buffer, &block) 96 | // // 将连接好的字节数组转换为字符串并输出 97 | // publicKeyStr = buffer.Bytes() 98 | // return 99 | //} 100 | // 101 | //// RSA 加密, 公钥加密 102 | //func RSAEncrypt(plainText []byte, fileName string) []byte { 103 | // // 1. 打开文件, 并且读出文件内容 104 | // file, err := os.Open(fileName) 105 | // if err != nil { 106 | // panic(err) 107 | // } 108 | // fileInfo, err := file.Stat() 109 | // if err != nil { 110 | // panic(err) 111 | // } 112 | // buf := make([]byte, fileInfo.Size()) 113 | // file.Read(buf) 114 | // file.Close() 115 | // // 2. pem解码 116 | // block, _ := pem.Decode(buf) 117 | // pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) 118 | // //断言类型转换 119 | // pubKey := pubInterface.(*rsa.PublicKey) 120 | // // 3. 使用公钥加密 121 | // cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, plainText) 122 | // if err != nil { 123 | // panic(err) 124 | // } 125 | // return cipherText 126 | //} 127 | // 128 | //// RSA 解密 129 | //func RSADecrypt(cipherText []byte, fileName string) []byte { 130 | // // 1. 打开文件, 并且读出文件内容 131 | // file, err := os.Open(fileName) 132 | // if err != nil { 133 | // panic(err) 134 | // } 135 | // fileInfo, err := file.Stat() 136 | // if err != nil { 137 | // panic(err) 138 | // } 139 | // buf := make([]byte, fileInfo.Size()) 140 | // file.Read(buf) 141 | // file.Close() 142 | // // 2. pem解码 143 | // block, _ := pem.Decode(buf) 144 | // privKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) 145 | // if err != nil { 146 | // panic(err) 147 | // } 148 | // 149 | // // 3. 使用私钥解密 150 | // plainText, err := rsa.DecryptPKCS1v15(rand.Reader, privKey, cipherText) 151 | // if err != nil { 152 | // panic(err) 153 | // } 154 | // return plainText 155 | //} 156 | // 157 | //func main() { 158 | // GenerateRsaKey(4096) 159 | // src := []byte("我是小崔, 如果我死了, 肯定不是自杀...我是小崔, 如果我死了, 肯定不是自杀...我是小崔, 如果我死了, 肯定不是自杀...我是小崔, 如果我死了, 肯定不是自杀...我是小崔, 如果我死了, 肯定不是自杀...") 160 | // cipherText := RSAEncrypt(src, "public.pem") 161 | // plainText := RSADecrypt(cipherText, "private.pem") 162 | // fmt.Println(string(plainText)) 163 | // myHash() 164 | //} 165 | // 166 | //// 使用sha256 167 | //func myHash() { 168 | // // sha256.Sum256([]byte("hello, go")) 169 | // // 1. 创建哈希接口对象 170 | // myHash := sha256.New() 171 | // // 2. 添加数据 172 | // src := []byte("我是小崔, 如果我死了, 肯定不是自杀...我是小崔, 如果我死了, 肯定不是自杀...我是小崔, 如果我死了, 肯定不是自杀...我是小崔, 如果我死了, 肯定不是自杀...我是小崔, 如果我死了, 肯定不是自杀...") 173 | // myHash.Write(src) 174 | // myHash.Write(src) 175 | // myHash.Write(src) 176 | // // 3. 计算结果 177 | // res := myHash.Sum(nil) 178 | // // 4. 格式化为16进制形式 179 | // myStr := hex.EncodeToString(res) 180 | // fmt.Printf("%s\n", myStr) 181 | //} 182 | -------------------------------------------------------------------------------- /util/cryp/ras_test.go: -------------------------------------------------------------------------------- 1 | package cryp 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func TestRsaDecrypt(t *testing.T) { 10 | data, err := RsaEncrypt([]byte("hello world"), "123") 11 | fmt.Println(err, base64.StdEncoding.EncodeToString(data)) 12 | origData, err := RsaDecrypt(data, "123") 13 | fmt.Println(err, string(origData)) 14 | } 15 | 16 | func TestGenerateRsaKey(t *testing.T) { 17 | privateKeyByte, publicKeyByte := GenerateRsaKey(2048) 18 | 19 | privateKeyStr := string(privateKeyByte) 20 | fmt.Println("privateKeyStr:\n", privateKeyStr) 21 | fmt.Println() 22 | publicKeyStr := string(publicKeyByte) 23 | fmt.Println("publicKeyStr:\n", publicKeyStr) 24 | 25 | data, err := RsaEncrypt([]byte("hello world"), publicKeyStr) 26 | fmt.Println(err, base64.StdEncoding.EncodeToString(data)) 27 | origData, err := RsaDecrypt(data, privateKeyStr) 28 | fmt.Println(err, string(origData)) 29 | 30 | } 31 | 32 | func TestPriKeyENCTYPT(t *testing.T) { 33 | privateKeyByte, publicKeyByte := GenerateRsaKey(2048) 34 | privateKeyStr := string(privateKeyByte) 35 | publicKeyStr := string(publicKeyByte) 36 | data, err := PriKeyENCTYPT([]byte("hello world"), privateKeyStr) 37 | fmt.Println(err, base64.StdEncoding.EncodeToString(data)) 38 | origData, err := PubKeyDECRYPT(data, publicKeyStr) 39 | fmt.Println(err, string(origData)) 40 | } 41 | -------------------------------------------------------------------------------- /util/cryp/rsa.go: -------------------------------------------------------------------------------- 1 | package cryp 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "crypto/rsa" 7 | "crypto/x509" 8 | "encoding/pem" 9 | "errors" 10 | "io/ioutil" 11 | ) 12 | 13 | // 可通过openssl产生 14 | //openssl genrsa -out rsa_private_key.pem 1024 15 | //var privateKey = []byte(` 16 | //-----BEGIN RSA PRIVATE KEY----- 17 | //MIIEowIBAAKCAQEAzLeI8JGMWFSjJaOzDFxloLwMsH6vwPZASttwVX0U0eiNOf3X 18 | ///Q08+BbTJB1W2Xy4YQv7/Y1slI3ZpcKu6FoEowW/Nf4RmlKgMflENdjD6BijQUDn 19 | //9qYYBEJRkLSMSD+EiKUfq08z5x9i08NZg7E4u10mColtSuFp4RoAQKpFEA5qyrYd 20 | //KmXoyW3JuTyszwO+aCMAS1dXKsKBF4HbC0GPbt8/7Es2E3zHJasxkL2iZ5mh3awJ 21 | //wVnz/I0w+TGAu/XGzshGBAKDxHIRFS82YPw7/zwpk8NquZ55bzyNxCXIjvtLp2R7 22 | //A/HmYwJ2u/xh9AtQpEj0sXW95Ouo/OO+1QElNwIDAQABAoIBABU46Z9W11/I1mju 23 | //gX9EjNyO4hnh6EJuxNd9zDVwlBn2q71ZTWzUVH+7jgPubrR5M3wMDAGLCbiUw/1l 24 | //I1C/FD/6NopYXmbLLgRAPQv//r8u3q3DFskBCvhWD7KapPhQbWLlC1VtDoplPI+L 25 | //btoyIxl5XJo3CPd8SselNGV/wU0aogCjTaa40oauC0wiw/WoeCRA2Ok4GsQrYAN0 26 | //IbV0/8jIBzIQONbENVDncm/u5vss/r7br0e/jdzcP0reMofAdLcQDn4nsU1JYD4i 27 | //Ex087EmV2HTnn+uew8wtZlmsAZxKZh2FyTa0fCiVQXMw3Lf1GiZLXCs43ZysHJI3 28 | //DNpNL9ECgYEA70Xuql67jvwhYndNtifLBdszrncTiNnT+B0T6ketSBKRCdBLGgnz 29 | //hDm971dQIFjko8aQwsecg7u6t2zz8vHDXFUk1yeeOqhYMYbzpkfnR2k9TQ4IPJtl 30 | //xgWoQ9S80jPFrG2F0rvFdULhP8eE7UOqiPdP30TLZyJreEPOoYiVx7sCgYEA2wct 31 | //2kPFvCqSyoAtz4ZsbQMQIgxLfkXZM3zy1TKPCDd2E/ey1LV4Og/+6vrnQZ8GAhcD 32 | //1brbmO6syepilB897bI0hNu03dWwT49Z4t6tDQ8YVOudm5HzFu2kK4ZEvayF8Ot/ 33 | //mZFvevUe893/VZwJlW7RpxMZFmtRpV5wHB3+6rUCgYAJ+1LfjKAqcN47q1p0lOhl 34 | //UCWxy4nnFZ9AJIZmKaNS9GNUk3nuliewhnAkAfJ3xv2Sz3/OgGFJJZW+fS8YHXnW 35 | //6j5lM2PocolrV4Pmle1SD1PdWQ6C6MCwKCBC5CcUZdCDRvZkOi0cnTOkY4BqHX6J 36 | //xDdyyv3pSYhONhXyqy4EbQKBgQDUIUredv8etBkBeU1lDasblXjdkQzY2mt3u48w 37 | //v0vaSGTbB+6ypqMvkOhyytiJLKxT/9hd+yDOKHM/B/u7u9ptyUemWWf95gVhuNP0 38 | //r3fpCvKk5KH710oZrcVvxhXzohEDegJWSI4xBxCYXiz6zCpYCUGSUCPfG8eyoxlv 39 | //kfmfdQKBgHSvwUPAYcNATSSaXbAJErLqknsRUdq9EWTNqJ8W4WrkTx2dWjW6deE9 40 | //7VfIdxj/zUBe5fMD9BQXejP+nZDOdJQyXjfDcNmCGR5AEKiG9CzRey/w+rH+CbKf 41 | //0R6Scku/cP3itx9akIgV7NIyIxq0HQWn7opkfGH3rGR4TtGmCU7w 42 | //-----END RSA PRIVATE KEY----- 43 | //`) 44 | 45 | //openssl 46 | //openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem 47 | //var publicKey = []byte(` 48 | //-----BEGIN PUBLIC KEY----- 49 | //MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzLeI8JGMWFSjJaOzDFxl 50 | //oLwMsH6vwPZASttwVX0U0eiNOf3X/Q08+BbTJB1W2Xy4YQv7/Y1slI3ZpcKu6FoE 51 | //owW/Nf4RmlKgMflENdjD6BijQUDn9qYYBEJRkLSMSD+EiKUfq08z5x9i08NZg7E4 52 | //u10mColtSuFp4RoAQKpFEA5qyrYdKmXoyW3JuTyszwO+aCMAS1dXKsKBF4HbC0GP 53 | //bt8/7Es2E3zHJasxkL2iZ5mh3awJwVnz/I0w+TGAu/XGzshGBAKDxHIRFS82YPw7 54 | ///zwpk8NquZ55bzyNxCXIjvtLp2R7A/HmYwJ2u/xh9AtQpEj0sXW95Ouo/OO+1QEl 55 | //NwIDAQAB 56 | //-----END PUBLIC KEY----- 57 | //`) 58 | 59 | // 加密 60 | func RsaEncrypt(origData []byte, publicKey string) ([]byte, error) { 61 | 62 | //解密pem格式的公钥 63 | block, _ := pem.Decode([]byte(publicKey)) 64 | if block == nil { 65 | return nil, errors.New("public key error") 66 | } 67 | // 解析公钥 68 | pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) 69 | if err != nil { 70 | return nil, err 71 | } 72 | // 类型断言 73 | pub := pubInterface.(*rsa.PublicKey) 74 | //加密 75 | return rsa.EncryptPKCS1v15(rand.Reader, pub, origData) 76 | } 77 | 78 | // 解密 79 | func RsaDecrypt(ciphertext []byte, privateKey string) ([]byte, error) { 80 | //解密 81 | block, _ := pem.Decode([]byte(privateKey)) 82 | if block == nil { 83 | return nil, errors.New("private key error!") 84 | } 85 | //解析PKCS1格式的私钥 86 | priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) 87 | if err != nil { 88 | return nil, err 89 | } 90 | // 解密 91 | return rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext) 92 | } 93 | 94 | // 公钥解密 95 | func PubKeyDECRYPT(input []byte, publicKey string) ([]byte, error) { 96 | // 类型断言 97 | //解密pem格式的公钥 98 | block, _ := pem.Decode([]byte(publicKey)) 99 | if block == nil { 100 | return nil, errors.New("public key error") 101 | } 102 | // 解析公钥 103 | pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) 104 | if err != nil { 105 | return nil, err 106 | } 107 | // 类型断言 108 | pub := pubInterface.(*rsa.PublicKey) 109 | output := bytes.NewBuffer(nil) 110 | err = pubKeyIO(pub, bytes.NewReader(input), output, false) 111 | if err != nil { 112 | return []byte(""), err 113 | } 114 | return ioutil.ReadAll(output) 115 | } 116 | 117 | // 私钥加密 118 | func PriKeyENCTYPT(input []byte, privateKey string) ([]byte, error) { 119 | 120 | //解密 121 | block, _ := pem.Decode([]byte(privateKey)) 122 | if block == nil { 123 | return nil, errors.New("private key error!") 124 | } 125 | //解析PKCS1格式的私钥 126 | priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) 127 | output := bytes.NewBuffer(nil) 128 | err = priKeyIO(priv, bytes.NewReader(input), output, true) 129 | if err != nil { 130 | return []byte(""), err 131 | } 132 | return ioutil.ReadAll(output) 133 | } 134 | 135 | // 生成rsa的密钥对, 返回字节切片 136 | func GenerateRsaKey(keySize int) (privateKeyStr, publicKeyStr []byte) { 137 | // 1. 使用rsa中的GenerateKey方法生成私钥 138 | privateKey, err := rsa.GenerateKey(rand.Reader, keySize) 139 | if err != nil { 140 | panic(err) 141 | } 142 | // 2. 通过x509标准将得到的ras私钥序列化为ASN.1 的 DER编码字符串 143 | derText := x509.MarshalPKCS1PrivateKey(privateKey) 144 | // 3. 要组织一个pem.Block(base64编码) 145 | block := pem.Block{ 146 | Type: "Elliptic curve cryptography private 5120", // rsa private key这个地方写个字符串就行 147 | Bytes: derText, 148 | } 149 | // 4. pem编码 150 | // 定义一个字节缓冲, 快速地连接字符串 151 | buffer := new(bytes.Buffer) 152 | pem.Encode(buffer, &block) 153 | // 将连接好的字节数组转换为字符串并输出 154 | privateKeyStr = buffer.Bytes() 155 | // ============ 公钥 ========== 156 | // 1. 从私钥中取出公钥 157 | publicKey := privateKey.PublicKey 158 | // 2. 使用x509标准序列化 159 | derstream, err := x509.MarshalPKIXPublicKey(&publicKey) 160 | if err != nil { 161 | panic(err) 162 | } 163 | // 3. 将得到的数据放到pem.Block中 164 | block = pem.Block{ 165 | Type: "Elliptic curve cryptography private 5120", 166 | Bytes: derstream, 167 | } 168 | 169 | buffer = new(bytes.Buffer) 170 | pem.Encode(buffer, &block) 171 | // 将连接好的字节数组转换为字符串并输出 172 | publicKeyStr = buffer.Bytes() 173 | return 174 | } 175 | -------------------------------------------------------------------------------- /util/cryp/rsa_ext.go: -------------------------------------------------------------------------------- 1 | package cryp 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "crypto/rsa" 7 | "crypto/x509" 8 | "encoding/pem" 9 | "errors" 10 | "io" 11 | "io/ioutil" 12 | "math/big" 13 | ) 14 | 15 | var ( 16 | ErrDataToLarge = errors.New("message too long for RSA public key size") 17 | ErrDataLen = errors.New("data length error") 18 | ErrDataBroken = errors.New("data broken, first byte is not zero") 19 | ErrKeyPairDismatch = errors.New("data is not encrypted by the private key") 20 | ErrDecryption = errors.New("decryption error") 21 | ErrPublicKey = errors.New("get public key error") 22 | ErrPrivateKey = errors.New("get private key error") 23 | ) 24 | 25 | // 设置公钥 26 | func getPubKey(publickey []byte) (*rsa.PublicKey, error) { 27 | // decode public key 28 | block, _ := pem.Decode(publickey) 29 | if block == nil { 30 | return nil, errors.New("get public key error") 31 | } 32 | // x509 parse public key 33 | pub, err := x509.ParsePKIXPublicKey(block.Bytes) 34 | if err != nil { 35 | return nil, err 36 | } 37 | return pub.(*rsa.PublicKey), err 38 | } 39 | 40 | // 设置私钥 41 | func getPriKey(privatekey []byte) (*rsa.PrivateKey, error) { 42 | block, _ := pem.Decode(privatekey) 43 | if block == nil { 44 | return nil, errors.New("get private key error") 45 | } 46 | pri, err := x509.ParsePKCS1PrivateKey(block.Bytes) 47 | if err == nil { 48 | return pri, nil 49 | } 50 | pri2, err := x509.ParsePKCS8PrivateKey(block.Bytes) 51 | if err != nil { 52 | return nil, err 53 | } 54 | return pri2.(*rsa.PrivateKey), nil 55 | } 56 | 57 | // 公钥加密或解密byte 58 | func pubKeyByte(pub *rsa.PublicKey, in []byte, isEncrytp bool) ([]byte, error) { 59 | k := (pub.N.BitLen() + 7) / 8 60 | if isEncrytp { 61 | k = k - 11 62 | } 63 | if len(in) <= k { 64 | if isEncrytp { 65 | return rsa.EncryptPKCS1v15(rand.Reader, pub, in) 66 | } else { 67 | return pubKeyDecrypt(pub, in) 68 | } 69 | } else { 70 | iv := make([]byte, k) 71 | out := bytes.NewBuffer(iv) 72 | if err := pubKeyIO(pub, bytes.NewReader(in), out, isEncrytp); err != nil { 73 | return nil, err 74 | } 75 | return ioutil.ReadAll(out) 76 | } 77 | } 78 | 79 | // 私钥加密或解密byte 80 | func priKeyByte(pri *rsa.PrivateKey, in []byte, isEncrytp bool) ([]byte, error) { 81 | k := (pri.N.BitLen() + 7) / 8 82 | if isEncrytp { 83 | k = k - 11 84 | } 85 | if len(in) <= k { 86 | if isEncrytp { 87 | return priKeyEncrypt(rand.Reader, pri, in) 88 | } else { 89 | return rsa.DecryptPKCS1v15(rand.Reader, pri, in) 90 | } 91 | } else { 92 | iv := make([]byte, k) 93 | out := bytes.NewBuffer(iv) 94 | if err := priKeyIO(pri, bytes.NewReader(in), out, isEncrytp); err != nil { 95 | return nil, err 96 | } 97 | return ioutil.ReadAll(out) 98 | } 99 | } 100 | 101 | // 公钥加密或解密Reader 102 | func pubKeyIO(pub *rsa.PublicKey, in io.Reader, out io.Writer, isEncrytp bool) (err error) { 103 | k := (pub.N.BitLen() + 7) / 8 104 | if isEncrytp { 105 | k = k - 11 106 | } 107 | buf := make([]byte, k) 108 | var b []byte 109 | size := 0 110 | for { 111 | size, err = in.Read(buf) 112 | if err != nil { 113 | if err == io.EOF { 114 | return nil 115 | } 116 | return err 117 | } 118 | if size < k { 119 | b = buf[:size] 120 | } else { 121 | b = buf 122 | } 123 | if isEncrytp { 124 | b, err = rsa.EncryptPKCS1v15(rand.Reader, pub, b) 125 | } else { 126 | b, err = pubKeyDecrypt(pub, b) 127 | } 128 | if err != nil { 129 | return err 130 | } 131 | if _, err = out.Write(b); err != nil { 132 | return err 133 | } 134 | } 135 | return nil 136 | } 137 | 138 | // 私钥加密或解密Reader 139 | func priKeyIO(pri *rsa.PrivateKey, r io.Reader, w io.Writer, isEncrytp bool) (err error) { 140 | k := (pri.N.BitLen() + 7) / 8 141 | if isEncrytp { 142 | k = k - 11 143 | } 144 | buf := make([]byte, k) 145 | var b []byte 146 | size := 0 147 | for { 148 | size, err = r.Read(buf) 149 | if err != nil { 150 | if err == io.EOF { 151 | return nil 152 | } 153 | return err 154 | } 155 | if size < k { 156 | b = buf[:size] 157 | } else { 158 | b = buf 159 | } 160 | if isEncrytp { 161 | b, err = priKeyEncrypt(rand.Reader, pri, b) 162 | } else { 163 | b, err = rsa.DecryptPKCS1v15(rand.Reader, pri, b) 164 | } 165 | if err != nil { 166 | return err 167 | } 168 | if _, err = w.Write(b); err != nil { 169 | return err 170 | } 171 | } 172 | return nil 173 | } 174 | 175 | // 公钥解密 176 | func pubKeyDecrypt(pub *rsa.PublicKey, data []byte) ([]byte, error) { 177 | k := (pub.N.BitLen() + 7) / 8 178 | if k != len(data) { 179 | return nil, ErrDataLen 180 | } 181 | m := new(big.Int).SetBytes(data) 182 | if m.Cmp(pub.N) > 0 { 183 | return nil, ErrDataToLarge 184 | } 185 | m.Exp(m, big.NewInt(int64(pub.E)), pub.N) 186 | d := leftPad(m.Bytes(), k) 187 | if d[0] != 0 { 188 | return nil, ErrDataBroken 189 | } 190 | if d[1] != 0 && d[1] != 1 { 191 | return nil, ErrKeyPairDismatch 192 | } 193 | var i = 2 194 | for ; i < len(d); i++ { 195 | if d[i] == 0 { 196 | break 197 | } 198 | } 199 | i++ 200 | if i == len(d) { 201 | return nil, nil 202 | } 203 | return d[i:], nil 204 | } 205 | 206 | // 私钥加密 207 | func priKeyEncrypt(rand io.Reader, priv *rsa.PrivateKey, hashed []byte) ([]byte, error) { 208 | tLen := len(hashed) 209 | k := (priv.N.BitLen() + 7) / 8 210 | if k < tLen+11 { 211 | return nil, ErrDataLen 212 | } 213 | em := make([]byte, k) 214 | em[1] = 1 215 | for i := 2; i < k-tLen-1; i++ { 216 | em[i] = 0xff 217 | } 218 | copy(em[k-tLen:k], hashed) 219 | m := new(big.Int).SetBytes(em) 220 | c, err := decrypt(rand, priv, m) 221 | if err != nil { 222 | return nil, err 223 | } 224 | copyWithLeftPad(em, c.Bytes()) 225 | return em, nil 226 | } 227 | 228 | // 从crypto/rsa复制 229 | var bigZero = big.NewInt(0) 230 | var bigOne = big.NewInt(1) 231 | 232 | // 从crypto/rsa复制 233 | func encrypt(c *big.Int, pub *rsa.PublicKey, m *big.Int) *big.Int { 234 | e := big.NewInt(int64(pub.E)) 235 | c.Exp(m, e, pub.N) 236 | return c 237 | } 238 | 239 | // 从crypto/rsa复制 240 | func decrypt(random io.Reader, priv *rsa.PrivateKey, c *big.Int) (m *big.Int, err error) { 241 | if c.Cmp(priv.N) > 0 { 242 | err = ErrDecryption 243 | return 244 | } 245 | var ir *big.Int 246 | if random != nil { 247 | var r *big.Int 248 | 249 | for { 250 | r, err = rand.Int(random, priv.N) 251 | if err != nil { 252 | return 253 | } 254 | if r.Cmp(bigZero) == 0 { 255 | r = bigOne 256 | } 257 | var ok bool 258 | ir, ok = modInverse(r, priv.N) 259 | if ok { 260 | break 261 | } 262 | } 263 | bigE := big.NewInt(int64(priv.E)) 264 | rpowe := new(big.Int).Exp(r, bigE, priv.N) 265 | cCopy := new(big.Int).Set(c) 266 | cCopy.Mul(cCopy, rpowe) 267 | cCopy.Mod(cCopy, priv.N) 268 | c = cCopy 269 | } 270 | if priv.Precomputed.Dp == nil { 271 | m = new(big.Int).Exp(c, priv.D, priv.N) 272 | } else { 273 | m = new(big.Int).Exp(c, priv.Precomputed.Dp, priv.Primes[0]) 274 | m2 := new(big.Int).Exp(c, priv.Precomputed.Dq, priv.Primes[1]) 275 | m.Sub(m, m2) 276 | if m.Sign() < 0 { 277 | m.Add(m, priv.Primes[0]) 278 | } 279 | m.Mul(m, priv.Precomputed.Qinv) 280 | m.Mod(m, priv.Primes[0]) 281 | m.Mul(m, priv.Primes[1]) 282 | m.Add(m, m2) 283 | 284 | for i, values := range priv.Precomputed.CRTValues { 285 | prime := priv.Primes[2+i] 286 | m2.Exp(c, values.Exp, prime) 287 | m2.Sub(m2, m) 288 | m2.Mul(m2, values.Coeff) 289 | m2.Mod(m2, prime) 290 | if m2.Sign() < 0 { 291 | m2.Add(m2, prime) 292 | } 293 | m2.Mul(m2, values.R) 294 | m.Add(m, m2) 295 | } 296 | } 297 | if ir != nil { 298 | m.Mul(m, ir) 299 | m.Mod(m, priv.N) 300 | } 301 | 302 | return 303 | } 304 | 305 | // 从crypto/rsa复制 306 | func copyWithLeftPad(dest, src []byte) { 307 | numPaddingBytes := len(dest) - len(src) 308 | for i := 0; i < numPaddingBytes; i++ { 309 | dest[i] = 0 310 | } 311 | copy(dest[numPaddingBytes:], src) 312 | } 313 | 314 | // 从crypto/rsa复制 315 | func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) { 316 | _, err = io.ReadFull(rand, s) 317 | if err != nil { 318 | return 319 | } 320 | for i := 0; i < len(s); i++ { 321 | for s[i] == 0 { 322 | _, err = io.ReadFull(rand, s[i:i+1]) 323 | if err != nil { 324 | return 325 | } 326 | s[i] ^= 0x42 327 | } 328 | } 329 | return 330 | } 331 | 332 | // 从crypto/rsa复制 333 | func leftPad(input []byte, size int) (out []byte) { 334 | n := len(input) 335 | if n > size { 336 | n = size 337 | } 338 | out = make([]byte, size) 339 | copy(out[len(out)-n:], input) 340 | return 341 | } 342 | 343 | // 从crypto/rsa复制 344 | func modInverse(a, n *big.Int) (ia *big.Int, ok bool) { 345 | g := new(big.Int) 346 | x := new(big.Int) 347 | y := new(big.Int) 348 | g.GCD(x, y, a, n) 349 | if g.Cmp(bigOne) != 0 { 350 | return 351 | } 352 | if x.Cmp(bigOne) < 0 { 353 | x.Add(x, n) 354 | } 355 | return x, true 356 | } 357 | -------------------------------------------------------------------------------- /util/cryp/rsa_private_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAzLeI8JGMWFSjJaOzDFxloLwMsH6vwPZASttwVX0U0eiNOf3X 3 | /Q08+BbTJB1W2Xy4YQv7/Y1slI3ZpcKu6FoEowW/Nf4RmlKgMflENdjD6BijQUDn 4 | 9qYYBEJRkLSMSD+EiKUfq08z5x9i08NZg7E4u10mColtSuFp4RoAQKpFEA5qyrYd 5 | KmXoyW3JuTyszwO+aCMAS1dXKsKBF4HbC0GPbt8/7Es2E3zHJasxkL2iZ5mh3awJ 6 | wVnz/I0w+TGAu/XGzshGBAKDxHIRFS82YPw7/zwpk8NquZ55bzyNxCXIjvtLp2R7 7 | A/HmYwJ2u/xh9AtQpEj0sXW95Ouo/OO+1QElNwIDAQABAoIBABU46Z9W11/I1mju 8 | gX9EjNyO4hnh6EJuxNd9zDVwlBn2q71ZTWzUVH+7jgPubrR5M3wMDAGLCbiUw/1l 9 | I1C/FD/6NopYXmbLLgRAPQv//r8u3q3DFskBCvhWD7KapPhQbWLlC1VtDoplPI+L 10 | btoyIxl5XJo3CPd8SselNGV/wU0aogCjTaa40oauC0wiw/WoeCRA2Ok4GsQrYAN0 11 | IbV0/8jIBzIQONbENVDncm/u5vss/r7br0e/jdzcP0reMofAdLcQDn4nsU1JYD4i 12 | Ex087EmV2HTnn+uew8wtZlmsAZxKZh2FyTa0fCiVQXMw3Lf1GiZLXCs43ZysHJI3 13 | DNpNL9ECgYEA70Xuql67jvwhYndNtifLBdszrncTiNnT+B0T6ketSBKRCdBLGgnz 14 | hDm971dQIFjko8aQwsecg7u6t2zz8vHDXFUk1yeeOqhYMYbzpkfnR2k9TQ4IPJtl 15 | xgWoQ9S80jPFrG2F0rvFdULhP8eE7UOqiPdP30TLZyJreEPOoYiVx7sCgYEA2wct 16 | 2kPFvCqSyoAtz4ZsbQMQIgxLfkXZM3zy1TKPCDd2E/ey1LV4Og/+6vrnQZ8GAhcD 17 | 1brbmO6syepilB897bI0hNu03dWwT49Z4t6tDQ8YVOudm5HzFu2kK4ZEvayF8Ot/ 18 | mZFvevUe893/VZwJlW7RpxMZFmtRpV5wHB3+6rUCgYAJ+1LfjKAqcN47q1p0lOhl 19 | UCWxy4nnFZ9AJIZmKaNS9GNUk3nuliewhnAkAfJ3xv2Sz3/OgGFJJZW+fS8YHXnW 20 | 6j5lM2PocolrV4Pmle1SD1PdWQ6C6MCwKCBC5CcUZdCDRvZkOi0cnTOkY4BqHX6J 21 | xDdyyv3pSYhONhXyqy4EbQKBgQDUIUredv8etBkBeU1lDasblXjdkQzY2mt3u48w 22 | v0vaSGTbB+6ypqMvkOhyytiJLKxT/9hd+yDOKHM/B/u7u9ptyUemWWf95gVhuNP0 23 | r3fpCvKk5KH710oZrcVvxhXzohEDegJWSI4xBxCYXiz6zCpYCUGSUCPfG8eyoxlv 24 | kfmfdQKBgHSvwUPAYcNATSSaXbAJErLqknsRUdq9EWTNqJ8W4WrkTx2dWjW6deE9 25 | 7VfIdxj/zUBe5fMD9BQXejP+nZDOdJQyXjfDcNmCGR5AEKiG9CzRey/w+rH+CbKf 26 | 0R6Scku/cP3itx9akIgV7NIyIxq0HQWn7opkfGH3rGR4TtGmCU7w 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /util/cryp/rsa_public_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzLeI8JGMWFSjJaOzDFxl 3 | oLwMsH6vwPZASttwVX0U0eiNOf3X/Q08+BbTJB1W2Xy4YQv7/Y1slI3ZpcKu6FoE 4 | owW/Nf4RmlKgMflENdjD6BijQUDn9qYYBEJRkLSMSD+EiKUfq08z5x9i08NZg7E4 5 | u10mColtSuFp4RoAQKpFEA5qyrYdKmXoyW3JuTyszwO+aCMAS1dXKsKBF4HbC0GP 6 | bt8/7Es2E3zHJasxkL2iZ5mh3awJwVnz/I0w+TGAu/XGzshGBAKDxHIRFS82YPw7 7 | /zwpk8NquZ55bzyNxCXIjvtLp2R7A/HmYwJ2u/xh9AtQpEj0sXW95Ouo/OO+1QEl 8 | NwIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /util/cryp/sha256.go: -------------------------------------------------------------------------------- 1 | package cryp 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/hex" 6 | ) 7 | 8 | //SHA256生成哈希值 9 | func GetSHA256HashCode(message []byte) string { 10 | //方法一: 11 | //创建一个基于SHA256算法的hash.Hash接口的对象 12 | hash := sha256.New() 13 | //输入数据 14 | hash.Write(message) 15 | //计算哈希值 16 | bytes := hash.Sum(nil) 17 | //将字符串编码为16进制格式,返回字符串 18 | hashCode := hex.EncodeToString(bytes) 19 | //返回哈希值 20 | return hashCode 21 | 22 | //方法二: 23 | //bytes2:=sha256.Sum256(message)//计算哈希值,返回一个长度为32的数组 24 | //hashcode2:=hex.EncodeToString(bytes2[:])//将数组转换成切片,转换成16进制,返回字符串 25 | //return hashcode2 26 | } 27 | -------------------------------------------------------------------------------- /util/cryp/uuid.go: -------------------------------------------------------------------------------- 1 | package cryp 2 | 3 | import ( 4 | "github.com/google/uuid" 5 | ) 6 | 7 | func GenUUID() string { 8 | u, _ := uuid.NewRandom() 9 | return u.String() 10 | } 11 | -------------------------------------------------------------------------------- /util/json/json.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import "encoding/json" 4 | 5 | func Encode(v interface{}) (string, error) { 6 | bytes, err := json.Marshal(v) 7 | if err != nil { 8 | return "", err 9 | } 10 | return string(bytes), nil 11 | } 12 | 13 | func Decode(data []byte, val interface{}) error { 14 | return json.Unmarshal(data, val) 15 | } 16 | -------------------------------------------------------------------------------- /util/jwt/jwt.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | import ( 4 | "errors" 5 | "fehu/constant" 6 | "github.com/dgrijalva/jwt-go" 7 | ) 8 | 9 | var ( 10 | Secret = constant.Salt //salt 11 | ExpireTime = 3600 * 3 //token expire time 12 | ) 13 | 14 | const ( 15 | ErrorServerBusy = "server is busy" 16 | ErrorReLogin = "relogin" 17 | ) 18 | 19 | type JWTClaims struct { 20 | jwt.StandardClaims 21 | //UserID int `json:"user_id"` 22 | //Password string `json:"password"` 23 | //Username string `json:"username"` 24 | TokenId string `json:"tokenId"` 25 | } 26 | 27 | //generate jwt token 28 | func GenToken(claims *JWTClaims) (string, error) { 29 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 30 | signedToken, err := token.SignedString([]byte(Secret)) 31 | if err != nil { 32 | return "", errors.New(ErrorServerBusy) 33 | } 34 | return signedToken, nil 35 | } 36 | 37 | //验证jwt token 38 | func VerifyAction(strToken string) (*JWTClaims, error) { 39 | token, err := jwt.ParseWithClaims(strToken, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) { 40 | return []byte(Secret), nil 41 | }) 42 | if err != nil { 43 | return nil, errors.New(ErrorServerBusy) 44 | } 45 | claims, ok := token.Claims.(*JWTClaims) 46 | if !ok { 47 | return nil, errors.New(ErrorReLogin) 48 | } 49 | if err := token.Claims.Valid(); err != nil { 50 | return nil, errors.New(ErrorReLogin) 51 | } 52 | return claims, nil 53 | } 54 | -------------------------------------------------------------------------------- /util/mail/mail.go: -------------------------------------------------------------------------------- 1 | package mail 2 | 3 | import ( 4 | "fmt" 5 | "gopkg.in/gomail.v2" 6 | "strings" 7 | ) 8 | 9 | type Options struct { 10 | MailHost string 11 | MailPort int 12 | MailUser string // 发件人 13 | MailPass string // 发件人密码 14 | MailTo string // 收件人 多个用,分割 15 | Subject string // 邮件主题 16 | Body string // 邮件内容 17 | } 18 | 19 | func Send(o *Options) error { 20 | 21 | m := gomail.NewMessage() 22 | 23 | //设置发件人 24 | m.SetHeader("From", o.MailUser) 25 | 26 | //设置发送给多个用户 27 | mailArrTo := strings.Split(o.MailTo, ",") 28 | m.SetHeader("To", mailArrTo...) 29 | 30 | //设置邮件主题 31 | m.SetHeader("Subject", o.Subject) 32 | 33 | //设置邮件正文 34 | m.SetBody("text/html", o.Body) 35 | 36 | d := gomail.NewDialer(o.MailHost, o.MailPort, o.MailUser, o.MailPass) 37 | 38 | err := d.DialAndSend(m) 39 | if err != nil { 40 | fmt.Println(err) 41 | } 42 | return err 43 | } 44 | -------------------------------------------------------------------------------- /util/map_builder/map_builder.go: -------------------------------------------------------------------------------- 1 | package map_builder 2 | 3 | func BuilderMap(kvs ...interface{}) map[string]interface{} { 4 | m := make(map[string]interface{}) 5 | if len(kvs) >= 2 { 6 | for i := 0; i < len(kvs); i += 2 { 7 | m[kvs[i].(string)] = kvs[i+1] 8 | } 9 | } 10 | return m 11 | } 12 | -------------------------------------------------------------------------------- /util/math/math.go: -------------------------------------------------------------------------------- 1 | package math 2 | 3 | import ( 4 | "fmt" 5 | m "math" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // 小数点后 n 位 - 四舍五入 11 | func RoundedFixed(val float64, n int) float64 { 12 | shift := m.Pow(10, float64(n)) 13 | fv := 0.0000000001 + val //对浮点数产生.xxx999999999 计算不准进行处理 14 | return m.Floor(fv*shift+.5) / shift 15 | } 16 | 17 | // 小数点后 n 位 - 舍去 18 | func TruncRound(val float64, n int) float64 { 19 | floatStr := fmt.Sprintf("%."+strconv.Itoa(n+1)+"f", val) 20 | temp := strings.Split(floatStr, ".") 21 | var newFloat string 22 | if len(temp) < 2 || n >= len(temp[1]) { 23 | newFloat = floatStr 24 | } else { 25 | newFloat = temp[0] + "." + temp[1][:n] 26 | } 27 | inst, _ := strconv.ParseFloat(newFloat, 64) 28 | return inst 29 | } 30 | -------------------------------------------------------------------------------- /util/response/response.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | type Gin struct { 6 | Ctx *gin.Context 7 | } 8 | 9 | type Response struct { 10 | Code int `json:"code"` 11 | Message string `json:"msg"` 12 | Data interface{} `json:"data"` 13 | } 14 | 15 | func (g *Gin) Response(code int, msg string, data interface{}) { 16 | g.Ctx.JSON(200, Response{ 17 | Code: code, 18 | Message: msg, 19 | Data: data, 20 | }) 21 | return 22 | } 23 | -------------------------------------------------------------------------------- /util/rsaaaa/gorsa.go: -------------------------------------------------------------------------------- 1 | package gorsa 2 | 3 | import ( 4 | "encoding/base64" 5 | ) 6 | 7 | // 公钥加密 8 | func PublicEncrypt(data, publicKey string) (string, error) { 9 | 10 | grsa := RSASecurity{} 11 | grsa.SetPublicKey(publicKey) 12 | 13 | rsadata, err := grsa.PubKeyENCTYPT([]byte(data)) 14 | if err != nil { 15 | return "", err 16 | } 17 | 18 | return base64.StdEncoding.EncodeToString(rsadata), nil 19 | } 20 | 21 | // 私钥加密 22 | func PriKeyEncrypt(data, privateKey string) (string, error) { 23 | 24 | grsa := RSASecurity{} 25 | grsa.SetPrivateKey(privateKey) 26 | 27 | rsadata, err := grsa.PriKeyENCTYPT([]byte(data)) 28 | if err != nil { 29 | return "", err 30 | } 31 | 32 | return base64.StdEncoding.EncodeToString(rsadata), nil 33 | } 34 | 35 | // 公钥解密 36 | func PublicDecrypt(data, publicKey string) (string, error) { 37 | 38 | databs, _ := base64.StdEncoding.DecodeString(data) 39 | 40 | grsa := RSASecurity{} 41 | grsa.SetPublicKey(publicKey) 42 | 43 | rsadata, err := grsa.PubKeyDECRYPT([]byte(databs)) 44 | if err != nil { 45 | return "", err 46 | } 47 | 48 | return string(rsadata), nil 49 | 50 | } 51 | 52 | // 私钥解密 53 | func PriKeyDecrypt(data, privateKey string) (string, error) { 54 | 55 | databs, _ := base64.StdEncoding.DecodeString(data) 56 | 57 | grsa := RSASecurity{} 58 | grsa.SetPrivateKey(privateKey) 59 | 60 | rsadata, err := grsa.PriKeyDECRYPT([]byte(databs)) 61 | if err != nil { 62 | return "", err 63 | } 64 | 65 | return string(rsadata), nil 66 | } 67 | -------------------------------------------------------------------------------- /util/rsaaaa/gorsaSign.go: -------------------------------------------------------------------------------- 1 | package gorsa 2 | 3 | // 使用RSAWithSHA1算法签名 4 | func SignSha1WithRsa(data string, privateKey string) (string, error) { 5 | grsa := RSASecurity{} 6 | grsa.SetPrivateKey(privateKey) 7 | 8 | sign, err := grsa.SignSha1WithRsa(data) 9 | if err != nil { 10 | return "", err 11 | } 12 | 13 | return sign, err 14 | } 15 | 16 | // 使用RSAWithSHA256算法签名 17 | func SignSha256WithRsa(data string, privateKey string) (string, error) { 18 | grsa := RSASecurity{} 19 | grsa.SetPrivateKey(privateKey) 20 | 21 | sign, err := grsa.SignSha256WithRsa(data) 22 | if err != nil { 23 | return "", err 24 | } 25 | return sign, err 26 | } 27 | 28 | // 使用RSAWithSHA1验证签名 29 | func VerifySignSha1WithRsa(data string, signData string, publicKey string) error { 30 | grsa := RSASecurity{} 31 | grsa.SetPublicKey(publicKey) 32 | return grsa.VerifySignSha1WithRsa(data, signData) 33 | } 34 | 35 | // 使用RSAWithSHA256验证签名 36 | func VerifySignSha256WithRsa(data string, signData string, publicKey string) error { 37 | grsa := RSASecurity{} 38 | grsa.SetPublicKey(publicKey) 39 | return grsa.VerifySignSha256WithRsa(data, signData) 40 | } 41 | -------------------------------------------------------------------------------- /util/rsaaaa/rsa.go: -------------------------------------------------------------------------------- 1 | package gorsa 2 | 3 | import ( 4 | "bytes" 5 | "crypto" 6 | "crypto/rand" 7 | "crypto/rsa" 8 | "crypto/sha1" 9 | "crypto/sha256" 10 | "encoding/base64" 11 | "errors" 12 | "io/ioutil" 13 | ) 14 | 15 | var RSA = &RSASecurity{} 16 | 17 | type RSASecurity struct { 18 | pubStr string //公钥字符串 19 | priStr string //私钥字符串 20 | pubkey *rsa.PublicKey //公钥 21 | prikey *rsa.PrivateKey //私钥 22 | } 23 | 24 | // 设置公钥 25 | func (rsas *RSASecurity) SetPublicKey(pubStr string) (err error) { 26 | rsas.pubStr = pubStr 27 | rsas.pubkey, err = rsas.GetPublickey() 28 | return err 29 | } 30 | 31 | // 设置私钥 32 | func (rsas *RSASecurity) SetPrivateKey(priStr string) (err error) { 33 | rsas.priStr = priStr 34 | rsas.prikey, err = rsas.GetPrivatekey() 35 | return err 36 | } 37 | 38 | // *rsa.PublicKey 39 | func (rsas *RSASecurity) GetPrivatekey() (*rsa.PrivateKey, error) { 40 | return getPriKey([]byte(rsas.priStr)) 41 | } 42 | 43 | // *rsa.PrivateKey 44 | func (rsas *RSASecurity) GetPublickey() (*rsa.PublicKey, error) { 45 | return getPubKey([]byte(rsas.pubStr)) 46 | } 47 | 48 | // 公钥加密 49 | func (rsas *RSASecurity) PubKeyENCTYPT(input []byte) ([]byte, error) { 50 | if rsas.pubkey == nil { 51 | return []byte(""), errors.New(`Please set the public key in advance`) 52 | } 53 | output := bytes.NewBuffer(nil) 54 | err := pubKeyIO(rsas.pubkey, bytes.NewReader(input), output, true) 55 | if err != nil { 56 | return []byte(""), err 57 | } 58 | return ioutil.ReadAll(output) 59 | } 60 | 61 | // 公钥解密 62 | func (rsas *RSASecurity) PubKeyDECRYPT(input []byte) ([]byte, error) { 63 | if rsas.pubkey == nil { 64 | return []byte(""), errors.New(`Please set the public key in advance`) 65 | } 66 | output := bytes.NewBuffer(nil) 67 | err := pubKeyIO(rsas.pubkey, bytes.NewReader(input), output, false) 68 | if err != nil { 69 | return []byte(""), err 70 | } 71 | return ioutil.ReadAll(output) 72 | } 73 | 74 | // 私钥加密 75 | func (rsas *RSASecurity) PriKeyENCTYPT(input []byte) ([]byte, error) { 76 | if rsas.prikey == nil { 77 | return []byte(""), errors.New(`Please set the private key in advance`) 78 | } 79 | output := bytes.NewBuffer(nil) 80 | err := priKeyIO(rsas.prikey, bytes.NewReader(input), output, true) 81 | if err != nil { 82 | return []byte(""), err 83 | } 84 | return ioutil.ReadAll(output) 85 | } 86 | 87 | // 私钥解密 88 | func (rsas *RSASecurity) PriKeyDECRYPT(input []byte) ([]byte, error) { 89 | if rsas.prikey == nil { 90 | return []byte(""), errors.New(`Please set the private key in advance`) 91 | } 92 | output := bytes.NewBuffer(nil) 93 | err := priKeyIO(rsas.prikey, bytes.NewReader(input), output, false) 94 | if err != nil { 95 | return []byte(""), err 96 | } 97 | 98 | return ioutil.ReadAll(output) 99 | } 100 | 101 | /** 102 | * 使用RSAWithSHA1算法签名 103 | */ 104 | func (rsas *RSASecurity) SignSha1WithRsa(data string) (string, error) { 105 | sha1Hash := sha1.New() 106 | s_data := []byte(data) 107 | sha1Hash.Write(s_data) 108 | hashed := sha1Hash.Sum(nil) 109 | 110 | signByte, err := rsa.SignPKCS1v15(rand.Reader, rsas.prikey, crypto.SHA1, hashed) 111 | sign := base64.StdEncoding.EncodeToString(signByte) 112 | return string(sign), err 113 | } 114 | 115 | /** 116 | * 使用RSAWithSHA256算法签名 117 | */ 118 | func (rsas *RSASecurity) SignSha256WithRsa(data string) (string, error) { 119 | sha256Hash := sha256.New() 120 | s_data := []byte(data) 121 | sha256Hash.Write(s_data) 122 | hashed := sha256Hash.Sum(nil) 123 | 124 | signByte, err := rsa.SignPKCS1v15(rand.Reader, rsas.prikey, crypto.SHA256, hashed) 125 | sign := base64.StdEncoding.EncodeToString(signByte) 126 | return string(sign), err 127 | } 128 | 129 | /** 130 | * 使用RSAWithSHA1验证签名 131 | */ 132 | func (rsas *RSASecurity) VerifySignSha1WithRsa(data string, signData string) error { 133 | sign, err := base64.StdEncoding.DecodeString(signData) 134 | if err != nil { 135 | return err 136 | } 137 | hash := sha1.New() 138 | hash.Write([]byte(data)) 139 | return rsa.VerifyPKCS1v15(rsas.pubkey, crypto.SHA1, hash.Sum(nil), sign) 140 | } 141 | 142 | /** 143 | * 使用RSAWithSHA256验证签名 144 | */ 145 | func (rsas *RSASecurity) VerifySignSha256WithRsa(data string, signData string) error { 146 | sign, err := base64.StdEncoding.DecodeString(signData) 147 | if err != nil { 148 | return err 149 | } 150 | hash := sha256.New() 151 | hash.Write([]byte(data)) 152 | 153 | return rsa.VerifyPKCS1v15(rsas.pubkey, crypto.SHA256, hash.Sum(nil), sign) 154 | } 155 | -------------------------------------------------------------------------------- /util/rsaaaa/rsa_ext.go: -------------------------------------------------------------------------------- 1 | package gorsa 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "crypto/rsa" 7 | "crypto/x509" 8 | "encoding/pem" 9 | "errors" 10 | "io" 11 | "io/ioutil" 12 | "math/big" 13 | ) 14 | 15 | var ( 16 | ErrDataToLarge = errors.New("message too long for RSA public key size") 17 | ErrDataLen = errors.New("data length error") 18 | ErrDataBroken = errors.New("data broken, first byte is not zero") 19 | ErrKeyPairDismatch = errors.New("data is not encrypted by the private key") 20 | ErrDecryption = errors.New("decryption error") 21 | ErrPublicKey = errors.New("get public key error") 22 | ErrPrivateKey = errors.New("get private key error") 23 | ) 24 | 25 | // 设置公钥 26 | func getPubKey(publickey []byte) (*rsa.PublicKey, error) { 27 | // decode public key 28 | block, _ := pem.Decode(publickey) 29 | if block == nil { 30 | return nil, errors.New("get public key error") 31 | } 32 | // x509 parse public key 33 | pub, err := x509.ParsePKIXPublicKey(block.Bytes) 34 | if err != nil { 35 | return nil, err 36 | } 37 | return pub.(*rsa.PublicKey), err 38 | } 39 | 40 | // 设置私钥 41 | func getPriKey(privatekey []byte) (*rsa.PrivateKey, error) { 42 | block, _ := pem.Decode(privatekey) 43 | if block == nil { 44 | return nil, errors.New("get private key error") 45 | } 46 | pri, err := x509.ParsePKCS1PrivateKey(block.Bytes) 47 | if err == nil { 48 | return pri, nil 49 | } 50 | pri2, err := x509.ParsePKCS8PrivateKey(block.Bytes) 51 | if err != nil { 52 | return nil, err 53 | } 54 | return pri2.(*rsa.PrivateKey), nil 55 | } 56 | 57 | // 公钥加密或解密byte 58 | func pubKeyByte(pub *rsa.PublicKey, in []byte, isEncrytp bool) ([]byte, error) { 59 | k := (pub.N.BitLen() + 7) / 8 60 | if isEncrytp { 61 | k = k - 11 62 | } 63 | if len(in) <= k { 64 | if isEncrytp { 65 | return rsa.EncryptPKCS1v15(rand.Reader, pub, in) 66 | } else { 67 | return pubKeyDecrypt(pub, in) 68 | } 69 | } else { 70 | iv := make([]byte, k) 71 | out := bytes.NewBuffer(iv) 72 | if err := pubKeyIO(pub, bytes.NewReader(in), out, isEncrytp); err != nil { 73 | return nil, err 74 | } 75 | return ioutil.ReadAll(out) 76 | } 77 | } 78 | 79 | // 私钥加密或解密byte 80 | func priKeyByte(pri *rsa.PrivateKey, in []byte, isEncrytp bool) ([]byte, error) { 81 | k := (pri.N.BitLen() + 7) / 8 82 | if isEncrytp { 83 | k = k - 11 84 | } 85 | if len(in) <= k { 86 | if isEncrytp { 87 | return priKeyEncrypt(rand.Reader, pri, in) 88 | } else { 89 | return rsa.DecryptPKCS1v15(rand.Reader, pri, in) 90 | } 91 | } else { 92 | iv := make([]byte, k) 93 | out := bytes.NewBuffer(iv) 94 | if err := priKeyIO(pri, bytes.NewReader(in), out, isEncrytp); err != nil { 95 | return nil, err 96 | } 97 | return ioutil.ReadAll(out) 98 | } 99 | } 100 | 101 | // 公钥加密或解密Reader 102 | func pubKeyIO(pub *rsa.PublicKey, in io.Reader, out io.Writer, isEncrytp bool) (err error) { 103 | k := (pub.N.BitLen() + 7) / 8 104 | if isEncrytp { 105 | k = k - 11 106 | } 107 | buf := make([]byte, k) 108 | var b []byte 109 | size := 0 110 | for { 111 | size, err = in.Read(buf) 112 | if err != nil { 113 | if err == io.EOF { 114 | return nil 115 | } 116 | return err 117 | } 118 | if size < k { 119 | b = buf[:size] 120 | } else { 121 | b = buf 122 | } 123 | if isEncrytp { 124 | b, err = rsa.EncryptPKCS1v15(rand.Reader, pub, b) 125 | } else { 126 | b, err = pubKeyDecrypt(pub, b) 127 | } 128 | if err != nil { 129 | return err 130 | } 131 | if _, err = out.Write(b); err != nil { 132 | return err 133 | } 134 | } 135 | return nil 136 | } 137 | 138 | // 私钥加密或解密Reader 139 | func priKeyIO(pri *rsa.PrivateKey, r io.Reader, w io.Writer, isEncrytp bool) (err error) { 140 | k := (pri.N.BitLen() + 7) / 8 141 | if isEncrytp { 142 | k = k - 11 143 | } 144 | buf := make([]byte, k) 145 | var b []byte 146 | size := 0 147 | for { 148 | size, err = r.Read(buf) 149 | if err != nil { 150 | if err == io.EOF { 151 | return nil 152 | } 153 | return err 154 | } 155 | if size < k { 156 | b = buf[:size] 157 | } else { 158 | b = buf 159 | } 160 | if isEncrytp { 161 | b, err = priKeyEncrypt(rand.Reader, pri, b) 162 | } else { 163 | b, err = rsa.DecryptPKCS1v15(rand.Reader, pri, b) 164 | } 165 | if err != nil { 166 | return err 167 | } 168 | if _, err = w.Write(b); err != nil { 169 | return err 170 | } 171 | } 172 | return nil 173 | } 174 | 175 | // 公钥解密 176 | func pubKeyDecrypt(pub *rsa.PublicKey, data []byte) ([]byte, error) { 177 | k := (pub.N.BitLen() + 7) / 8 178 | if k != len(data) { 179 | return nil, ErrDataLen 180 | } 181 | m := new(big.Int).SetBytes(data) 182 | if m.Cmp(pub.N) > 0 { 183 | return nil, ErrDataToLarge 184 | } 185 | m.Exp(m, big.NewInt(int64(pub.E)), pub.N) 186 | d := leftPad(m.Bytes(), k) 187 | if d[0] != 0 { 188 | return nil, ErrDataBroken 189 | } 190 | if d[1] != 0 && d[1] != 1 { 191 | return nil, ErrKeyPairDismatch 192 | } 193 | var i = 2 194 | for ; i < len(d); i++ { 195 | if d[i] == 0 { 196 | break 197 | } 198 | } 199 | i++ 200 | if i == len(d) { 201 | return nil, nil 202 | } 203 | return d[i:], nil 204 | } 205 | 206 | // 私钥加密 207 | func priKeyEncrypt(rand io.Reader, priv *rsa.PrivateKey, hashed []byte) ([]byte, error) { 208 | tLen := len(hashed) 209 | k := (priv.N.BitLen() + 7) / 8 210 | if k < tLen+11 { 211 | return nil, ErrDataLen 212 | } 213 | em := make([]byte, k) 214 | em[1] = 1 215 | for i := 2; i < k-tLen-1; i++ { 216 | em[i] = 0xff 217 | } 218 | copy(em[k-tLen:k], hashed) 219 | m := new(big.Int).SetBytes(em) 220 | c, err := decrypt(rand, priv, m) 221 | if err != nil { 222 | return nil, err 223 | } 224 | copyWithLeftPad(em, c.Bytes()) 225 | return em, nil 226 | } 227 | 228 | // 从crypto/rsa复制 229 | var bigZero = big.NewInt(0) 230 | var bigOne = big.NewInt(1) 231 | 232 | // 从crypto/rsa复制 233 | func encrypt(c *big.Int, pub *rsa.PublicKey, m *big.Int) *big.Int { 234 | e := big.NewInt(int64(pub.E)) 235 | c.Exp(m, e, pub.N) 236 | return c 237 | } 238 | 239 | // 从crypto/rsa复制 240 | func decrypt(random io.Reader, priv *rsa.PrivateKey, c *big.Int) (m *big.Int, err error) { 241 | if c.Cmp(priv.N) > 0 { 242 | err = ErrDecryption 243 | return 244 | } 245 | var ir *big.Int 246 | if random != nil { 247 | var r *big.Int 248 | 249 | for { 250 | r, err = rand.Int(random, priv.N) 251 | if err != nil { 252 | return 253 | } 254 | if r.Cmp(bigZero) == 0 { 255 | r = bigOne 256 | } 257 | var ok bool 258 | ir, ok = modInverse(r, priv.N) 259 | if ok { 260 | break 261 | } 262 | } 263 | bigE := big.NewInt(int64(priv.E)) 264 | rpowe := new(big.Int).Exp(r, bigE, priv.N) 265 | cCopy := new(big.Int).Set(c) 266 | cCopy.Mul(cCopy, rpowe) 267 | cCopy.Mod(cCopy, priv.N) 268 | c = cCopy 269 | } 270 | if priv.Precomputed.Dp == nil { 271 | m = new(big.Int).Exp(c, priv.D, priv.N) 272 | } else { 273 | m = new(big.Int).Exp(c, priv.Precomputed.Dp, priv.Primes[0]) 274 | m2 := new(big.Int).Exp(c, priv.Precomputed.Dq, priv.Primes[1]) 275 | m.Sub(m, m2) 276 | if m.Sign() < 0 { 277 | m.Add(m, priv.Primes[0]) 278 | } 279 | m.Mul(m, priv.Precomputed.Qinv) 280 | m.Mod(m, priv.Primes[0]) 281 | m.Mul(m, priv.Primes[1]) 282 | m.Add(m, m2) 283 | 284 | for i, values := range priv.Precomputed.CRTValues { 285 | prime := priv.Primes[2+i] 286 | m2.Exp(c, values.Exp, prime) 287 | m2.Sub(m2, m) 288 | m2.Mul(m2, values.Coeff) 289 | m2.Mod(m2, prime) 290 | if m2.Sign() < 0 { 291 | m2.Add(m2, prime) 292 | } 293 | m2.Mul(m2, values.R) 294 | m.Add(m, m2) 295 | } 296 | } 297 | if ir != nil { 298 | m.Mul(m, ir) 299 | m.Mod(m, priv.N) 300 | } 301 | 302 | return 303 | } 304 | 305 | // 从crypto/rsa复制 306 | func copyWithLeftPad(dest, src []byte) { 307 | numPaddingBytes := len(dest) - len(src) 308 | for i := 0; i < numPaddingBytes; i++ { 309 | dest[i] = 0 310 | } 311 | copy(dest[numPaddingBytes:], src) 312 | } 313 | 314 | // 从crypto/rsa复制 315 | func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) { 316 | _, err = io.ReadFull(rand, s) 317 | if err != nil { 318 | return 319 | } 320 | for i := 0; i < len(s); i++ { 321 | for s[i] == 0 { 322 | _, err = io.ReadFull(rand, s[i:i+1]) 323 | if err != nil { 324 | return 325 | } 326 | s[i] ^= 0x42 327 | } 328 | } 329 | return 330 | } 331 | 332 | // 从crypto/rsa复制 333 | func leftPad(input []byte, size int) (out []byte) { 334 | n := len(input) 335 | if n > size { 336 | n = size 337 | } 338 | out = make([]byte, size) 339 | copy(out[len(out)-n:], input) 340 | return 341 | } 342 | 343 | // 从crypto/rsa复制 344 | func modInverse(a, n *big.Int) (ia *big.Int, ok bool) { 345 | g := new(big.Int) 346 | x := new(big.Int) 347 | y := new(big.Int) 348 | g.GCD(x, y, a, n) 349 | if g.Cmp(bigOne) != 0 { 350 | return 351 | } 352 | if x.Cmp(bigOne) < 0 { 353 | x.Add(x, n) 354 | } 355 | return x, true 356 | } 357 | -------------------------------------------------------------------------------- /util/time/time.go: -------------------------------------------------------------------------------- 1 | package time 2 | 3 | import "time" 4 | 5 | // 获取当前的时间 - 字符串 6 | func GetCurrentDate() string { 7 | return time.Now().Format("2006/01/02 15:04:05") 8 | } 9 | 10 | // 获取当前的时间 - Unix时间戳 11 | func GetCurrentUnix() int64 { 12 | return time.Now().Unix() 13 | } 14 | 15 | // 获取当前的时间 - 毫秒级时间戳 16 | func GetCurrentMilliUnix() int64 { 17 | return time.Now().UnixNano() / 1000000 18 | } 19 | 20 | // 获取当前的时间 - 纳秒级时间戳 21 | func GetCurrentNanoUnix() int64 { 22 | return time.Now().UnixNano() 23 | } 24 | -------------------------------------------------------------------------------- /util/validator/validator.go: -------------------------------------------------------------------------------- 1 | package validator 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | // 判断变量是否为空 8 | func Empty(val interface{}) bool { 9 | if val == nil { 10 | return true 11 | } 12 | 13 | v := reflect.ValueOf(val) 14 | switch v.Kind() { 15 | case reflect.String, reflect.Array: 16 | return v.Len() == 0 17 | case reflect.Map, reflect.Slice: 18 | return v.Len() == 0 || v.IsNil() 19 | case reflect.Bool: 20 | return !v.Bool() 21 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 22 | return v.Int() == 0 23 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 24 | return v.Uint() == 0 25 | case reflect.Float32, reflect.Float64: 26 | return v.Float() == 0 27 | case reflect.Interface, reflect.Ptr: 28 | return v.IsNil() 29 | } 30 | 31 | return reflect.DeepEqual(val, reflect.Zero(v.Type()).Interface()) 32 | } 33 | --------------------------------------------------------------------------------