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 }