github.com/godaddy-x/freego@v1.0.156/node/node.go (about)

     1  package node
     2  
     3  import (
     4  	"bytes"
     5  	"github.com/buaazp/fasthttprouter"
     6  	"github.com/godaddy-x/freego/cache"
     7  	"github.com/godaddy-x/freego/ex"
     8  	"github.com/godaddy-x/freego/node/common"
     9  	"github.com/godaddy-x/freego/utils"
    10  	"github.com/godaddy-x/freego/utils/crypto"
    11  	"github.com/godaddy-x/freego/utils/jwt"
    12  	"github.com/godaddy-x/freego/zlog"
    13  	"github.com/valyala/fasthttp"
    14  	"net/http"
    15  	"strings"
    16  	"unsafe"
    17  )
    18  
    19  const (
    20  	UTF8 = "UTF-8"
    21  
    22  	ANDROID = "android"
    23  	IOS     = "ios"
    24  	WEB     = "web"
    25  
    26  	TEXT_PLAIN       = "text/plain; charset=utf-8"
    27  	APPLICATION_JSON = "application/json; charset=utf-8"
    28  
    29  	GET     = "GET"
    30  	POST    = "POST"
    31  	PUT     = "PUT"
    32  	PATCH   = "PATCH"
    33  	DELETE  = "DELETE"
    34  	HEAD    = "HEAD"
    35  	OPTIONS = "OPTIONS"
    36  
    37  	MAX_VALUE_LEN = 200000 // 最大参数值长度
    38  	MAX_TOKEN_LEN = 2048   // 最大Token值长度
    39  	MAX_CODE_LEN  = 1024   // 最大Code值长度
    40  
    41  	Authorization = "Authorization"
    42  	RandomCode    = "RandomCode"
    43  )
    44  
    45  type HookNode struct {
    46  	Context *Context
    47  	filters []*FilterObject
    48  }
    49  
    50  type Configs struct {
    51  	jwtConfig     jwt.JwtConfig
    52  	routerConfigs map[string]*RouterConfig
    53  	langConfigs   map[string]map[string]string
    54  	defaultLang   string
    55  }
    56  
    57  type RouterConfig struct {
    58  	Guest       bool // 游客模式,原始请求 false.否 true.是
    59  	UseRSA      bool // 非登录状态使用RSA模式请求 false.否 true.是
    60  	UseHAX      bool // 非登录状态,判定公钥哈希验签 false.否 true.是
    61  	AesRequest  bool // 请求是否必须AES加密 false.否 true.是
    62  	AesResponse bool // 响应是否必须AES加密 false.否 true.是
    63  }
    64  
    65  type HttpLog struct {
    66  	Method   string // 请求方法
    67  	LogNo    string // 日志唯一标记
    68  	CreateAt int64  // 日志创建时间
    69  	UpdateAt int64  // 日志完成时间
    70  	CostMill int64  // 业务耗时,毫秒
    71  }
    72  
    73  type JsonBody struct {
    74  	Data  interface{} `json:"d"`
    75  	Time  int64       `json:"t"`
    76  	Nonce string      `json:"n"`
    77  	Plan  int64       `json:"p"` // 0.默认(登录状态) 1.AES(登录状态) 2.RSA/ECC模式(匿名状态) 3.独立验签模式(匿名状态)
    78  	Sign  string      `json:"s"`
    79  }
    80  
    81  type JsonResp struct {
    82  	Code    int         `json:"c"`
    83  	Message string      `json:"m"`
    84  	Data    interface{} `json:"d"`
    85  	Time    int64       `json:"t"`
    86  	Nonce   string      `json:"n"`
    87  	Plan    int64       `json:"p"`
    88  	Sign    string      `json:"s"`
    89  }
    90  
    91  type Permission struct {
    92  	//Ready     bool    // true.已有权限配置
    93  	MatchAll  bool    // true.满足所有权限角色才放行
    94  	NeedLogin bool    // true.需要登录状态
    95  	HasRole   []int64 // 拥有角色ID列表
    96  	NeedRole  []int64 // 所需角色ID列表
    97  }
    98  
    99  type System struct {
   100  	Name          string // 系统名
   101  	Version       string // 系统版本
   102  	AcceptTimeout int64  // 超时主动断开客户端连接,秒
   103  	enableECC     bool
   104  }
   105  
   106  type Context struct {
   107  	configs       *Configs
   108  	router        *fasthttprouter.Router
   109  	CacheAware    func(ds ...string) (cache.Cache, error)
   110  	Method        string
   111  	Path          string
   112  	System        *System
   113  	RequestCtx    *fasthttp.RequestCtx
   114  	Subject       *jwt.Subject
   115  	JsonBody      *JsonBody
   116  	Response      *Response
   117  	filterChain   *filterChain
   118  	RouterConfig  *RouterConfig
   119  	RSA           crypto.Cipher
   120  	roleRealm     func(ctx *Context, onlyRole bool) (*Permission, error) // 资源对象
   121  	Storage       map[string]interface{}
   122  	postCompleted bool
   123  	postHandle    PostHandle
   124  	errorHandle   ErrorHandle
   125  }
   126  
   127  type Response struct {
   128  	Encoding      string
   129  	ContentType   string
   130  	ContentEntity interface{}
   131  	// response result
   132  	StatusCode        int
   133  	ContentEntityByte bytes.Buffer
   134  }
   135  
   136  func (self *JsonBody) ParseData(dst interface{}) error {
   137  	raw, b := self.Data.([]byte)
   138  	if !b {
   139  		return utils.Error("jsonBody data not string")
   140  	}
   141  	return utils.JsonUnmarshal(raw, dst)
   142  }
   143  
   144  func (self *JsonBody) RawData() []byte {
   145  	raw, b := self.Data.([]byte)
   146  	if !b {
   147  		return nil
   148  	}
   149  	return raw
   150  }
   151  
   152  func (self *Context) GetTokenSecret() string {
   153  	return jwt.GetTokenSecret(utils.Bytes2Str(self.Subject.GetRawBytes()), self.configs.jwtConfig.TokenKey)
   154  }
   155  
   156  func (self *Context) GetHmac256Sign(d, n string, t, p int64, key string) string {
   157  	if len(key) > 0 {
   158  		return utils.HMAC_SHA256(utils.AddStr(self.Path, d, n, t, p), key, true)
   159  	}
   160  	return utils.HMAC_SHA256(utils.AddStr(self.Path, d, n, t, p), self.GetTokenSecret(), true)
   161  }
   162  
   163  func (self *Context) AddStorage(k string, v interface{}) {
   164  	if self.Storage == nil {
   165  		self.Storage = map[string]interface{}{}
   166  	}
   167  	if len(k) == 0 || v == nil {
   168  		return
   169  	}
   170  	self.Storage[k] = v
   171  }
   172  
   173  func (self *Context) GetStorage(k string) interface{} {
   174  	if self.Storage == nil {
   175  		return nil
   176  	}
   177  	if len(k) == 0 {
   178  		return nil
   179  	}
   180  	v, b := self.Storage[k]
   181  	if !b || v == nil {
   182  		return nil
   183  	}
   184  	return v
   185  }
   186  
   187  func (self *Context) DelStorage(k string) {
   188  	if self.Storage == nil {
   189  		return
   190  	}
   191  	delete(self.Storage, k)
   192  }
   193  
   194  func (self *Context) Authenticated() bool {
   195  	if self.Subject == nil || !self.Subject.CheckReady() {
   196  		return false
   197  	}
   198  	return true
   199  }
   200  
   201  func (self *Context) Parser(dst interface{}) error {
   202  	if self.JsonBody == nil || self.JsonBody.Data == nil {
   203  		return nil
   204  	}
   205  	if err := self.JsonBody.ParseData(dst); err != nil {
   206  		msg := "JSON parameter parsing failed"
   207  		zlog.Error(msg, 0, zlog.String("path", self.Path), zlog.String("device", self.ClientDevice()), zlog.Any("data", self.JsonBody), zlog.AddError(err))
   208  		return ex.Throw{Msg: msg, Err: err}
   209  	}
   210  	// TODO 备注: 已有会话状态时,指针填充context值,不能随意修改指针偏移值
   211  	identify := &common.Identify{}
   212  	if self.Authenticated() {
   213  		identify.ID = self.Subject.GetSub()
   214  	}
   215  	context := common.Context{
   216  		Identify:   identify,
   217  		Path:       self.Path,
   218  		System:     &common.System{Name: self.System.Name, Version: self.System.Version},
   219  		CacheAware: self.CacheAware,
   220  		RSA:        self.RSA,
   221  	}
   222  	src := utils.GetPtr(dst, 0)
   223  	req := common.GetBasePtrReq(src)
   224  	base := common.BaseReq{
   225  		Context: context,
   226  		Offset:  req.Offset,
   227  		Limit:   req.Limit,
   228  		PrevID:  req.PrevID,
   229  		LastID:  req.LastID,
   230  		CountQ:  req.CountQ,
   231  		Cmd:     req.Cmd,
   232  	}
   233  	*((*common.BaseReq)(unsafe.Pointer(src))) = base
   234  	return nil
   235  }
   236  
   237  func (self *Context) ClientDevice() string {
   238  	agent := utils.Bytes2Str(self.RequestCtx.Request.Header.Peek("User-Agent"))
   239  	if utils.HasStr(agent, "Android") || utils.HasStr(agent, "Adr") {
   240  		return ANDROID
   241  	} else if utils.HasStr(agent, "iPad") || utils.HasStr(agent, "iPhone") || utils.HasStr(agent, "Mac") {
   242  		return IOS
   243  	} else {
   244  		return WEB
   245  	}
   246  }
   247  
   248  func (self *Context) ClientLanguage() string {
   249  	return utils.Bytes2Str(self.RequestCtx.Request.Header.Peek("Language"))
   250  }
   251  
   252  func (self *Context) readParams() error {
   253  	if self.Method != POST {
   254  		return nil
   255  	}
   256  	body := self.RequestCtx.PostBody()
   257  	// 原始请求模式
   258  	if self.RouterConfig.Guest {
   259  		if body == nil || len(body) == 0 {
   260  			return nil
   261  		}
   262  		self.JsonBody.Data = body
   263  		return nil
   264  	}
   265  	// 安全请求模式
   266  	auth := self.RequestCtx.Request.Header.Peek(Authorization)
   267  	if len(auth) > MAX_TOKEN_LEN {
   268  		return ex.Throw{Code: http.StatusBadRequest, Msg: "authorization parameters length is too long"}
   269  	}
   270  	self.Subject.ResetTokenBytes(auth)
   271  	//self.Subject.ResetTokenBytes(self.RequestCtx.Request.Header.Peek(Authorization))
   272  	if body == nil || len(body) == 0 {
   273  		return ex.Throw{Code: http.StatusBadRequest, Msg: "body parameters is nil"}
   274  	}
   275  	if len(body) > (MAX_VALUE_LEN) {
   276  		return ex.Throw{Code: http.StatusLengthRequired, Msg: "body parameters length is too long"}
   277  	}
   278  	self.JsonBody.Data = utils.GetJsonString(body, "d")
   279  	self.JsonBody.Time = utils.GetJsonInt64(body, "t")
   280  	self.JsonBody.Nonce = utils.GetJsonString(body, "n")
   281  	self.JsonBody.Plan = utils.GetJsonInt64(body, "p")
   282  	self.JsonBody.Sign = utils.GetJsonString(body, "s")
   283  	//if err := utils.JsonUnmarshal(body, self.JsonBody); err != nil {
   284  	//	panic(err)
   285  	//}
   286  	if err := self.validJsonBody(); err != nil { // TODO important
   287  		return err
   288  	}
   289  	return nil
   290  }
   291  
   292  func (self *Context) validReplayAttack(sign string) error {
   293  	if self.CacheAware == nil {
   294  		return nil
   295  	}
   296  	c, err := self.CacheAware()
   297  	if err != nil {
   298  		return ex.Throw{Code: http.StatusBadRequest, Msg: "cache instance invalid"}
   299  	}
   300  	b, err := c.Exists(sign)
   301  	if err != nil || b {
   302  		return ex.Throw{Code: http.StatusBadRequest, Msg: "replay attack invalid"}
   303  	}
   304  	if err := c.Put(sign, 1, 600); err != nil {
   305  		return ex.Throw{Code: http.StatusBadRequest, Msg: "cache replay attack value error"}
   306  	}
   307  	return nil
   308  }
   309  
   310  func (self *Context) validJsonBody() error {
   311  	if self.JsonBody == nil {
   312  		return ex.Throw{Code: http.StatusBadRequest, Msg: "request json body is nil"}
   313  	}
   314  	body := self.JsonBody
   315  	d, b := body.Data.(string)
   316  	if !b || len(d) == 0 {
   317  		return ex.Throw{Code: http.StatusBadRequest, Msg: "request data is nil"}
   318  	}
   319  	if !utils.CheckInt64(body.Plan, 0, 1, 2, 3) {
   320  		return ex.Throw{Code: http.StatusBadRequest, Msg: "request plan invalid"}
   321  	}
   322  	if !utils.CheckLen(body.Nonce, 8, 32) {
   323  		return ex.Throw{Code: http.StatusBadRequest, Msg: "request nonce invalid"}
   324  	}
   325  	if body.Time <= 0 {
   326  		return ex.Throw{Code: http.StatusBadRequest, Msg: "request time must be > 0"}
   327  	}
   328  	if utils.MathAbs(utils.UnixSecond()-body.Time) > jwt.FIVE_MINUTES { // 判断绝对时间差超过5分钟
   329  		return ex.Throw{Code: http.StatusBadRequest, Msg: "request time invalid"}
   330  	}
   331  	if self.RouterConfig.AesRequest && body.Plan != 1 {
   332  		return ex.Throw{Code: http.StatusBadRequest, Msg: "request parameters must use AES encryption"}
   333  	}
   334  	if !utils.CheckStrLen(body.Sign, 32, 64) {
   335  		return ex.Throw{Code: http.StatusBadRequest, Msg: "request signature length invalid"}
   336  	}
   337  	if utils.CheckInt64(body.Plan, 0, 1) && len(self.Subject.GetRawBytes()) == 0 {
   338  		return ex.Throw{Code: http.StatusBadRequest, Msg: "request header token is nil"}
   339  	}
   340  	if utils.CheckInt64(body.Plan, 2, 3) { // reset token
   341  		if body.Plan == 2 {
   342  			if !self.RouterConfig.UseRSA {
   343  				return ex.Throw{Code: http.StatusBadRequest, Msg: "request parameters must use RSA encryption"}
   344  			}
   345  		}
   346  		if body.Plan == 3 {
   347  			if !self.RouterConfig.UseHAX {
   348  				return ex.Throw{Code: http.StatusBadRequest, Msg: "request parameters must use HAX signature"}
   349  			}
   350  		}
   351  	}
   352  	var key string
   353  	var anonymous bool // true.匿名状态
   354  	if self.RouterConfig.UseRSA || self.RouterConfig.UseHAX {
   355  		_, key = self.RSA.GetPublicKey()
   356  		anonymous = true
   357  	}
   358  	if self.GetHmac256Sign(d, body.Nonce, body.Time, body.Plan, key) != body.Sign {
   359  		return ex.Throw{Code: http.StatusBadRequest, Msg: "request signature invalid"}
   360  	}
   361  	if err := self.validReplayAttack(body.Sign); err != nil {
   362  		return err
   363  	}
   364  	var rawData []byte
   365  	var err error
   366  	if body.Plan == 0 && !anonymous { // 登录状态 P0 Base64
   367  		rawData = utils.Base64Decode(d)
   368  		if rawData == nil || len(rawData) == 0 {
   369  			return ex.Throw{Code: http.StatusBadRequest, Msg: "parameter Base64 parsing failed"}
   370  		}
   371  	} else if body.Plan == 1 && !anonymous { // 登录状态 P1 AES
   372  		rawData, err = utils.AesDecrypt(d, self.GetTokenSecret(), utils.AddStr(body.Nonce, body.Time))
   373  		if err != nil {
   374  			return ex.Throw{Code: http.StatusBadRequest, Msg: "AES failed to parse data", Err: err}
   375  		}
   376  	} else if body.Plan == 2 && self.RouterConfig.UseRSA && anonymous { // 非登录状态 P2 RSA+AES
   377  		codeBs := self.RequestCtx.Request.Header.Peek(RandomCode)
   378  		if len(codeBs) > MAX_CODE_LEN {
   379  			return ex.Throw{Code: http.StatusBadRequest, Msg: "client random code invalid"}
   380  		}
   381  		randomCode := utils.Bytes2Str(codeBs)
   382  		if len(randomCode) == 0 {
   383  			return ex.Throw{Code: http.StatusBadRequest, Msg: "client random code invalid"}
   384  		}
   385  		code, err := self.RSA.Decrypt(randomCode)
   386  		if err != nil {
   387  			return ex.Throw{Code: http.StatusBadRequest, Msg: "server private-key decrypt failed", Err: err}
   388  		}
   389  		if len(code) == 0 {
   390  			return ex.Throw{Code: http.StatusBadRequest, Msg: "server private-key decrypt data is nil", Err: err}
   391  		}
   392  		rawData, err = utils.AesDecrypt(d, code, code)
   393  		if err != nil {
   394  			return ex.Throw{Code: http.StatusBadRequest, Msg: "AES failed to parse data", Err: err}
   395  		}
   396  		self.AddStorage(RandomCode, code)
   397  	} else if body.Plan == 3 && self.RouterConfig.UseHAX && anonymous { // 非登录状态 P3 Base64
   398  		rawData = utils.Base64Decode(d)
   399  		if rawData == nil || len(rawData) == 0 {
   400  			return ex.Throw{Code: http.StatusBadRequest, Msg: "parameter Base64 parsing failed"}
   401  		}
   402  	} else {
   403  		return ex.Throw{Code: http.StatusBadRequest, Msg: "request parameters plan invalid"}
   404  	}
   405  	self.JsonBody.Data = rawData
   406  	return nil
   407  }
   408  
   409  func (self *Context) GetHeader(key string) string {
   410  	return utils.Bytes2Str(self.RequestCtx.Request.Header.Peek(key))
   411  }
   412  
   413  func (self *Context) GetPostBody() string {
   414  	return utils.Bytes2Str(self.RequestCtx.Request.Body())
   415  }
   416  
   417  func (self *Context) GetJwtConfig() jwt.JwtConfig {
   418  	return self.configs.jwtConfig
   419  }
   420  
   421  func (self *Context) Handle() error {
   422  	if self.postCompleted {
   423  		return nil
   424  	}
   425  	self.postCompleted = true
   426  	return self.postHandle(self)
   427  }
   428  
   429  func (self *Context) RemoteIP() string {
   430  	clientIP := string(self.RequestCtx.Request.Header.Peek("X-Forwarded-For"))
   431  	if index := strings.IndexByte(clientIP, ','); index >= 0 {
   432  		clientIP = clientIP[0:index]
   433  	}
   434  	clientIP = strings.TrimSpace(clientIP)
   435  	if len(clientIP) > 0 {
   436  		return clientIP
   437  	}
   438  	clientIP = strings.TrimSpace(utils.Bytes2Str(self.RequestCtx.Request.Header.Peek("X-Real-Ip")))
   439  	if len(clientIP) > 0 {
   440  		return clientIP
   441  	}
   442  	return self.RequestCtx.RemoteIP().String()
   443  }
   444  
   445  func (self *Context) reset(ctx *Context, handle PostHandle, request *fasthttp.RequestCtx, fs []*FilterObject) {
   446  	if self.CacheAware == nil {
   447  		self.CacheAware = ctx.CacheAware
   448  	}
   449  	if self.RSA == nil {
   450  		self.RSA = ctx.RSA
   451  	}
   452  	if self.roleRealm == nil {
   453  		self.roleRealm = ctx.roleRealm
   454  	}
   455  	if self.errorHandle == nil {
   456  		self.errorHandle = ctx.errorHandle
   457  	}
   458  	if len(self.filterChain.filters) == 0 {
   459  		self.filterChain.filters = fs
   460  	}
   461  	self.System = ctx.System
   462  	self.postHandle = handle
   463  	self.RequestCtx = request
   464  	self.Method = utils.Bytes2Str(self.RequestCtx.Method())
   465  	self.Path = utils.Bytes2Str(self.RequestCtx.Path())
   466  	self.RouterConfig = self.configs.routerConfigs[self.Path]
   467  	self.postCompleted = false
   468  	self.filterChain.pos = 0
   469  	self.resetJsonBody()
   470  	self.resetResponse()
   471  	self.resetSubject()
   472  	self.resetTokenStorage()
   473  }
   474  
   475  func (self *Context) resetTokenStorage() {
   476  	if len(self.Storage) == 0 {
   477  		return
   478  	}
   479  	for k, _ := range self.Storage {
   480  		delete(self.Storage, k)
   481  	}
   482  }
   483  
   484  func (self *Context) resetJsonBody() {
   485  	if self.JsonBody == nil {
   486  		self.JsonBody = &JsonBody{}
   487  	}
   488  	self.JsonBody.Data = nil
   489  	self.JsonBody.Nonce = ""
   490  	self.JsonBody.Sign = ""
   491  	self.JsonBody.Time = 0
   492  	self.JsonBody.Plan = 0
   493  }
   494  
   495  func (self *Context) resetResponse() {
   496  	if self.Response == nil {
   497  		self.Response = &Response{}
   498  	}
   499  	if len(self.Response.Encoding) == 0 {
   500  		self.Response.Encoding = UTF8
   501  	}
   502  	if len(self.Response.ContentType) == 0 {
   503  		self.Response.ContentType = APPLICATION_JSON
   504  	}
   505  	self.Response.ContentEntity = nil
   506  	self.Response.StatusCode = 0
   507  	if self.Response.ContentEntityByte.Len() > 0 {
   508  		self.Response.ContentEntityByte.Reset()
   509  	}
   510  }
   511  
   512  func (self *Context) resetSubject() {
   513  	if self.Subject == nil {
   514  		self.Subject = &jwt.Subject{}
   515  		self.Subject.Header = &jwt.Header{}
   516  		self.Subject.Payload = &jwt.Payload{}
   517  	}
   518  	self.Subject.Payload.Sub = ""
   519  	self.Subject.Payload.Iss = ""
   520  	self.Subject.Payload.Aud = ""
   521  	self.Subject.Payload.Iat = 0
   522  	self.Subject.Payload.Exp = 0
   523  	self.Subject.Payload.Dev = ""
   524  	self.Subject.Payload.Jti = ""
   525  	self.Subject.Payload.Ext = ""
   526  	self.Subject.ResetTokenBytes(nil)
   527  	self.Subject.ResetPayloadBytes(nil)
   528  }
   529  
   530  func (self *Context) Json(data interface{}) error {
   531  	self.Response.ContentType = APPLICATION_JSON
   532  	if data == nil {
   533  		self.Response.ContentEntity = emptyMap
   534  	} else {
   535  		self.Response.ContentEntity = data
   536  	}
   537  	return nil
   538  }
   539  
   540  func (self *Context) Text(data string) error {
   541  	self.Response.ContentType = TEXT_PLAIN
   542  	self.Response.ContentEntity = data
   543  	return nil
   544  }