package bootstrap import ( "crypto/md5" "encoding/hex" "fmt" "io" "io/fs" "os" "path/filepath" "rtzh_elec_temperature/enum" "rtzh_elec_temperature/test" "net" "net/url" "rtzh_elec_temperature/arrayex" "rtzh_elec_temperature/conf" "rtzh_elec_temperature/controllers" "rtzh_elec_temperature/db" "rtzh_elec_temperature/global" "rtzh_elec_temperature/logger" "rtzh_elec_temperature/models/bo" "rtzh_elec_temperature/monitor" "rtzh_elec_temperature/mqtt" "rtzh_elec_temperature/rtelec_app_public_lib/service" "rtzh_elec_temperature/sms" "rtzh_elec_temperature/tools" "rtzh_elec_temperature/upgrade" "runtime" "sort" "strconv" "strings" "time" "github.com/astaxie/beego/context" "github.com/astaxie/beego" "github.com/gin-gonic/gin" ) func Run() { logger.Logger.Init() logger.Logger.Println("****** 当前操作系统:" + string(runtime.GOOS) + " " + runtime.GOARCH + " ******") conf.LoadAppConf() dbtype := tools.IsEmpty(conf.GlobalConfig["dbtype"]) runmode := tools.IsEmpty(conf.GlobalConfig["runmode"]) if dbtype == "" || dbtype == "mysql" { db.ConnDB("conf/mysql-" + runmode + ".cnf") } else if dbtype == "sqlite" { db.ConnSqlite("conf/sqlite-" + runmode + ".cnf") } //连接mqtt go mqtt.Start() go bo.LoadSysParam() go monitor.StartDataMonitor() //初始化短信对象 go sms.StartSms() //启动与管理app的服务 service.RtelecManageApp() //启动WS数据接收进程 go controllers.ReceiveData() //启动WS数据发送进程 go controllers.SendData() if conf.GlobalConfig["wsport"] != "" { logger.Logger.Println("WS服务 Listen:" + conf.GlobalConfig["wsport"]) var g1 = func() { r := gin.New() r.GET("/", controllers.WebSocketBase) err := r.Run(":" + conf.GlobalConfig["wsport"]) if err != nil { return } } go g1() } beego.SetLevel(beego.LevelDebug) //启动web服务 beego.SetStaticPath("/", "static/login.html") beego.SetStaticPath("/login", "static/login.html") beego.SetStaticPath("/logout", "static/login.html") beego.SetStaticPath("/swagger", "/static/swagger/index.html") beego.InsertFilter("/api/*", beego.BeforeRouter, AuthFilter) logger.Logger.Println("****** 当前程序运行版本:" + upgrade.GetVersion() + " 运行环境:" + runmode + " ******") logger.Logger.Println("如果需要重新生成api接口文档,终端运行生成命令:bee run -gendoc=true -downdoc=true") go func() { //将生成的swagger文件复制到web目录下 dirchar := string(os.PathSeparator) swaggerdir, err := os.Stat("." + dirchar + "swagger") if os.IsNotExist(err) { //项目中没有启用swagger时,不处理 return } if swaggerdir.IsDir() { filepath.Walk("."+dirchar+"swagger", func(pathitem string, info fs.FileInfo, err error) error { if info.IsDir() { return nil } src, _ := os.Open(pathitem) defer src.Close() dst, _ := os.Create("." + dirchar + "static" + dirchar + "swagger" + dirchar + info.Name()) defer dst.Close() io.Copy(dst, src) return nil }) } }() //根据配置的运行模型启动单元测试 test.Start("testtoken") if conf.GlobalConfig["enablehttps"] == "true" { logger.Logger.Println("启动应用" + conf.GlobalConfig["appname"] + "端口号:" + conf.GlobalConfig["httpsport"]) beego.Run() } else { if conf.GlobalConfig["appport"] != "" { logger.Logger.Println("启动应用" + conf.GlobalConfig["appname"] + "端口号:" + conf.GlobalConfig["appport"]) beego.Run(":" + conf.GlobalConfig["appport"]) } } } var AuthFilter = func(ctx *context.Context) { ip := ctx.Request.Header.Get("X-Real-IP") if net.ParseIP(ip) == nil { ips := ctx.Request.Header.Get("X-Forward-For") for _, i := range strings.Split(ips, ",") { if net.ParseIP(i) != nil { ip = i break } } if ip == "" { ip, _, _ = net.SplitHostPort(ctx.Request.RemoteAddr) } } if ip != "" { //当前IP是否需要检查访问授权,默认为需要 isCheckThisIP := true if _, exist := global.AccessedIpsList.Load(ip); exist { //IP已经访问过系统,则无需检查 isCheckThisIP = false } if isCheckThisIP && global.AllowAccessIps != "*" { //判断当前客户端是否在允许范围内 ips := strings.Split(global.AllowAccessIps, ",") allowLogin := false for _, iplimt := range ips { if iplimt == ip { allowLogin = true break } tmppos := strings.Index(iplimt, ".*") if tmppos > 1 { //ip段 if len(ip) < tmppos { continue } if ip[:tmppos] == iplimt[:tmppos] { allowLogin = true break } } } if !allowLogin { userInfo := map[string]interface{}{} userInfo["ip"] = ip userInfo["name"] = "访问控制" new(bo.SystemLog).Fail(enum.AuditType_Client, enum.LogType_commit, enum.OptEventType_System, enum.OptEventLevel_Hight, fmt.Sprintf("未授权终端(IP:%s)尝试访问系统被拦截", ip), userInfo) //c.Data["json"] = c.WarpError("系统限制:未授权的访问!") //c.ServeJSON() ctx.Redirect(430, "/static/430.html") return } } //将IP缓存到允许队列中,如果更改了允许访问的IP配置时,需要重新初始化该队列 global.AccessedIpsList.Store(ip, "allow") } logger.Logger.Debug(fmt.Sprintf("接收到请求URL:%s", ctx.Request.RequestURI)) u, err := url.Parse(strings.ReplaceAll(ctx.Request.RequestURI, "//", "/")) if err != nil { ctx.Redirect(500, "/error") return } uri := strings.ToLower(u.Path) if uri == "/api/logout" || uri == "/api/version" || arrayex.IndexOf(global.NoAuthRouter, uri) > -1 { return } var token string token = ctx.Request.Header.Get("Authorization") if token == "" { logger.Logger.Println("token标识缺失") ctx.ResponseWriter.WriteHeader(401) //ctx.Redirect(401, "/login") return } //单元测试模式时,不做校验 if conf.GlobalConfig["unittest"] == "true" && token == test.UnitTestToken { return } token = strings.ReplaceAll(strings.ReplaceAll(token, "Bearer ", ""), " ", "") if token == "" { logger.Logger.Println("token标识无效") ctx.ResponseWriter.WriteHeader(401) //ctx.Redirect(301, "/login") return } //验证请求时间戳和随机数,防数据重放 tim := ctx.Request.Header.Get("auth_time") if tim == "" { logger.Logger.Println("auth_time标识缺失") ctx.ResponseWriter.WriteHeader(401) //ctx.Redirect(301, "/login") return } nowhaomiao := time.Now().Unix() tim2 := tim[0:10] timint, er := strconv.Atoi(tim2) //接口请求有效时长为10秒内 if er != nil || (int(nowhaomiao)-timint) > 10000 { logger.Logger.Println("接口请求超时" + tim2 + ":" + strconv.Itoa(int(nowhaomiao))) ctx.ResponseWriter.WriteHeader(401) //ctx.Redirect(301, "/login") return } nonce := ctx.Request.Header.Get("auth_nonce") if nonce == "" { logger.Logger.Println("auth_nonce标识缺失") ctx.ResponseWriter.WriteHeader(401) //ctx.Redirect(301, "/login") return } kv, has := global.GoCahce.Get(nonce) if has == true || kv != nil { logger.Logger.Println("auth_nonce已存在,本次请求可能是接口重放攻击") ctx.ResponseWriter.WriteHeader(401) //ctx.Redirect(301, "/login") return } sign := ctx.Request.Header.Get("sign") if sign == "" { logger.Logger.Println("签名缺失") ctx.ResponseWriter.WriteHeader(401) //ctx.Redirect(301, "/login") return } //获取请求的所有参数,并进行签名对比,防数据篡改 //获取请求的所有参数,并进行签名对比,防数据篡改 signParalist := map[string]string{"auth_time": tim, "auth_nonce": nonce} reqPara1 := ctx.Request.URL.Query() for k, _ := range reqPara1 { signParalist[(k)] = tools.IsEmpty(reqPara1[k][0]) } if ctx.Request.MultipartForm == nil || len(ctx.Request.MultipartForm.File) == 0 { reqPara2 := ctx.Request.PostForm for k, _ := range reqPara2 { signParalist[(k)] = tools.IsEmpty(reqPara2[k][0]) } } var entiy []string var keys []string for key := range signParalist { keys = append(keys, (key)) } sort.Stable(sort.StringSlice(keys)) for _, name := range keys { /*tmpStr := url.QueryEscape(signParalist[name]) tmpStr = strings.ReplaceAll(tmpStr, "%28", "(") tmpStr = strings.ReplaceAll(tmpStr, "%29", ")")*/ entiy = append(entiy, name+"="+signParalist[name]) } newParalist := strings.Join(entiy, "&") // + token h := md5.New() h.Write([]byte(newParalist)) cipherStr := h.Sum(nil) signstr := hex.EncodeToString(cipherStr) // 输出加密结果 if sign != signstr { logger.Logger.Println("签名不一致。原始签名:" + sign + " 后台签名:" + signstr) logger.Logger.Println(fmt.Sprintf("签名参数:%+v", newParalist)) ctx.ResponseWriter.WriteHeader(401) //ctx.Redirect(401, "/login") return } //请求验证全部通过,更新session /* err = bo.UpdateSession(token) if err != nil { if uri != "/login" { ctx.ResponseWriter.WriteHeader(401) //ctx.Redirect(401, "/login") } return }*/ //本次接口分配的随机数,10秒内过期 global.GoCahce.Set(nonce, nonce, 10*time.Second) }