├── 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
--------------------------------------------------------------------------------