├── LICENSE ├── README.md ├── README ├── globalmessage.MD └── tokenmessage.MD ├── app.conf ├── config ├── appconfig.go └── appconfigmodel.go ├── const ├── logdefine.go └── redisdefine.go ├── framework ├── crypto │ └── cryptos.go ├── exception │ └── exception.go ├── file │ └── fileutil.go ├── http │ └── httputil.go ├── json │ └── jsonutil.go ├── log │ ├── innerlogger.go │ └── logger.go ├── redis │ └── redisutil.go ├── task │ └── tasks.go └── times │ └── times.go ├── httpserver ├── handlers │ ├── global │ │ └── golbalHandler.go │ └── token │ │ └── tokenhandler.go ├── model │ └── response.go ├── router.go └── server.go ├── main.go └── version.log /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 devfeel pzrr@qq.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TokenServer 2 | token服务,提供token一致性服务以及相关的全局ID生成服务等 3 | 4 | ##API列表 5 | * Create Token 6 | * Query Token 7 | * Verify Token 8 | * Create GlobalID 9 | 10 | ##配置说明 11 | ``` 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ``` 22 | 23 | ##运行说明 24 | 假设执行文件安装在/home/devfeel/tokenserver/目录下: 25 |
26 | * tokenserver    可执行文件 27 | * innerlogs      程序内部日志目录 28 | * logs           程序业务日志目录 29 | * app.conf       程序配置文件 30 | 31 | *默认配置下,会监听两个端口,一个为业务端口,一个为pprof端口 32 | 33 | 34 | ##外部依赖 35 | * mgo - gopkg.in/mgo.v2/bson 36 | * redigo - github.com/garyburd/redigo/redis 37 | -------------------------------------------------------------------------------- /README/globalmessage.MD: -------------------------------------------------------------------------------- 1 | ##1、Create GlobalID: 2 | 描述:创建全局ID 3 |
接口: http://127.0.0.1/global/createid?idtype=?&appid=?&module=? 4 |
HttpMethod:Get
5 | 6 | ####Get Param: 7 | * idtype: 代表请求ID方式,目前支持:uuid、number、timenumber、mongodb 8 | * appid: 代表请求的应用 9 | * module: 代表请求应用某特定模块 10 | 11 | ####返回结构:HandlerResponse 12 | * 0: 成功 13 | * -100001:idtype is empty 14 | * -100002:appid & module is empty 15 | * -201001:no config RedisID_Global Redis 16 | * -201002:create global number error [redis] 17 | * Response.Message = GlobalID //string 18 | 19 | ####示例: 20 | * UUID 21 | ``` 22 | curl http://127.0.0.1/global/createid?idtype=uuid&appid=test&module=test 23 | {"RetCode":0,"RetMsg":"ok","Message":"b6b81359c127554ee2a34bb36457026b"} 24 | ``` 25 | * Number 26 | ``` 27 | curl http://127.0.0.1/global/createid?idtype=number&appid=test&module=test 28 | {"RetCode":0,"RetMsg":"","Message":"64"} 29 | ``` 30 | * TimeNumber 31 | ``` 32 | curl http://127.0.0.1/global/createid?idtype=timenumber&appid=test&module=test 33 | {"RetCode":0,"RetMsg":"","Message":"2017021611304200000002"} 34 | ``` 35 | * MongoDB 36 | ``` 37 | curl http://127.0.0.1/global/createid?idtype=mongodb&appid=test&module=test 38 | {"RetCode":0,"RetMsg":"ok","Message":"58abd9dae138236e64ebfdf8"} 39 | ``` 40 | -------------------------------------------------------------------------------- /README/tokenmessage.MD: -------------------------------------------------------------------------------- 1 | ##1、Create Token: 2 | 描述:创建Token 3 |
接口: http://127.0.0.1/token/create 4 |
HttpMethod:Post 5 |
Post Param:TokenInfo 6 |
返回结构:HandlerResponse 7 | * 0: 成功 8 | * -100001:post data not legal 9 | * -100002:AppID is empty 10 | * -200001:no config RedisID_Token Redis 11 | * -200002:create token error [uuid] 12 | * -200003:create token error [redis set] 13 | * Response.Message = TokenInfo 14 | 15 | 其中TokenInfo结构: 16 | ```go 17 | type TokenInfo struct { 18 | Token string 19 | AppID string 20 | TokenBody string 21 | LifeSeconds int //有效时间,单位为秒,如果输入不合法,默认为1800秒 22 | } 23 | ``` 24 | 测试数据: 25 | ``` 26 | { 27 | "Token":"", 28 | "AppID":"10000", 29 | "TokenBody":"1", 30 | "LifeSeconds":1800 31 | } 32 | ``` 33 | 34 | result: 35 | ``` 36 | {"RetCode":0,"RetMsg":"ok[OK]","Message":{"Token":"a8d3400acb3217e10e946d1e0ca107db","AppID":"10000","TokenBody":"1","LifeSeconds":30}} 37 | ``` 38 | 39 | ************************************************************************************************** 40 | 41 | ##2、Query Token: 42 | 描述:查询token,不影响token有效性,调用完token依然存在,如需一次性token验证,请调用verify方法 43 |
接口:http://192.168.8.178:8201/token/query?appid=?&token=? 44 |
HttpMethod:Get 45 |
Get Param: 46 | * appid: 应用编码 47 | * token: token 48 |
返回结构:HandlerResponse 49 | * 0: 成功 50 | * -100001:querystring token|appid is empty 51 | * -200001:no config RedisID_Token Redis 52 | * -200002:query token error [redis get] 53 | * Response.Message = TokenInfo 54 | 55 | result: 56 | ``` 57 | {"RetCode":0,"RetMsg":"ok","Message":"{\"Token\":\"a8d3400acb3217e10e946d1e0ca107db\",\"AppID\":\"10000\",\"TokenBody\":\"1\",\"LifeSeconds\":1800}"} 58 | ``` 59 | 其中TokenInfo结构: 60 | ```go 61 | type TokenInfo struct { 62 | Token string 63 | AppID string 64 | TokenBody string 65 | LifeSeconds int //有效时间,单位为秒 66 | } 67 | ``` 68 | 69 | ************************************************************************************************** 70 | 71 | ##3、Verify Token: 72 | 描述:验证token,一次有效,调用完token移除,如需多次验证,请调用query方法 73 |
接口:http://192.168.8.178:8201/token/verify 74 |
HttpMethod:Post 75 |
Post Param:VerifyTokenRequest 76 |
返回结构:HandlerResponse 77 | * 0: 成功 78 | * -100001:post data not legal 79 | * -100002:AppID is empty 80 | * -200001:no config RedisID_Token Redis 81 | * -201001: query token exists error 82 | * -201002: redis token not exists 83 | * -202001: get token-locker error 84 | * -202002: token-locker locked by other 85 | * -203001: query token error 86 | * -203002: redis token data not legal 87 | * -203003: token body is not match 88 | * Response.Message = TokenInfo 89 | 90 | 其中VerifyTokenRequest结构: 91 | ```go 92 | type VerifyTokenRequest struct { 93 | Token string 94 | AppID string 95 | TokenBody string 96 | IsCheckBody bool //是否需要验证Body是否一致 97 | } 98 | ``` 99 | 测试数据: 100 | ``` 101 | { 102 | "Token":"a8d3400acb3217e10e946d1e0ca107db", 103 | "AppID":"10000", 104 | "TokenBody":"1", 105 | "IsCheckBody":true 106 | } 107 | ``` 108 | result: 109 | ``` 110 | {"RetCode":0,"RetMsg":"ok","Message":{"Token":"9d8ccbdd35a61aacadf80ea91e4f90bb","AppID":"10000","TokenBody":"1","LifeSeconds":1800}} 111 | ``` 112 | 113 | 其中TokenInfo结构: 114 | ```go 115 | type TokenInfo struct { 116 | Token string 117 | AppID string 118 | TokenBody string 119 | LifeSeconds int //有效时间,单位为秒 120 | } 121 | ``` 122 | -------------------------------------------------------------------------------- /app.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /config/appconfig.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/xml" 5 | "github.com/devfeel/tokenserver/framework/json" 6 | "github.com/devfeel/tokenserver/framework/log" 7 | "io/ioutil" 8 | "os" 9 | "sync" 10 | ) 11 | 12 | const () 13 | 14 | var ( 15 | CurrentConfig AppConfig 16 | CurrentBaseDir string 17 | innerLogger *logger.InnerLogger 18 | redisMap map[string]*RedisInfo 19 | redisMutex *sync.RWMutex 20 | ) 21 | 22 | func init() { 23 | //初始化读写锁 24 | redisMutex = new(sync.RWMutex) 25 | innerLogger = logger.GetInnerLogger() 26 | } 27 | 28 | func SetBaseDir(baseDir string) { 29 | CurrentBaseDir = baseDir 30 | } 31 | 32 | //初始化配置文件 33 | func InitConfig(configFile string) *AppConfig { 34 | innerLogger.Info("AppConfig::InitConfig 配置文件[" + configFile + "]开始...") 35 | content, err := ioutil.ReadFile(configFile) 36 | if err != nil { 37 | innerLogger.Warn("AppConfig::InitConfig 配置文件[" + configFile + "]无法解析 - " + err.Error()) 38 | os.Exit(1) 39 | } 40 | 41 | var result AppConfig 42 | err = xml.Unmarshal(content, &result) 43 | if err != nil { 44 | innerLogger.Warn("AppConfig::InitConfig 配置文件[" + configFile + "]解析失败 - " + err.Error()) 45 | os.Exit(1) 46 | } 47 | 48 | CurrentConfig = result 49 | 50 | innerLogger.Info("AppConfig::InitConfig Start Load RedisInfo") 51 | //初始化RedisMap 52 | tmpRedisMap := make(map[string]*RedisInfo) 53 | for k, v := range result.Redises { 54 | tmpRedisMap[v.ID] = &result.Redises[k] 55 | innerLogger.Info("AppConfig::InitConfig Load RedisInfo => " + jsonutil.GetJsonString(v)) 56 | } 57 | redisMutex.Lock() 58 | redisMap = tmpRedisMap 59 | redisMutex.Unlock() 60 | innerLogger.Info("AppConfig::InitConfig Finish Load RedisInfo") 61 | 62 | innerLogger.Info("AppConfig::InitConfig 配置文件[" + configFile + "]完成") 63 | return &CurrentConfig 64 | } 65 | 66 | func GetRedisInfo(redisID string) (*RedisInfo, bool) { 67 | redisMutex.RLock() 68 | defer redisMutex.RUnlock() 69 | redis, exists := redisMap[redisID] 70 | return redis, exists 71 | } 72 | -------------------------------------------------------------------------------- /config/appconfigmodel.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/xml" 5 | ) 6 | 7 | //配置信息 8 | type AppConfig struct { 9 | XMLName xml.Name `xml:"config"` 10 | Log Log `xml:"log"` 11 | HttpServer HttpServer `xml:"httpserver"` 12 | Redises []RedisInfo `xml:"redises>redis"` 13 | } 14 | 15 | //全局配置 16 | type HttpServer struct { 17 | HttpPort int `xml:"httpport,attr"` 18 | PProfPort int `xml:"pprofport,attr"` 19 | } 20 | 21 | //log配置 22 | type Log struct { 23 | FilePath string `xml:"filepath,attr"` 24 | } 25 | 26 | //Redis信息 27 | type RedisInfo struct { 28 | ID string `xml:"id,attr"` 29 | ServerIP string `xml:"serverip,attr"` 30 | KeyPre string `xml:"keypre,attr"` 31 | } 32 | -------------------------------------------------------------------------------- /const/logdefine.go: -------------------------------------------------------------------------------- 1 | package constdefine 2 | 3 | const ( 4 | LogTarget_Default = "Default" 5 | LogTarget_Token = "Token" 6 | LogTarget_Global = "Global" 7 | ) 8 | 9 | const ( 10 | LogLevel_Debug = "debug" 11 | LogLevel_Info = "info" 12 | LogLevel_Warn = "warn" 13 | LogLevel_Error = "error" 14 | ) 15 | -------------------------------------------------------------------------------- /const/redisdefine.go: -------------------------------------------------------------------------------- 1 | package constdefine 2 | 3 | //RedisID定义 4 | const ( 5 | RedisID_Token = "tokenredis" 6 | RedisID_Global = "globalredis" 7 | ) 8 | -------------------------------------------------------------------------------- /framework/crypto/cryptos.go: -------------------------------------------------------------------------------- 1 | package cryptos 2 | 3 | import ( 4 | "crypto/md5" 5 | "crypto/rand" 6 | "encoding/base64" 7 | "encoding/hex" 8 | "io" 9 | ) 10 | 11 | //获取MD5值 12 | func GetMd5String(s string) string { 13 | h := md5.New() 14 | h.Write([]byte(s)) 15 | return hex.EncodeToString(h.Sum(nil)) 16 | } 17 | 18 | //获取guid 19 | func GetGuid() string { 20 | b := make([]byte, 48) 21 | 22 | if _, err := io.ReadFull(rand.Reader, b); err != nil { 23 | return "" 24 | } 25 | return GetMd5String(base64.URLEncoding.EncodeToString(b)) 26 | } 27 | -------------------------------------------------------------------------------- /framework/exception/exception.go: -------------------------------------------------------------------------------- 1 | package exception 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "runtime" 7 | 8 | "github.com/devfeel/tokenserver/framework/log" 9 | ) 10 | 11 | //统一异常处理 12 | func CatchError(title string, logtarget string, loglevel string, err interface{}) (errmsg string) { 13 | errmsg = fmt.Sprintln(err) 14 | os.Stdout.Write([]byte(title + " error! => " + errmsg + " => ")) 15 | buf := make([]byte, 4096) 16 | n := runtime.Stack(buf, true) 17 | logger.Log(title+" error! => "+errmsg+" => "+string(buf[:n]), logtarget, loglevel) 18 | return errmsg 19 | } 20 | -------------------------------------------------------------------------------- /framework/file/fileutil.go: -------------------------------------------------------------------------------- 1 | package fileutil 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | ) 9 | 10 | func GetCurrentDirectory() string { 11 | dir, err := filepath.Abs(filepath.Dir(os.Args[0])) 12 | if err != nil { 13 | log.Fatalln(err) 14 | os.Exit(1) 15 | } 16 | return strings.Replace(dir, "\\", "/", -1) 17 | } 18 | -------------------------------------------------------------------------------- /framework/http/httputil.go: -------------------------------------------------------------------------------- 1 | // httpHelper 2 | package httputil 3 | 4 | import ( 5 | "bytes" 6 | "fmt" 7 | "io/ioutil" 8 | "net" 9 | "net/http" 10 | "net/url" 11 | "os" 12 | "path/filepath" 13 | "strings" 14 | "time" 15 | ) 16 | 17 | //定义设置了超时时间的httpclient 18 | var currClient *http.Client = &http.Client{ 19 | Transport: &http.Transport{ 20 | Dial: func(netw, addr string) (net.Conn, error) { 21 | c, err := net.DialTimeout(netw, addr, time.Second*300) 22 | if err != nil { 23 | fmt.Println("dail timeout", err) 24 | return nil, err 25 | } 26 | return c, nil 27 | }, 28 | MaxIdleConnsPerHost: 10, 29 | ResponseHeaderTimeout: time.Second * 200, 30 | }, 31 | } 32 | 33 | func HttpGet(url string) (body string, contentType string, intervalTime int64, errReturn error) { 34 | startTime := time.Now() 35 | intervalTime = 0 36 | contentType = "" 37 | body = "" 38 | errReturn = nil 39 | 40 | resp, err := currClient.Get(url) 41 | if err != nil { 42 | intervalTime = int64(time.Now().Sub(startTime) / time.Millisecond) 43 | errReturn = err 44 | return 45 | } 46 | defer resp.Body.Close() 47 | 48 | bytebody, err := ioutil.ReadAll(resp.Body) 49 | intervalTime = int64(time.Now().Sub(startTime) / time.Millisecond) 50 | if err != nil { 51 | intervalTime = int64(time.Now().Sub(startTime) / time.Millisecond) 52 | errReturn = err 53 | return 54 | } 55 | body = string(bytebody) 56 | contentType = resp.Header.Get("Content-Type") 57 | intervalTime = int64(time.Now().Sub(startTime) / time.Millisecond) 58 | return 59 | } 60 | 61 | func HttpPost(url string, postbody string, bodyType string) (body string, contentType string, intervalTime int64, errReturn error) { 62 | startTime := time.Now() 63 | intervalTime = 0 64 | contentType = "" 65 | body = "" 66 | errReturn = nil 67 | postbytes := bytes.NewBuffer([]byte(postbody)) 68 | //resp, err := currClient.Post(url, "application/x-www-form-urlencoded", postbytes) 69 | //resp, err := currClient.Post(url, "application/json", postbytes) 70 | if bodyType == "" { 71 | bodyType = "application/x-www-form-urlencoded" 72 | } 73 | resp, err := currClient.Post(url, bodyType, postbytes) 74 | if err != nil { 75 | intervalTime = int64(time.Now().Sub(startTime) / time.Millisecond) 76 | errReturn = err 77 | return 78 | } 79 | defer resp.Body.Close() 80 | 81 | bytebody, err := ioutil.ReadAll(resp.Body) 82 | if err != nil { 83 | intervalTime = int64(time.Now().Sub(startTime) / time.Millisecond) 84 | errReturn = err 85 | return 86 | } 87 | body = string(bytebody) 88 | contentType = resp.Header.Get("Content-Type") 89 | intervalTime = int64(time.Now().Sub(startTime) / time.Millisecond) 90 | return 91 | 92 | } 93 | 94 | //从指定query集合获取指定key的值 95 | func GetQuery(querys url.Values, key string) string { 96 | if len(querys[key]) > 0 { 97 | return querys[key][0] 98 | } 99 | return "" 100 | } 101 | 102 | //获取当前目录 103 | func GetCurrentDirectory() (string, error) { 104 | dir, err := filepath.Abs(filepath.Dir(os.Args[0])) 105 | if err != nil { 106 | return "", err 107 | } 108 | return strings.Replace(dir, "\\", "/", -1), nil 109 | } 110 | -------------------------------------------------------------------------------- /framework/json/jsonutil.go: -------------------------------------------------------------------------------- 1 | package jsonutil 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | //将传入对象转换为json字符串 8 | func GetJsonString(obj interface{}) string { 9 | resByte, err := json.Marshal(obj) 10 | if err != nil { 11 | return "" 12 | } 13 | return string(resByte) 14 | } 15 | 16 | //将传入对象转换为json字符串 17 | func Marshal(v interface{}) (string, error) { 18 | resByte, err := json.Marshal(v) 19 | if err != nil { 20 | return "", err 21 | } else { 22 | return string(resByte), nil 23 | } 24 | } 25 | 26 | //将传入的json字符串转换为对象 27 | func Unmarshal(jsonstring string, v interface{}) error { 28 | return json.Unmarshal([]byte(jsonstring), v) 29 | } 30 | -------------------------------------------------------------------------------- /framework/log/innerlogger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "syscall" 7 | "time" 8 | ) 9 | 10 | var innerLogRootPath string 11 | 12 | type InnerLogger struct { 13 | } 14 | 15 | var singleInnerLogger *InnerLogger 16 | 17 | func GetInnerLogger() *InnerLogger { 18 | if singleInnerLogger == nil { 19 | singleInnerLogger = new(InnerLogger) 20 | } 21 | return singleInnerLogger 22 | } 23 | 24 | func (logger *InnerLogger) Debug(log string) { 25 | logger.innerWriteLog(log, "debug") 26 | } 27 | 28 | func (logger *InnerLogger) Info(log string) { 29 | logger.innerWriteLog(log, "info") 30 | } 31 | 32 | func (logger *InnerLogger) Warn(log string) { 33 | logger.innerWriteLog(log, "warn") 34 | } 35 | 36 | func (logger *InnerLogger) Error(log string) { 37 | logger.innerWriteLog(log, "error") 38 | } 39 | 40 | //开启日志处理器 41 | func StartInnerLogHandler(rootPath string) { 42 | //设置日志根目录 43 | innerLogRootPath = rootPath 44 | if !strings.HasSuffix(innerLogRootPath, "/") { 45 | innerLogRootPath = innerLogRootPath + "/" 46 | } 47 | 48 | singleInnerLogger = new(InnerLogger) 49 | } 50 | 51 | func (logger *InnerLogger) innerWriteLog(log string, level string) { 52 | filePath := innerLogRootPath + "/innerlogs/" 53 | switch level { 54 | case "debug": 55 | filePath = filePath + "innerlog_debug_" + time.Now().Format(defaultDateFormatForFileName) + ".log" 56 | case "info": 57 | filePath = filePath + "innerlog_info_" + time.Now().Format(defaultDateFormatForFileName) + ".log" 58 | case "warn": 59 | filePath = filePath + "innerlog_warn_" + time.Now().Format(defaultDateFormatForFileName) + ".log" 60 | case "error": 61 | filePath = filePath + "innerlog_error_" + time.Now().Format(defaultDateFormatForFileName) + ".log" 62 | break 63 | } 64 | log = time.Now().Format(defaultFullTimeLayout) + " " + log 65 | logger.innerWriteFile(filePath, log) 66 | } 67 | 68 | func (logger *InnerLogger) innerWriteFile(logFile string, log string) { 69 | var mode os.FileMode 70 | flag := syscall.O_RDWR | syscall.O_APPEND | syscall.O_CREAT 71 | mode = 0666 72 | logstr := log + "\r\n" 73 | file, err := os.OpenFile(logFile, flag, mode) 74 | defer file.Close() 75 | if err != nil { 76 | return 77 | } 78 | file.WriteString(logstr) 79 | } 80 | -------------------------------------------------------------------------------- /framework/log/logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | "syscall" 8 | "time" 9 | ) 10 | 11 | type ChanLog struct { 12 | Content string 13 | LogTarget string 14 | } 15 | 16 | const ( 17 | defaultDateFormatForFileName = "2006_01_02" 18 | defaultDateLayout = "2006-01-02" 19 | defaultFullTimeLayout = "2006-01-02 15:04:05.999999" 20 | defaultTimeLayout = "2006-01-02 15:04:05" 21 | ) 22 | 23 | type Logger struct { 24 | } 25 | 26 | var singleLogger *Logger 27 | 28 | func GetLogger() *Logger { 29 | if singleLogger == nil { 30 | singleLogger = new(Logger) 31 | } 32 | return singleLogger 33 | } 34 | 35 | var ( 36 | logChan_Custom chan ChanLog 37 | ) 38 | 39 | var ( 40 | logRootPath string 41 | ) 42 | 43 | func init() { 44 | logChan_Custom = make(chan ChanLog, 10000) 45 | singleLogger = new(Logger) 46 | } 47 | 48 | func Debug(log string, logTarget string) { 49 | singleLogger.Log(log, logTarget, "debug") 50 | } 51 | 52 | func Info(log string, logTarget string) { 53 | singleLogger.Log(log, logTarget, "info") 54 | } 55 | 56 | func Warn(log string, logTarget string) { 57 | singleLogger.Log(log, logTarget, "warn") 58 | } 59 | 60 | func Error(log string, logTarget string) { 61 | singleLogger.Log(log, logTarget, "error") 62 | } 63 | 64 | func Log(log string, logTarget string, logLevel string) { 65 | singleLogger.Log(log, logTarget, logLevel) 66 | } 67 | 68 | func (logger *Logger) Debug(log string, logTarget string) { 69 | logger.Log(log, logTarget, "debug") 70 | } 71 | 72 | func (logger *Logger) Info(log string, logTarget string) { 73 | logger.Log(log, logTarget, "info") 74 | } 75 | 76 | func (logger *Logger) Warn(log string, logTarget string) { 77 | logger.Log(log, logTarget, "warn") 78 | } 79 | 80 | func (logger *Logger) Error(log string, logTarget string) { 81 | logger.Log(log, logTarget, "error") 82 | } 83 | 84 | func (logger *Logger) Log(log string, logTarget string, logLevel string) { 85 | chanLog := ChanLog{ 86 | LogTarget: logTarget + "_" + logLevel, 87 | Content: log, 88 | } 89 | logChan_Custom <- chanLog 90 | } 91 | 92 | //开启日志处理器 93 | func StartLogHandler(rootPath string) { 94 | //设置日志根目录 95 | logRootPath = rootPath 96 | if !strings.HasSuffix(logRootPath, "/") { 97 | logRootPath = logRootPath + "/" 98 | } 99 | go handleCustom() 100 | } 101 | 102 | //处理日志内部函数 103 | func handleCustom() { 104 | for { 105 | log := <-logChan_Custom 106 | writeLog(log, "custom") 107 | } 108 | } 109 | 110 | func writeLog(chanLog ChanLog, level string) { 111 | filePath := logRootPath + chanLog.LogTarget 112 | switch level { 113 | case "custom": 114 | filePath = filePath + "_" + time.Now().Format(defaultDateFormatForFileName) + ".log" 115 | break 116 | } 117 | log := time.Now().Format(defaultFullTimeLayout) + " " + chanLog.Content 118 | writeFile(filePath, log) 119 | } 120 | 121 | func writeFile(logFile string, log string) { 122 | var mode os.FileMode 123 | flag := syscall.O_RDWR | syscall.O_APPEND | syscall.O_CREAT 124 | mode = 0666 125 | logstr := log + "\r\n" 126 | file, err := os.OpenFile(logFile, flag, mode) 127 | defer file.Close() 128 | if err != nil { 129 | fmt.Println(logFile, err) 130 | return 131 | } 132 | file.WriteString(logstr) 133 | } 134 | -------------------------------------------------------------------------------- /framework/redis/redisutil.go: -------------------------------------------------------------------------------- 1 | // redisclient 2 | package redisutil 3 | 4 | import ( 5 | "sync" 6 | 7 | "github.com/garyburd/redigo/redis" 8 | ) 9 | 10 | type RedisClient struct { 11 | pool *redis.Pool 12 | Address string 13 | } 14 | 15 | var ( 16 | redisMap map[string]*RedisClient 17 | mapMutex *sync.RWMutex 18 | ) 19 | 20 | const ( 21 | defaultTimeout = 60 * 10 //默认10分钟 22 | ) 23 | 24 | func init() { 25 | redisMap = make(map[string]*RedisClient) 26 | mapMutex = new(sync.RWMutex) 27 | } 28 | 29 | // 重写生成连接池方法 30 | func newPool(redisIP string) *redis.Pool { 31 | return &redis.Pool{ 32 | MaxIdle: 5, 33 | MaxActive: 20, // max number of connections 34 | Dial: func() (redis.Conn, error) { 35 | c, err := redis.Dial("tcp", redisIP) 36 | if err != nil { 37 | panic(err) 38 | } 39 | return c, err 40 | }, 41 | } 42 | } 43 | 44 | //获取指定Address的RedisClient 45 | func GetRedisClient(address string) *RedisClient { 46 | var redis *RedisClient 47 | var mok bool 48 | mapMutex.RLock() 49 | redis, mok = redisMap[address] 50 | mapMutex.RUnlock() 51 | if !mok { 52 | redis = &RedisClient{Address: address, pool: newPool(address)} 53 | mapMutex.Lock() 54 | redisMap[address] = redis 55 | mapMutex.Unlock() 56 | } 57 | return redis 58 | } 59 | 60 | //检查指定key是否存在 61 | //如果为0不存在,为1则存在 62 | func (rc *RedisClient) Exists(key string) (int64, error) { 63 | // 从连接池里面获得一个连接 64 | conn := rc.pool.Get() 65 | // 连接完关闭,其实没有关闭,是放回池里,也就是队列里面,等待下一个重用 66 | defer conn.Close() 67 | reply, errDo := conn.Do("EXISTS", key) 68 | if errDo == nil && reply == nil { 69 | return 0, nil 70 | } 71 | val, err := redis.Int64(reply, errDo) 72 | return val, err 73 | } 74 | 75 | //获取指定key的内容 76 | func (rc *RedisClient) Get(key string) (string, error) { 77 | // 从连接池里面获得一个连接 78 | conn := rc.pool.Get() 79 | // 连接完关闭,其实没有关闭,是放回池里,也就是队列里面,等待下一个重用 80 | defer conn.Close() 81 | reply, errDo := conn.Do("GET", key) 82 | if errDo == nil && reply == nil { 83 | return "", nil 84 | } 85 | val, err := redis.String(reply, errDo) 86 | return val, err 87 | } 88 | 89 | //删除指定key的内容 90 | func (rc *RedisClient) Del(key string) (int64, error) { 91 | // 从连接池里面获得一个连接 92 | conn := rc.pool.Get() 93 | // 连接完关闭,其实没有关闭,是放回池里,也就是队列里面,等待下一个重用 94 | defer conn.Close() 95 | reply, errDo := conn.Do("DEL", key) 96 | if errDo == nil && reply == nil { 97 | return 0, nil 98 | } 99 | val, err := redis.Int64(reply, errDo) 100 | return val, err 101 | } 102 | 103 | //获取指定hashset的内容 104 | func (rc *RedisClient) HGet(hashID string, field string) (string, error) { 105 | conn := rc.pool.Get() 106 | defer conn.Close() 107 | reply, errDo := conn.Do("HGET", hashID, field) 108 | if errDo == nil && reply == nil { 109 | return "", nil 110 | } 111 | val, err := redis.String(reply, errDo) 112 | return val, err 113 | } 114 | 115 | //对存储在指定key的数值执行原子的加1操作 116 | func (rc *RedisClient) INCR(key string) (int, error) { 117 | conn := rc.pool.Get() 118 | defer conn.Close() 119 | reply, errDo := conn.Do("INCR", key) 120 | if errDo == nil && reply == nil { 121 | return 0, nil 122 | } 123 | val, err := redis.Int(reply, errDo) 124 | return val, err 125 | } 126 | 127 | //对存储在指定key的数值执行原子的减1操作 128 | func (rc *RedisClient) DECR(key string) (int, error) { 129 | conn := rc.pool.Get() 130 | defer conn.Close() 131 | reply, errDo := conn.Do("DECR", key) 132 | if errDo == nil && reply == nil { 133 | return 0, nil 134 | } 135 | val, err := redis.Int(reply, errDo) 136 | return val, err 137 | } 138 | 139 | //获取指定hashset的所有内容 140 | func (rc *RedisClient) HGetAll(hashID string) (map[string]string, error) { 141 | conn := rc.pool.Get() 142 | defer conn.Close() 143 | reply, err := redis.StringMap(conn.Do("HGetAll", hashID)) 144 | return reply, err 145 | } 146 | 147 | //设置指定hashset的内容 148 | func (rc *RedisClient) HSet(hashID string, field string, val string) error { 149 | conn := rc.pool.Get() 150 | defer conn.Close() 151 | _, err := conn.Do("HSET", hashID, field, val) 152 | return err 153 | } 154 | 155 | //删除,并获得该列表中的最后一个元素,或阻塞,直到有一个可用 156 | func (rc *RedisClient) BRPop(key string) (string, error) { 157 | conn := rc.pool.Get() 158 | defer conn.Close() 159 | val, err := redis.StringMap(conn.Do("BRPOP", key, defaultTimeout)) 160 | if err != nil { 161 | return "", err 162 | } else { 163 | return val[key], nil 164 | } 165 | } 166 | 167 | //将所有指定的值插入到存于 key 的列表的头部 168 | func (rc *RedisClient) LPush(key string, val string) (int64, error) { 169 | conn := rc.pool.Get() 170 | defer conn.Close() 171 | ret, err := redis.Int64(conn.Do("LPUSH", key, val)) 172 | if err != nil { 173 | return -1, err 174 | } else { 175 | return ret, nil 176 | } 177 | } 178 | 179 | //设置指定key的内容 180 | func (rc *RedisClient) Set(key string, val string) (string, error) { 181 | conn := rc.pool.Get() 182 | defer conn.Close() 183 | val, err := redis.String(conn.Do("SET", key, val)) 184 | return val, err 185 | } 186 | 187 | //设置指定key的内容 188 | func (rc *RedisClient) SetWithExpire(key string, val string, timeOutSeconds int) (string, error) { 189 | conn := rc.pool.Get() 190 | defer conn.Close() 191 | val, err := redis.String(conn.Do("SET", key, val, "EX", timeOutSeconds)) 192 | return val, err 193 | } 194 | -------------------------------------------------------------------------------- /framework/task/tasks.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "github.com/devfeel/tokenserver/framework/log" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | const ( 10 | taskLogTarget = "task" 11 | taskState_Init = "0" 12 | taskState_Run = "1" 13 | taskState_Stop = "2" 14 | ) 15 | 16 | //task 容器 17 | type TaskService struct { 18 | taskMap map[string]*TaskInfo 19 | taskMutex *sync.RWMutex 20 | logPath string 21 | taskHandleMap map[string]TaskHandle 22 | taskHandleMutex *sync.RWMutex 23 | } 24 | 25 | //Task定义 26 | type TaskInfo struct { 27 | TimeTicker *time.Ticker 28 | TaskID string 29 | handler TaskHandle 30 | Context *TaskContext 31 | state string //匹配 taskState_Init、taskState_Run、taskState_Stop 32 | } 33 | 34 | //Task上下文信息 35 | type TaskContext struct { 36 | TaskID string 37 | Interval int64 //运行间隔时间,单位毫秒 38 | HandleName string 39 | TaskData interface{} 40 | } 41 | 42 | func StartNewService() *TaskService { 43 | service := new(TaskService) 44 | service.taskMutex = new(sync.RWMutex) 45 | service.taskHandleMutex = new(sync.RWMutex) 46 | service.taskHandleMap = make(map[string]TaskHandle) 47 | service.taskMap = make(map[string]*TaskInfo) 48 | 49 | return service 50 | } 51 | 52 | type TaskHandle func(*TaskContext) 53 | 54 | //get handler by name 55 | func (service *TaskService) GetHandler(name string) (TaskHandle, bool) { 56 | service.taskHandleMutex.RLock() 57 | defer service.taskHandleMutex.RUnlock() 58 | handler, exists := service.taskHandleMap[name] 59 | return handler, exists 60 | } 61 | 62 | //set task handler with handlername 63 | func (service *TaskService) SetHandler(handlerName string, handler TaskHandle) { 64 | service.taskHandleMap[handlerName] = handler 65 | } 66 | 67 | //创建Task对象 68 | func (service *TaskService) CreateTask(taskID string, interval int64, handlerName string, taskData interface{}) *TaskInfo { 69 | context := new(TaskContext) 70 | context.HandleName = handlerName 71 | context.TaskID = taskID 72 | context.Interval = interval 73 | context.TaskData = taskData 74 | 75 | handler, exists := service.GetHandler(handlerName) 76 | if !exists { 77 | return nil 78 | } 79 | 80 | task := new(TaskInfo) 81 | task.TaskID = context.TaskID 82 | task.handler = handler 83 | task.state = taskState_Init 84 | task.Context = context 85 | 86 | service.taskMutex.Lock() 87 | service.taskMap[context.TaskID] = task 88 | service.taskMutex.Unlock() 89 | return task 90 | } 91 | 92 | //start timeticker 93 | func (task *TaskInfo) Start() { 94 | if task.state == taskState_Init || task.state == taskState_Stop { 95 | task.state = taskState_Run 96 | task.TimeTicker = time.NewTicker(time.Duration(task.Context.Interval) * time.Millisecond) 97 | go func() { 98 | for { 99 | select { 100 | case <-task.TimeTicker.C: 101 | //TODO:do log 102 | task.handler(task.Context) 103 | } 104 | } 105 | }() 106 | } 107 | } 108 | 109 | //stop timeticker 110 | func (task *TaskInfo) Stop() { 111 | if task.state == taskState_Stop { 112 | task.TimeTicker.Stop() 113 | task.state = taskState_Stop 114 | } 115 | } 116 | 117 | //remove all task 118 | func (service *TaskService) RemoveAllTask() { 119 | logger.Info("Task::resetAllTask begin...", taskLogTarget) 120 | service.StopAllTask() 121 | service.taskMap = make(map[string]*TaskInfo) 122 | } 123 | 124 | //结束所有Task 125 | func (service *TaskService) StopAllTask() { 126 | logger.Info("Task::StopAllTask begin...", taskLogTarget) 127 | for k, v := range service.taskMap { 128 | logger.Info("Task::StopAllTask => "+k, taskLogTarget) 129 | v.Stop() 130 | } 131 | logger.Info("Task::StopAllTask end["+string(len(service.taskMap))+"]", taskLogTarget) 132 | } 133 | 134 | //启动所有Task 135 | func (service *TaskService) StartAllTask() { 136 | logger.Info("Task::StartAllTask begin...", taskLogTarget) 137 | for _, v := range service.taskMap { 138 | logger.Info("Task::StartAllTask::StartTask => "+v.TaskID, taskLogTarget) 139 | v.Start() 140 | } 141 | logger.Info("Task::StartAllTask end", taskLogTarget) 142 | } 143 | -------------------------------------------------------------------------------- /framework/times/times.go: -------------------------------------------------------------------------------- 1 | package times 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | const ( 8 | DefaultDateLayout = "2006-01-02" 9 | DefaultFullTimeLayout = "2006-01-02 15:04:05.999999" 10 | DefaultTimeLayout = "2006-01-02 15:04:05" 11 | ) 12 | 13 | //将time转义成"2006-01-02 15:04:05" 形式的字符串 14 | func ConvertDefaultTimeString(time time.Time) string { 15 | return time.Format(DefaultTimeLayout) 16 | } 17 | 18 | //将time转义成"2006-01-02 15:04:05.999999" 形式的字符串 19 | func ConvertFullTimeString(time time.Time) string { 20 | return time.Format(DefaultFullTimeLayout) 21 | } 22 | 23 | //将time转义成"2006-01-02" 形式的字符串 24 | func ConvertDateString(time time.Time) string { 25 | return time.Format(DefaultDateLayout) 26 | } 27 | -------------------------------------------------------------------------------- /httpserver/handlers/global/golbalHandler.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "fmt" 5 | "github.com/devfeel/dotweb" 6 | "github.com/devfeel/tokenserver/config" 7 | "github.com/devfeel/tokenserver/const" 8 | "github.com/devfeel/tokenserver/framework/crypto" 9 | "github.com/devfeel/tokenserver/framework/json" 10 | "github.com/devfeel/tokenserver/framework/log" 11 | "github.com/devfeel/tokenserver/framework/redis" 12 | "github.com/devfeel/tokenserver/httpserver/model" 13 | "strconv" 14 | "time" 15 | 16 | "gopkg.in/mgo.v2/bson" 17 | ) 18 | 19 | const ( 20 | IdType_UUID = "uuid" 21 | IdType_Number = "number" 22 | IdType_TimeNumber = "timenumber" 23 | IdType_MongoDB = "mongodb" 24 | ) 25 | 26 | /*创建GlobalID 27 | Author: panxinming 28 | CreateTime: 2017-02-16 11:00 29 | HttpMethod:Get 30 | Route Param: 31 | idtype: 代表请求ID方式,目前支持:uuid、number、timenumber、mongodb 32 | Get Param: 33 | appid: 代表请求的应用 34 | module: 代表请求应用某特定模块 35 | 36 | 返回结构:HandlerResponse 37 | 0: 成功 38 | -100001:idtype is empty 39 | -100002:appid & module is empty 40 | -201001:no config RedisID_Global Redis 41 | -201002:create global number error [redis] 42 | Response.Message = GlobalID //string 43 | 44 | UPDATE LOG: 45 | 1、初始版本 --2017-02-16 11:00 by pxm 46 | */ 47 | func CreateGlobalID(ctx dotweb.Context) error { 48 | result := &models.HandlerResponse{RetCode: 0, RetMsg: ""} 49 | idtype := ctx.QueryString("idtype") 50 | appid := ctx.QueryString("appid") //代表请求的应用 51 | module := ctx.QueryString("module") //代表请求应用某特定模块 52 | var code string 53 | 54 | defer func() { 55 | logger.Info("TokenServer::CreateGlobalID ["+idtype+"] ["+appid+"] ["+module+"] => "+jsonutil.GetJsonString(result), constdefine.LogTarget_Global) 56 | ctx.WriteJson(result) 57 | }() 58 | 59 | if idtype == "" { 60 | result.RetCode = -100001 61 | result.RetMsg = "idtype is empty" 62 | return nil 63 | } 64 | if appid == "" || module == "" { 65 | result.RetCode = -100002 66 | result.RetMsg = "appid & module is empty" 67 | return nil 68 | } 69 | 70 | if idtype == IdType_UUID { 71 | code = cryptos.GetGuid() 72 | result.RetCode = 0 73 | result.RetMsg = "ok" 74 | result.Message = code 75 | return nil 76 | } 77 | 78 | if idtype == IdType_TimeNumber { 79 | createTimeNumberID(appid, module, result) 80 | return nil 81 | } 82 | 83 | if idtype == IdType_Number { 84 | createNumberID(appid, module, result) 85 | return nil 86 | } 87 | 88 | if idtype == IdType_MongoDB { 89 | code = createMongoID() 90 | result.RetCode = 0 91 | result.RetMsg = "ok" 92 | result.Message = code 93 | return nil 94 | } 95 | 96 | //no match idtype 97 | result.RetCode = -200001 98 | result.RetMsg = "idtype[" + idtype + "] is not support" 99 | return nil 100 | } 101 | 102 | //基于Redis创建连续数字 103 | func createNumberID(appid, module string, result *models.HandlerResponse) { 104 | code := "" 105 | 106 | //获取redis配置 107 | redisInfo, exists := config.GetRedisInfo(constdefine.RedisID_Global) 108 | if !exists { 109 | result.RetCode = -201001 110 | result.RetMsg = "no config RedisID_Global redis" 111 | return 112 | } 113 | 114 | key := redisInfo.KeyPre + ":Global_Number:" + appid + ":" + module 115 | redisClient := redisutil.GetRedisClient(redisInfo.ServerIP) 116 | val, err := redisClient.INCR(key) 117 | if err != nil { 118 | result.RetCode = -201002 119 | result.RetMsg = "create global number error [redis] => " + err.Error() 120 | return 121 | } 122 | 123 | code = strconv.Itoa(val) 124 | result.Message = code 125 | } 126 | 127 | //基于Redis创建TimeNumber 128 | func createTimeNumberID(appid, module string, result *models.HandlerResponse) { 129 | code := "" 130 | timeLayout := "20060102150405" 131 | 132 | //获取redis配置 133 | redisInfo, exists := config.GetRedisInfo(constdefine.RedisID_Global) 134 | if !exists { 135 | result.RetCode = -202001 136 | result.RetMsg = "no config RedisID_Global redis" 137 | return 138 | } 139 | 140 | key := redisInfo.KeyPre + ":Global_TimeNumber:" + appid + ":" + module 141 | redisClient := redisutil.GetRedisClient(redisInfo.ServerIP) 142 | val, err := redisClient.INCR(key) 143 | if err != nil { 144 | result.RetCode = -202002 145 | result.RetMsg = "create global number error [redis] => " + err.Error() 146 | return 147 | } 148 | //创建8位补0字符串 149 | code = time.Now().Format(timeLayout) + fmt.Sprintf("%08d", val) 150 | result.Message = code 151 | } 152 | 153 | //基于Mongodb的_id创建全局ID 154 | func createMongoID() string { 155 | code := bson.NewObjectId().Hex() 156 | return code 157 | } 158 | -------------------------------------------------------------------------------- /httpserver/handlers/token/tokenhandler.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | import ( 4 | "github.com/devfeel/dotweb" 5 | "github.com/devfeel/tokenserver/config" 6 | "github.com/devfeel/tokenserver/const" 7 | "github.com/devfeel/tokenserver/framework/crypto" 8 | "github.com/devfeel/tokenserver/framework/json" 9 | "github.com/devfeel/tokenserver/framework/log" 10 | "github.com/devfeel/tokenserver/framework/redis" 11 | "github.com/devfeel/tokenserver/httpserver/model" 12 | "strconv" 13 | ) 14 | 15 | type TokenInfo struct { 16 | Token string 17 | AppID string 18 | TokenBody string 19 | LifeSeconds int //有效时间,单位为秒 20 | } 21 | 22 | type VerifyTokenRequest struct { 23 | Token string 24 | AppID string 25 | TokenBody string 26 | IsCheckBody bool //是否需要验证Body是否一致 27 | } 28 | 29 | const ( 30 | Token_DefaultLifeSeconds = 60 * 30 //token默认有效期,默认30分钟有效期 31 | ) 32 | 33 | /*创建token 34 | Author: panxinming 35 | CreateTime: 2017-02-10 12:00 36 | HttpMethod:Post 37 | Post Param:TokenInfo 38 | 返回结构:HandlerResponse 39 | 0: 成功 40 | -100001:post data not legal 41 | -100002:AppID is empty 42 | -200001:no config RedisID_Token Redis 43 | -200002:create token error [uuid] 44 | -200003:create token error [redis set] 45 | Response.Message = TokenInfo 46 | 47 | UPDATE LOG: 48 | 1、初始版本 --2017-02-10 12:00 by pxm 49 | */ 50 | func CreateToken(ctx dotweb.Context) error{ 51 | result := &models.HandlerResponse{RetCode: 0, RetMsg: ""} 52 | var tokenInfo TokenInfo 53 | //获取Post内容 54 | postContent := string(ctx.Request().PostBody()) 55 | 56 | defer func() { 57 | logger.Info("TokenServer::CreateToken["+postContent+"] => "+jsonutil.GetJsonString(result), constdefine.LogTarget_Token) 58 | ctx.WriteJson(result) 59 | }() 60 | 61 | //解析提交数据 62 | if err_jsonunmar := jsonutil.Unmarshal(postContent, &tokenInfo); err_jsonunmar != nil { 63 | result.RetCode = -100001 64 | result.RetMsg = "post data not legal => " + err_jsonunmar.Error() 65 | return nil 66 | } 67 | if tokenInfo.AppID == "" { 68 | result.RetCode = -100002 69 | result.RetMsg = "AppID is empty" 70 | return nil 71 | } 72 | //默认值处理 73 | if tokenInfo.LifeSeconds <= 0 { 74 | tokenInfo.LifeSeconds = Token_DefaultLifeSeconds 75 | } 76 | 77 | //获取redis配置 78 | redisInfo, exists := config.GetRedisInfo(constdefine.RedisID_Token) 79 | if !exists { 80 | result.RetCode = -200001 81 | result.RetMsg = "no config RedisID_Token Redis" 82 | return nil 83 | } 84 | 85 | //创建token 86 | token := cryptos.GetGuid() 87 | if token == "" { 88 | result.RetCode = -200002 89 | result.RetMsg = "create token error [uuid]" 90 | return nil 91 | } 92 | tokenInfo.Token = token 93 | 94 | key := redisInfo.KeyPre + ":Token:" + tokenInfo.AppID + "_" + tokenInfo.Token 95 | value := jsonutil.GetJsonString(tokenInfo) 96 | redisClient := redisutil.GetRedisClient(redisInfo.ServerIP) 97 | val, err := redisClient.SetWithExpire(key, value, tokenInfo.LifeSeconds) 98 | if err != nil { 99 | result.RetCode = -200003 100 | result.RetMsg = "create token error [redis set] => " + err.Error() 101 | return nil 102 | } 103 | 104 | result.RetCode = 0 105 | result.RetMsg = "ok[" + val + "]" 106 | result.Message = tokenInfo 107 | //设置原子锁,默认有效期为token的两倍,初始值为1 108 | key = redisInfo.KeyPre + ":TokenLock:" + tokenInfo.AppID + "_" + tokenInfo.Token 109 | redisClient.SetWithExpire(key, "1", tokenInfo.LifeSeconds*2) 110 | 111 | return nil 112 | } 113 | 114 | /*验证token 115 | Author: panxinming 116 | CreateTime: 2017-02-10 12:00 117 | HttpMethod:Post 118 | Post Param:VerifyTokenRequest 119 | 返回结构:HandlerResponse 120 | 0: 成功 121 | -100001:post data not legal 122 | -100002:AppID is empty 123 | -200001:no config RedisID_Token Redis 124 | -201001: query token exists error 125 | -201002: redis token not exists 126 | -202001: get token-locker error 127 | -202002: token-locker locked by other 128 | -203001: query token error 129 | -203002: redis token data not legal 130 | -203003: token body is not match 131 | 132 | Response.Message = TokenInfo 133 | 134 | UPDATE LOG: 135 | 1、初始版本 --2017-02-10 12:00 by pxm 136 | */ 137 | func VerifyToken(ctx dotweb.Context) error{ 138 | result := &models.HandlerResponse{RetCode: 0, RetMsg: ""} 139 | var verifyToken VerifyTokenRequest 140 | //获取Post内容 141 | postContent := string(ctx.Request().PostBody()) 142 | 143 | defer func() { 144 | logger.Info("TokenServer::VerifyToken["+postContent+"] => "+jsonutil.GetJsonString(result), constdefine.LogTarget_Token) 145 | ctx.WriteJson(result) 146 | }() 147 | 148 | //解析提交数据 149 | err_jsonunmar := jsonutil.Unmarshal(postContent, &verifyToken) 150 | if err_jsonunmar != nil { 151 | result.RetCode = -100001 152 | result.RetMsg = "post data not legal => " + err_jsonunmar.Error() 153 | return nil 154 | } 155 | if verifyToken.AppID == "" { 156 | result.RetCode = -100002 157 | result.RetMsg = "AppID is empty" 158 | return nil 159 | } 160 | 161 | //获取Redis配置 162 | redisInfo, exists := config.GetRedisInfo(constdefine.RedisID_Token) 163 | if !exists { 164 | result.RetCode = -200001 165 | result.RetMsg = "no config RedisID_Token Redis" 166 | return nil 167 | } 168 | 169 | keyLocker := redisInfo.KeyPre + ":TokenLock:" + verifyToken.AppID + "_" + verifyToken.Token 170 | keyToken := redisInfo.KeyPre + ":Token:" + verifyToken.AppID + "_" + verifyToken.Token 171 | 172 | //创建Redis链接 173 | var redisClient *redisutil.RedisClient 174 | redisClient = redisutil.GetRedisClient(redisInfo.ServerIP) 175 | 176 | //检查token是否存在 177 | numExists, errExists := redisClient.Exists(keyToken) 178 | if errExists != nil { 179 | result.RetCode = -201001 180 | result.RetMsg = "query token exists error => " + errExists.Error() 181 | return nil 182 | } 183 | if numExists == 0 { 184 | result.RetCode = -201002 185 | result.RetMsg = "redis token not exists" 186 | return nil 187 | } 188 | 189 | //加锁 190 | valIncr, errIncr := redisClient.DECR(keyLocker) 191 | if errIncr != nil { 192 | result.RetCode = -202001 193 | result.RetMsg = "get token-locker error => " + errIncr.Error() 194 | return nil 195 | } 196 | if valIncr != 0 { 197 | result.RetCode = -202002 198 | result.RetMsg = "token-locker locked by other [" + strconv.Itoa(valIncr) + "]" 199 | //归还当前锁 200 | redisClient.INCR(keyLocker) 201 | return nil 202 | } 203 | 204 | var redisToken TokenInfo 205 | //检查Token 206 | valToken, errToken := redisClient.Get(keyToken) 207 | if errToken != nil { 208 | result.RetCode = -203001 209 | result.RetMsg = "query token error => " + errToken.Error() 210 | //归还当前锁 211 | redisClient.INCR(keyLocker) 212 | return nil 213 | } 214 | err_jsonunmar = jsonutil.Unmarshal(valToken, &redisToken) 215 | if err_jsonunmar != nil { 216 | result.RetCode = -203002 217 | result.RetMsg = "redis token data not legal [" + valToken + "] => " + err_jsonunmar.Error() 218 | //归还当前锁 219 | redisClient.INCR(keyLocker) 220 | return nil 221 | } 222 | if verifyToken.IsCheckBody { 223 | if verifyToken.TokenBody != redisToken.TokenBody { 224 | result.RetCode = -201003 225 | result.RetMsg = "token body is not match [" + valToken + "]" 226 | //归还当前锁 227 | redisClient.INCR(keyLocker) 228 | return nil 229 | } 230 | } 231 | 232 | result.RetCode = 0 233 | result.RetMsg = "ok" 234 | result.Message = redisToken 235 | //一切正常,删除相关token信息 236 | redisClient.Del(keyLocker) 237 | redisClient.Del(keyToken) 238 | 239 | return nil 240 | } 241 | 242 | /*查询token 243 | Author: panxinming 244 | CreateTime: 2017-02-10 12:00 245 | HttpMethod:Get 246 | Get Param: 247 | appid: 应用编码 248 | token: token 249 | 返回结构:HandlerResponse 250 | 0: 成功 251 | -100001:querystring token|appid is empty 252 | -200001:no config RedisID_Token Redis 253 | -200002:query token error [redis get] 254 | Response.Message = TokenInfo 255 | 256 | UPDATE LOG: 257 | 1、初始版本 --2017-02-10 12:00 by pxm 258 | */ 259 | func QueryToken(ctx dotweb.Context) error{ 260 | result := &models.HandlerResponse{RetCode: 0, RetMsg: ""} 261 | token := ctx.QueryString("token") 262 | appId := ctx.QueryString("appid") 263 | 264 | defer func() { 265 | logger.Info("TokenServer::QueryToken["+ctx.Request().RawQuery()+"] => "+jsonutil.GetJsonString(result), constdefine.LogTarget_Token) 266 | ctx.WriteJson(result) 267 | }() 268 | 269 | if token == "" || appId == "" { 270 | result.RetCode = -100001 271 | result.RetMsg = "querystring token|appid is empty" 272 | return nil 273 | } 274 | 275 | //处理数据 276 | redisInfo, exists := config.GetRedisInfo(constdefine.RedisID_Token) 277 | if !exists { 278 | result.RetCode = -200001 279 | result.RetMsg = "no config RedisID_Token Redis" 280 | return nil 281 | } 282 | key := redisInfo.KeyPre + ":Token:" + appId + "_" + token 283 | redisClient := redisutil.GetRedisClient(redisInfo.ServerIP) 284 | val, err := redisClient.Get(key) 285 | if err != nil { 286 | result.RetCode = -200002 287 | result.RetMsg = "query token error [get] => " + err.Error() 288 | return nil 289 | } 290 | 291 | if val == "" { 292 | result.RetCode = -200003 293 | result.RetMsg = "query token not exists" 294 | return nil 295 | } 296 | 297 | result.RetCode = 0 298 | result.RetMsg = "ok" 299 | result.Message = val 300 | return nil 301 | } 302 | -------------------------------------------------------------------------------- /httpserver/model/response.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type HandlerResponse struct { 4 | RetCode int 5 | RetMsg string 6 | Message interface{} 7 | } 8 | -------------------------------------------------------------------------------- /httpserver/router.go: -------------------------------------------------------------------------------- 1 | package httpserver 2 | 3 | import ( 4 | "github.com/devfeel/dotweb" 5 | "github.com/devfeel/tokenserver/httpserver/handlers/global" 6 | "github.com/devfeel/tokenserver/httpserver/handlers/token" 7 | ) 8 | 9 | func InitRoute(dotweb *dotweb.DotWeb) { 10 | //token 11 | dotweb.HttpServer.Router().POST("/token/create", token.CreateToken) 12 | dotweb.HttpServer.Router().POST("/token/verify", token.VerifyToken) 13 | dotweb.HttpServer.Router().GET("/token/query", token.QueryToken) 14 | //global 15 | dotweb.HttpServer.Router().GET("/global/createid", global.CreateGlobalID) 16 | } 17 | -------------------------------------------------------------------------------- /httpserver/server.go: -------------------------------------------------------------------------------- 1 | package httpserver 2 | 3 | import ( 4 | "fmt" 5 | "github.com/devfeel/dotweb" 6 | "github.com/devfeel/tokenserver/config" 7 | "github.com/devfeel/tokenserver/framework/log" 8 | "strconv" 9 | ) 10 | 11 | func StartServer() error { 12 | 13 | //初始化DotServer 14 | app := dotweb.New() 15 | 16 | //设置dotserver日志目录 17 | app.SetLogPath(config.CurrentConfig.Log.FilePath) 18 | 19 | //设置路由 20 | InitRoute(app) 21 | 22 | innerLogger := logger.GetInnerLogger() 23 | 24 | //启动监控服务 25 | pprofport := config.CurrentConfig.HttpServer.PProfPort 26 | app.SetPProfConfig(true, pprofport) 27 | 28 | // 开始服务 29 | port := config.CurrentConfig.HttpServer.HttpPort 30 | innerLogger.Debug("dotweb.StartServer => " + strconv.Itoa(port)) 31 | err := app.StartServer(port) 32 | return err 33 | } 34 | 35 | func ReSetServer() { 36 | //初始化应用信息 37 | fmt.Println("ReSetServer") 38 | } 39 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/devfeel/tokenserver/config" 7 | "github.com/devfeel/tokenserver/framework/file" 8 | "github.com/devfeel/tokenserver/framework/log" 9 | "github.com/devfeel/tokenserver/httpserver" 10 | "os" 11 | "os/signal" 12 | "runtime" 13 | "syscall" 14 | ) 15 | 16 | var ( 17 | innerLogger *logger.InnerLogger 18 | configFile string 19 | DefaultConfigFileName string 20 | ) 21 | 22 | func init() { 23 | innerLogger = logger.GetInnerLogger() 24 | DefaultConfigFileName = fileutil.GetCurrentDirectory() + "/app.conf" 25 | } 26 | 27 | func main() { 28 | defer func() { 29 | if err := recover(); err != nil { 30 | strLog := "TechService.tokenserver:main recover error => " + fmt.Sprintln(err) 31 | os.Stdout.Write([]byte(strLog)) 32 | innerLogger.Error(strLog) 33 | 34 | buf := make([]byte, 4096) 35 | n := runtime.Stack(buf, true) 36 | innerLogger.Error(string(buf[:n])) 37 | os.Stdout.Write(buf[:n]) 38 | } 39 | }() 40 | 41 | currentBaseDir := fileutil.GetCurrentDirectory() 42 | flag.StringVar(&configFile, "config", "", "配置文件路径") 43 | if configFile == "" { 44 | configFile = DefaultConfigFileName 45 | } 46 | 47 | //启动内部日志服务 48 | logger.StartInnerLogHandler(currentBaseDir) 49 | 50 | //加载xml配置文件 51 | appconfig := config.InitConfig(configFile) 52 | 53 | //设置基本目录 54 | config.SetBaseDir(currentBaseDir) 55 | 56 | //启动日志服务 57 | logger.StartLogHandler(appconfig.Log.FilePath) 58 | 59 | //监听系统信号 60 | go listenSignal() 61 | 62 | err := httpserver.StartServer() 63 | if err != nil { 64 | innerLogger.Warn("HttpServer.StartServer失败 " + err.Error()) 65 | fmt.Println("HttpServer.StartServer失败 " + err.Error()) 66 | } 67 | 68 | } 69 | 70 | func listenSignal() { 71 | c := make(chan os.Signal, 1) 72 | //syscall.SIGSTOP 73 | signal.Notify(c, syscall.SIGHUP) 74 | for { 75 | s := <-c 76 | innerLogger.Info("signal::ListenSignal [" + s.String() + "]") 77 | switch s { 78 | case syscall.SIGHUP: //配置重载 79 | innerLogger.Info("signal::ListenSignal reload config begin...") 80 | //重新加载配置文件 81 | config.InitConfig(configFile) 82 | innerLogger.Info("signal::ListenSignal reload config end") 83 | default: 84 | return 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /version.log: -------------------------------------------------------------------------------- 1 | tokenserver - 通用Token服务 2 | 3 | V1.3 4 | 1、适配新版dotweb 5 | 2、2018-06-01 10:00 6 | 7 | V1.2 8 | 1、GlobalID生成器增加mongodb模式 9 | 2、2017-02-21 14:00 10 | 11 | V1.1 12 | 1、新增GlobalID生成器功能,第一版支持uuid、number、timenumber 13 | 2、2017-02-16 11:00 14 | 15 | V1.0 16 | 1、基础版本,支持token创建\查询\验证三类接口 17 | 2、2017-02-10 15:00 --------------------------------------------------------------------------------