bootstrap.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. package bootstrap
  2. import (
  3. "crypto/md5"
  4. "encoding/hex"
  5. "fmt"
  6. "io"
  7. "io/fs"
  8. "os"
  9. "path/filepath"
  10. "rtzh_elec_temperature/enum"
  11. "rtzh_elec_temperature/test"
  12. "net"
  13. "net/url"
  14. "rtzh_elec_temperature/arrayex"
  15. "rtzh_elec_temperature/conf"
  16. "rtzh_elec_temperature/controllers"
  17. "rtzh_elec_temperature/db"
  18. "rtzh_elec_temperature/global"
  19. "rtzh_elec_temperature/logger"
  20. "rtzh_elec_temperature/models/bo"
  21. "rtzh_elec_temperature/monitor"
  22. "rtzh_elec_temperature/mqtt"
  23. "rtzh_elec_temperature/rtelec_app_public_lib/service"
  24. "rtzh_elec_temperature/sms"
  25. "rtzh_elec_temperature/tools"
  26. "rtzh_elec_temperature/upgrade"
  27. "runtime"
  28. "sort"
  29. "strconv"
  30. "strings"
  31. "time"
  32. "github.com/astaxie/beego/context"
  33. "github.com/astaxie/beego"
  34. "github.com/gin-gonic/gin"
  35. )
  36. func Run() {
  37. logger.Logger.Init()
  38. logger.Logger.Println("****** 当前操作系统:" + string(runtime.GOOS) + " " + runtime.GOARCH + " ******")
  39. conf.LoadAppConf()
  40. dbtype := tools.IsEmpty(conf.GlobalConfig["dbtype"])
  41. runmode := tools.IsEmpty(conf.GlobalConfig["runmode"])
  42. if dbtype == "" || dbtype == "mysql" {
  43. db.ConnDB("conf/mysql-" + runmode + ".cnf")
  44. } else if dbtype == "sqlite" {
  45. db.ConnSqlite("conf/sqlite-" + runmode + ".cnf")
  46. }
  47. //连接mqtt
  48. go mqtt.Start()
  49. go bo.LoadSysParam()
  50. go monitor.StartDataMonitor()
  51. //初始化短信对象
  52. go sms.StartSms()
  53. //启动与管理app的服务
  54. service.RtelecManageApp()
  55. //启动WS数据接收进程
  56. go controllers.ReceiveData()
  57. //启动WS数据发送进程
  58. go controllers.SendData()
  59. if conf.GlobalConfig["wsport"] != "" {
  60. logger.Logger.Println("WS服务 Listen:" + conf.GlobalConfig["wsport"])
  61. var g1 = func() {
  62. r := gin.New()
  63. r.GET("/", controllers.WebSocketBase)
  64. err := r.Run(":" + conf.GlobalConfig["wsport"])
  65. if err != nil {
  66. return
  67. }
  68. }
  69. go g1()
  70. }
  71. beego.SetLevel(beego.LevelDebug)
  72. //启动web服务
  73. beego.SetStaticPath("/", "static/login.html")
  74. beego.SetStaticPath("/login", "static/login.html")
  75. beego.SetStaticPath("/logout", "static/login.html")
  76. beego.SetStaticPath("/swagger", "/static/swagger/index.html")
  77. beego.InsertFilter("/api/*", beego.BeforeRouter, AuthFilter)
  78. logger.Logger.Println("****** 当前程序运行版本:" + upgrade.GetVersion() + " 运行环境:" + runmode + " ******")
  79. logger.Logger.Println("如果需要重新生成api接口文档,终端运行生成命令:bee run -gendoc=true -downdoc=true")
  80. go func() {
  81. //将生成的swagger文件复制到web目录下
  82. dirchar := string(os.PathSeparator)
  83. swaggerdir, err := os.Stat("." + dirchar + "swagger")
  84. if os.IsNotExist(err) {
  85. //项目中没有启用swagger时,不处理
  86. return
  87. }
  88. if swaggerdir.IsDir() {
  89. filepath.Walk("."+dirchar+"swagger", func(pathitem string, info fs.FileInfo, err error) error {
  90. if info.IsDir() {
  91. return nil
  92. }
  93. src, _ := os.Open(pathitem)
  94. defer src.Close()
  95. dst, _ := os.Create("." + dirchar + "static" + dirchar + "swagger" + dirchar + info.Name())
  96. defer dst.Close()
  97. io.Copy(dst, src)
  98. return nil
  99. })
  100. }
  101. }()
  102. //根据配置的运行模型启动单元测试
  103. test.Start("testtoken")
  104. if conf.GlobalConfig["enablehttps"] == "true" {
  105. logger.Logger.Println("启动应用" + conf.GlobalConfig["appname"] + "端口号:" + conf.GlobalConfig["httpsport"])
  106. beego.Run()
  107. } else {
  108. if conf.GlobalConfig["appport"] != "" {
  109. logger.Logger.Println("启动应用" + conf.GlobalConfig["appname"] + "端口号:" + conf.GlobalConfig["appport"])
  110. beego.Run(":" + conf.GlobalConfig["appport"])
  111. }
  112. }
  113. }
  114. var AuthFilter = func(ctx *context.Context) {
  115. ip := ctx.Request.Header.Get("X-Real-IP")
  116. if net.ParseIP(ip) == nil {
  117. ips := ctx.Request.Header.Get("X-Forward-For")
  118. for _, i := range strings.Split(ips, ",") {
  119. if net.ParseIP(i) != nil {
  120. ip = i
  121. break
  122. }
  123. }
  124. if ip == "" {
  125. ip, _, _ = net.SplitHostPort(ctx.Request.RemoteAddr)
  126. }
  127. }
  128. if ip != "" {
  129. //当前IP是否需要检查访问授权,默认为需要
  130. isCheckThisIP := true
  131. if _, exist := global.AccessedIpsList.Load(ip); exist {
  132. //IP已经访问过系统,则无需检查
  133. isCheckThisIP = false
  134. }
  135. if isCheckThisIP && global.AllowAccessIps != "*" {
  136. //判断当前客户端是否在允许范围内
  137. ips := strings.Split(global.AllowAccessIps, ",")
  138. allowLogin := false
  139. for _, iplimt := range ips {
  140. if iplimt == ip {
  141. allowLogin = true
  142. break
  143. }
  144. tmppos := strings.Index(iplimt, ".*")
  145. if tmppos > 1 {
  146. //ip段
  147. if len(ip) < tmppos {
  148. continue
  149. }
  150. if ip[:tmppos] == iplimt[:tmppos] {
  151. allowLogin = true
  152. break
  153. }
  154. }
  155. }
  156. if !allowLogin {
  157. userInfo := map[string]interface{}{}
  158. userInfo["ip"] = ip
  159. userInfo["name"] = "访问控制"
  160. new(bo.SystemLog).Fail(enum.AuditType_Client, enum.LogType_commit, enum.OptEventType_System, enum.OptEventLevel_Hight, fmt.Sprintf("未授权终端(IP:%s)尝试访问系统被拦截", ip), userInfo)
  161. //c.Data["json"] = c.WarpError("系统限制:未授权的访问!")
  162. //c.ServeJSON()
  163. ctx.Redirect(430, "/static/430.html")
  164. return
  165. }
  166. }
  167. //将IP缓存到允许队列中,如果更改了允许访问的IP配置时,需要重新初始化该队列
  168. global.AccessedIpsList.Store(ip, "allow")
  169. }
  170. logger.Logger.Debug(fmt.Sprintf("接收到请求URL:%s", ctx.Request.RequestURI))
  171. u, err := url.Parse(strings.ReplaceAll(ctx.Request.RequestURI, "//", "/"))
  172. if err != nil {
  173. ctx.Redirect(500, "/error")
  174. return
  175. }
  176. uri := strings.ToLower(u.Path)
  177. if uri == "/api/logout" || uri == "/api/version" || arrayex.IndexOf(global.NoAuthRouter, uri) > -1 {
  178. return
  179. }
  180. var token string
  181. token = ctx.Request.Header.Get("Authorization")
  182. if token == "" {
  183. logger.Logger.Println("token标识缺失")
  184. ctx.ResponseWriter.WriteHeader(401)
  185. //ctx.Redirect(401, "/login")
  186. return
  187. }
  188. //单元测试模式时,不做校验
  189. if conf.GlobalConfig["unittest"] == "true" && token == test.UnitTestToken {
  190. return
  191. }
  192. token = strings.ReplaceAll(strings.ReplaceAll(token, "Bearer ", ""), " ", "")
  193. if token == "" {
  194. logger.Logger.Println("token标识无效")
  195. ctx.ResponseWriter.WriteHeader(401)
  196. //ctx.Redirect(301, "/login")
  197. return
  198. }
  199. //验证请求时间戳和随机数,防数据重放
  200. tim := ctx.Request.Header.Get("auth_time")
  201. if tim == "" {
  202. logger.Logger.Println("auth_time标识缺失")
  203. ctx.ResponseWriter.WriteHeader(401)
  204. //ctx.Redirect(301, "/login")
  205. return
  206. }
  207. nowhaomiao := time.Now().Unix()
  208. tim2 := tim[0:10]
  209. timint, er := strconv.Atoi(tim2)
  210. //接口请求有效时长为10秒内
  211. if er != nil || (int(nowhaomiao)-timint) > 10000 {
  212. logger.Logger.Println("接口请求超时" + tim2 + ":" + strconv.Itoa(int(nowhaomiao)))
  213. ctx.ResponseWriter.WriteHeader(401)
  214. //ctx.Redirect(301, "/login")
  215. return
  216. }
  217. nonce := ctx.Request.Header.Get("auth_nonce")
  218. if nonce == "" {
  219. logger.Logger.Println("auth_nonce标识缺失")
  220. ctx.ResponseWriter.WriteHeader(401)
  221. //ctx.Redirect(301, "/login")
  222. return
  223. }
  224. kv, has := global.GoCahce.Get(nonce)
  225. if has == true || kv != nil {
  226. logger.Logger.Println("auth_nonce已存在,本次请求可能是接口重放攻击")
  227. ctx.ResponseWriter.WriteHeader(401)
  228. //ctx.Redirect(301, "/login")
  229. return
  230. }
  231. sign := ctx.Request.Header.Get("sign")
  232. if sign == "" {
  233. logger.Logger.Println("签名缺失")
  234. ctx.ResponseWriter.WriteHeader(401)
  235. //ctx.Redirect(301, "/login")
  236. return
  237. }
  238. //获取请求的所有参数,并进行签名对比,防数据篡改
  239. //获取请求的所有参数,并进行签名对比,防数据篡改
  240. signParalist := map[string]string{"auth_time": tim, "auth_nonce": nonce}
  241. reqPara1 := ctx.Request.URL.Query()
  242. for k, _ := range reqPara1 {
  243. signParalist[(k)] = tools.IsEmpty(reqPara1[k][0])
  244. }
  245. if ctx.Request.MultipartForm == nil || len(ctx.Request.MultipartForm.File) == 0 {
  246. reqPara2 := ctx.Request.PostForm
  247. for k, _ := range reqPara2 {
  248. signParalist[(k)] = tools.IsEmpty(reqPara2[k][0])
  249. }
  250. }
  251. var entiy []string
  252. var keys []string
  253. for key := range signParalist {
  254. keys = append(keys, (key))
  255. }
  256. sort.Stable(sort.StringSlice(keys))
  257. for _, name := range keys {
  258. /*tmpStr := url.QueryEscape(signParalist[name])
  259. tmpStr = strings.ReplaceAll(tmpStr, "%28", "(")
  260. tmpStr = strings.ReplaceAll(tmpStr, "%29", ")")*/
  261. entiy = append(entiy, name+"="+signParalist[name])
  262. }
  263. newParalist := strings.Join(entiy, "&") // + token
  264. h := md5.New()
  265. h.Write([]byte(newParalist))
  266. cipherStr := h.Sum(nil)
  267. signstr := hex.EncodeToString(cipherStr) // 输出加密结果
  268. if sign != signstr {
  269. logger.Logger.Println("签名不一致。原始签名:" + sign + " 后台签名:" + signstr)
  270. logger.Logger.Println(fmt.Sprintf("签名参数:%+v", newParalist))
  271. ctx.ResponseWriter.WriteHeader(401)
  272. //ctx.Redirect(401, "/login")
  273. return
  274. }
  275. //请求验证全部通过,更新session
  276. /*
  277. err = bo.UpdateSession(token)
  278. if err != nil {
  279. if uri != "/login" {
  280. ctx.ResponseWriter.WriteHeader(401)
  281. //ctx.Redirect(401, "/login")
  282. }
  283. return
  284. }*/
  285. //本次接口分配的随机数,10秒内过期
  286. global.GoCahce.Set(nonce, nonce, 10*time.Second)
  287. }