main.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. package main
  2. /*
  3. * SCD第二版本。
  4. * 本版本中引入了crc校验及ccd提取外部程序,该程序由C++编写,在部署时需要根据目标机器进行源码编译。其源程序在scdcrc目录下,编译时可参考目录的编译说明。
  5. * 本版本中引入了java开发的xml校验(根据xsd校验)外部程序,该程序由java编写,在部署时需要在目标机器上安装jdk1.8。务必(!!!)并将操作系统安装或者设置为中文语言,设置命令如下:
  6. * 1:全局配置修改。vi /etc/sysconfig/i18n 输入LANG="zh_CN.UTF-8"或者LANG="en_US.UTF-8",执行source /etc/sysconfig/i18n,重启电脑生效
  7. * 2:临时修改。export LANG="zh_CN.UTF-8" 或者 export LANG="en_US.UTF-8"
  8. * 3: 查看liunx系统当前语言。命令: echo $LANG
  9. */
  10. // ***************** 重新生成api接口文档,终端运行生成命令:bee run -gendoc=true -downdoc=true ***************
  11. // ***************** 如果生成swagger文档时失败,可运行:bee generate docs生成,可查看详情生成过程 ***************
  12. // ***************** 其他异常问题可尝试执行:swag init ***************
  13. // @title SCD文档管理系统API
  14. // @version 1.1.0
  15. // @description SCD文档管理系统接口文档。本文档仅用于本项目的前端接口说明
  16. // @termsOfService http://swagger.io/terms/
  17. // @contact.name liling
  18. // @contact.url http://www.swagger.io/support
  19. // @contact.email 3116246@qq.com
  20. // @license.name none
  21. // @license.url -
  22. // @host localhost:18001
  23. // @BasePath /api
  24. // @securityDefinitions.token token AUTH
  25. import (
  26. "bytes"
  27. "crypto/md5"
  28. "encoding/binary"
  29. "encoding/hex"
  30. "fmt"
  31. "io"
  32. "io/fs"
  33. "net"
  34. "net/url"
  35. "os"
  36. "os/exec"
  37. "path/filepath"
  38. "scd_check_tools/arrayex"
  39. "scd_check_tools/conf"
  40. _ "scd_check_tools/conf"
  41. "scd_check_tools/db"
  42. "scd_check_tools/email"
  43. "scd_check_tools/global"
  44. "scd_check_tools/logger"
  45. "scd_check_tools/models/bo"
  46. "scd_check_tools/models/enum"
  47. "scd_check_tools/monitor"
  48. "scd_check_tools/sms"
  49. "scd_check_tools/test"
  50. "scd_check_tools/upgrade"
  51. "syscall"
  52. //"regexp"
  53. "runtime"
  54. "scd_check_tools/mqtt"
  55. "scd_check_tools/tools"
  56. "sort"
  57. "strconv"
  58. "strings"
  59. "time"
  60. "github.com/astaxie/beego"
  61. //"github.com/astaxie/beego/logs"
  62. "github.com/astaxie/beego/context"
  63. _ "scd_check_tools/routers"
  64. //"github.com/shopspring/decimal"
  65. _ "scd_check_tools/docs"
  66. _ "scd_check_tools/models/enum"
  67. )
  68. var (
  69. cpunum = runtime.NumCPU() - 1
  70. )
  71. func main() {
  72. runtime.GOMAXPROCS(cpunum)
  73. logger.Logger.Init()
  74. //base64Str, _ := os.ReadFile("C:\\Users\\lenovo\\Desktop\\1666608995.jpg")
  75. //log.Println(string(base64Str))
  76. //tools.Base64MakePic(string(base64Str), "C:\\Users\\lenovo\\Desktop\\test.jpg")
  77. logger.Logger.Println(tools.NowTime() + " 当前操作系统:" + string(runtime.GOOS) + " " + runtime.GOARCH)
  78. conf.LoadAppConf()
  79. dbtype := tools.IsEmpty(conf.GlobalConfig["dbtype"])
  80. runmode := tools.IsEmpty(conf.GlobalConfig["runmode"])
  81. if runmode == "" {
  82. runmode = "dev"
  83. }
  84. if dbtype == "" || dbtype == "mysql" {
  85. db.ConnDB("conf/mysql-" + runmode + ".cnf")
  86. } else if dbtype == "sqlite" {
  87. db.ConnSqlite("conf/db.cnf")
  88. }
  89. /*
  90. temp := new(bo.ScdAreaMgr)
  91. temp.Init(0)
  92. temp.Reset(5000002)
  93. temp.TestAppendNode("#2主变高压侧智能终端A套")
  94. temp.TestAppendNode("220kV母线III智能终端")
  95. temp.TestAppendNode("220kV母线III测控")
  96. temp.TestAppendNode("220kV II母测控")
  97. temp.TestAppendNode("110KV昆钢II线169合并单元")
  98. temp.TestAppendNode("110KV昆钢II线168合并单元")
  99. */
  100. //logger.Logger.Println("=====")
  101. //new(bo.ScdParse).ForamtScdPath(`G:\工作目录\ME\GoProject\meishan_SCD\05.代码\scd1.1.0\scd\test\四川西昌110kV安宁变20210825.scd`)
  102. //logger.Logger.Println("=====")
  103. //bo.GetScdParseInstance().XmlParse("", `G:\工作目录\ME\GoProject\meishan_SCD\06.测试\750kVQXB.scd`, "安宁变.scd", 0)
  104. //scdNode := new(bo.ScdNode)
  105. /*
  106. scd1 := bo.GetScdParseInstance()
  107. //go scd1.Parse("0", "./static/download/test.scd", "test.scd", 0)
  108. _, errr := scd1.SchemaValid("12000005", "", "")
  109. if errr != nil {
  110. logger.Logger.Error(errr)
  111. }
  112. scdNode := new(bo.ScdNode)
  113. scdNode.RootID = 10000004
  114. scdNode.UpdateIedNode()
  115. scdNode.UpdateCommunication()
  116. scdNode.UpdateDataTypeTemplates()
  117. scdNode.RootID = 5000002
  118. scdNode.UpdateIedNode()
  119. scdNode.UpdateCommunication()
  120. scdNode.UpdateDataTypeTemplates()
  121. */
  122. /*
  123. scdNode.GetIedBlockRelations(20000008, "IM2203")
  124. queryPara := map[string]interface{}{}
  125. queryPara["scd_id"] = 20000008
  126. queryPara["m_ied_name"] = "IM2203"
  127. queryPara["s_ied_name"] = "CM2203"
  128. queryPara["m_ctrlid"] = ""
  129. queryPara["s_ctrlid"] = 20156751
  130. scdNode.GetIedCtrlInputsRelations(queryPara)
  131. */
  132. //scdNode.UpdateIedNode()
  133. //scdNode.MakeIedRelations(scdNode.RootID)
  134. //scdCompare := new(bo.ScdCompare)
  135. //scdCompare.CompareType = "SCD"
  136. //scdCompare.Sourceid = 5000002
  137. //scdCompare.Targetid = 25000010
  138. //scdCompare.Compare()
  139. go mqtt.Start()
  140. if runmode == "dev" {
  141. //自动生成接口权限定义
  142. //new(ApiDoc).Run()
  143. }
  144. new(ApiDoc).CacheApiDoc()
  145. bo.LoadSysParam()
  146. //缓存系统代码定义
  147. bo.LoadAndCacheGlobalCode()
  148. monitor.StartDataMonitor()
  149. monitor.StartSystem()
  150. //初始化短信对象
  151. smspf := conf.GlobalConfig["sms.platform"]
  152. if smspf != "" {
  153. smsSend := sms.GetSmsInstance(smspf)
  154. smsSend.AppId = conf.GlobalConfig["sms."+smspf+".appid"]
  155. smsSend.AppKey = conf.GlobalConfig["sms."+smspf+".appkey"]
  156. smsSend.SignName = conf.GlobalConfig["sms.signname"]
  157. }
  158. //启动邮件发送对象
  159. email.EmailConfig.Load()
  160. //发送邮件调用参考:
  161. //err := new(email.Send).SendEmail("3116246@qq.com", "测试", "这是邮件发送测试")
  162. logger.Logger.Println("****** 当前程序运行版本:" + upgrade.GetVersion() + " 运行端口:" + conf.GlobalConfig["appport"] + " 运行环境:" + runmode + " ******")
  163. logger.Logger.Println("如果需要重新生成api接口文档,终端运行生成命令:bee run -gendoc=true -downdoc=true")
  164. go test.Start("")
  165. go func() {
  166. //将生成的swagger文件复制到web目录下
  167. dirchar := string(os.PathSeparator)
  168. swaggerdir, err := os.Stat("." + dirchar + "docs")
  169. if os.IsNotExist(err) {
  170. //项目中没有启用swagger时,不处理
  171. return
  172. }
  173. if swaggerdir.IsDir() {
  174. filepath.Walk("."+dirchar+"docs", func(pathitem string, info fs.FileInfo, err error) error {
  175. if info.IsDir() {
  176. return nil
  177. }
  178. src, _ := os.Open(pathitem)
  179. defer src.Close()
  180. dst, _ := os.Create("." + dirchar + "static" + dirchar + "swagger" + dirchar + info.Name())
  181. defer dst.Close()
  182. io.Copy(dst, src)
  183. return nil
  184. })
  185. }
  186. }()
  187. if string(runtime.GOOS) == "windows" {
  188. if runmode == "prod" {
  189. go func() {
  190. //先杀掉残留的Web2Cs.exe进程
  191. killcmd := exec.Command("taskkill.exe", "/f", "/im", "Web2Cs.exe")
  192. killcmd.Start()
  193. time.Sleep(1 * time.Second)
  194. dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
  195. cmd := exec.Command("cmd.exe", "/c", "start "+dir+"/static/pc/Web2Cs.exe")
  196. cmd.Start()
  197. //隐藏CMD窗口
  198. kernel32 := syscall.NewLazyDLL("kernel32.dll")
  199. user32 := syscall.NewLazyDLL("user32.dll")
  200. getConsoleWindow := kernel32.NewProc("GetConsoleWindow")
  201. hideWin := user32.NewProc("ShowWindowAsync")
  202. winHander, _, err := getConsoleWindow.Call()
  203. if winHander == 0 {
  204. logger.Logger.Error(err)
  205. return
  206. }
  207. code, _, err := hideWin.Call(winHander, 0)
  208. if code != 1 {
  209. logger.Logger.Error(err)
  210. return
  211. }
  212. }()
  213. //检查Web2Cs.exe是否运行中,未运行时退出当前程序
  214. go func() {
  215. for {
  216. time.Sleep(3 * time.Second)
  217. param := []string{"/C", "wmic process list brief| findstr Web2Cs.exe"}
  218. cmd := exec.Command("cmd", param...)
  219. var stdout, stderr bytes.Buffer
  220. cmd.Stdout = &stdout
  221. cmd.Stderr = &stderr
  222. err := cmd.Run()
  223. if err != nil {
  224. logger.Logger.Error(err)
  225. }
  226. resultOut, _ := string(stdout.Bytes()), string(stderr.Bytes())
  227. if strings.ReplaceAll(resultOut, " ", "") == "" {
  228. break
  229. }
  230. }
  231. os.Exit(0)
  232. }()
  233. }
  234. }
  235. if conf.GlobalConfig["appport"] != "" {
  236. go bo.CheckSystemUser()
  237. beego.SetLevel(beego.LevelDebug)
  238. //beego.SetLogger(logs.AdapterFile, "{\"filename\": \"beego_log.log\"}")
  239. beego.SetStaticPath("/", "static/login.html")
  240. beego.SetStaticPath("/login", "static/login.html")
  241. beego.SetStaticPath("/logout", "static/login.html")
  242. beego.InsertFilter("/api/*", beego.BeforeRouter, AuthFilter)
  243. beego.Run(":" + conf.GlobalConfig["appport"])
  244. }
  245. }
  246. func BytesToInt(bys []byte) int {
  247. bytebuff := bytes.NewBuffer(bys)
  248. var data int64
  249. binary.Read(bytebuff, binary.BigEndian, &data)
  250. return int(data)
  251. }
  252. var AuthFilter = func(ctx *context.Context) {
  253. ip := ctx.Request.Header.Get("X-Real-IP")
  254. if net.ParseIP(ip) == nil {
  255. ips := ctx.Request.Header.Get("X-Forward-For")
  256. for _, i := range strings.Split(ips, ",") {
  257. if net.ParseIP(i) != nil {
  258. ip = i
  259. break
  260. }
  261. }
  262. if ip == "" {
  263. ip, _, _ = net.SplitHostPort(ctx.Request.RemoteAddr)
  264. }
  265. }
  266. if ip != "" {
  267. //当前IP是否需要检查访问授权,默认为需要
  268. isCheckThisIP := true
  269. if _, exist := global.AccessedIps.Load(ip); exist {
  270. //IP已经访问过系统,则无需检查
  271. isCheckThisIP = false
  272. }
  273. if isCheckThisIP && global.AllowAccessIps != "*" {
  274. //判断当前客户端是否在允许范围内
  275. ips := strings.Split(global.AllowAccessIps, ",")
  276. allowLogin := false
  277. for _, iplimt := range ips {
  278. if iplimt == ip {
  279. allowLogin = true
  280. break
  281. }
  282. tmppos := strings.Index(iplimt, ".*")
  283. if tmppos > 1 {
  284. //ip段
  285. if len(ip) < tmppos {
  286. continue
  287. }
  288. if ip[:tmppos] == iplimt[:tmppos] {
  289. allowLogin = true
  290. break
  291. }
  292. }
  293. }
  294. if !allowLogin {
  295. userInfo := map[string]interface{}{}
  296. userInfo["ip"] = ip
  297. userInfo["name"] = "系统内置访问控制"
  298. new(bo.SystemLog).Fail(enum.AuditType_Client, enum.LogType_commit, enum.OptEventType_System, enum.OptEventLevel_Hight, fmt.Sprintf("未授权终端(IP:%s)尝试访问系统被拦截", ip), userInfo)
  299. //c.Data["json"] = c.WarpError("系统限制:未授权的访问!")
  300. //c.ServeJSON()
  301. ctx.Redirect(430, "/static/430.html")
  302. return
  303. }
  304. }
  305. //将IP缓存到允许队列中,如果更改了允许访问的IP配置时,需要重新初始化该队列
  306. global.AccessedIps.Store(ip, 1)
  307. }
  308. u, err := url.Parse(strings.ReplaceAll(ctx.Request.RequestURI, "//", "/"))
  309. if err != nil {
  310. ctx.Redirect(500, "/error")
  311. return
  312. }
  313. uri := strings.ToLower(u.Path)
  314. if uri == "/api/logout" || uri == "/api/version" || uri == "/api/keep-alive" || arrayex.IndexOf(global.NoAuthRouter, uri) > -1 {
  315. return
  316. }
  317. var token string
  318. token = ctx.Request.Header.Get("Authorization")
  319. if token == "" {
  320. logger.Logger.Println("接口" + ctx.Request.RequestURI + "token标识缺失")
  321. ctx.ResponseWriter.WriteHeader(401)
  322. return
  323. }
  324. token = strings.ReplaceAll(strings.ReplaceAll(token, "Bearer ", ""), " ", "")
  325. if token == "" {
  326. logger.Logger.Println("接口" + ctx.Request.RequestURI + "token标识无效")
  327. ctx.ResponseWriter.WriteHeader(401)
  328. return
  329. }
  330. //校验该token有没有当前接口的访问权限
  331. if !bo.HasApiAccess(token, uri) {
  332. logger.Logger.Println("接口" + ctx.Request.RequestURI + "不能被token:" + token + "访问")
  333. ctx.ResponseWriter.WriteHeader(401)
  334. return
  335. }
  336. nonce := ctx.Request.Header.Get("auth_nonce")
  337. //判断是不是特殊的token(后台layui接口不能校验)
  338. if strings.HasPrefix(nonce, "police-s-admin-") && len(nonce) == 26 {
  339. return
  340. }
  341. //验证请求时间戳和随机数,防数据重放
  342. tim := ctx.Request.Header.Get("auth_time")
  343. if tim == "" {
  344. logger.Logger.Println("接口" + ctx.Request.RequestURI + " auth_time标识缺失")
  345. ctx.ResponseWriter.WriteHeader(401)
  346. //ctx.Redirect(301, "/login")
  347. return
  348. }
  349. nowhaomiao := time.Now().Unix()
  350. tim2 := tim[0:10]
  351. timint, er := strconv.Atoi(tim2)
  352. //接口请求有效时长为2秒内
  353. //log.Println(fmt.Sprintf("now:%d tim:%d", nowhaomiao, timint))
  354. if er != nil || (int(nowhaomiao)-timint) > 4 {
  355. logger.Logger.Println("接口" + ctx.Request.RequestURI + "请求超时。" + tim2 + ":" + strconv.Itoa(int(nowhaomiao)))
  356. ctx.ResponseWriter.WriteHeader(401)
  357. //ctx.Redirect(301, "/login")
  358. return
  359. }
  360. if nonce == "" {
  361. logger.Logger.Println("接口" + ctx.Request.RequestURI + " auth_nonce标识缺失")
  362. ctx.ResponseWriter.WriteHeader(401)
  363. //ctx.Redirect(301, "/login")
  364. return
  365. }
  366. kv, has := global.GoCahce.Get(nonce)
  367. if has == true || kv != nil {
  368. logger.Logger.Println("接口" + ctx.Request.RequestURI + " auth_nonce已存在,本次请求可能是接口重放攻击")
  369. ctx.ResponseWriter.WriteHeader(401)
  370. //ctx.Redirect(301, "/login")
  371. return
  372. }
  373. sign := ctx.Request.Header.Get("sign")
  374. if sign == "" {
  375. logger.Logger.Println("接口" + ctx.Request.RequestURI + "签名缺失")
  376. ctx.ResponseWriter.WriteHeader(401)
  377. //ctx.Redirect(301, "/login")
  378. return
  379. }
  380. //获取请求的所有参数,并进行签名对比,防数据篡改
  381. signParalist := map[string]string{"auth_time": tim, "auth_nonce": nonce}
  382. reqPara1 := ctx.Request.URL.Query()
  383. for k, _ := range reqPara1 {
  384. signParalist[(k)] = tools.IsEmpty(reqPara1[k][0])
  385. }
  386. if ctx.Request.MultipartForm == nil || len(ctx.Request.MultipartForm.File) == 0 {
  387. reqPara2 := ctx.Request.PostForm
  388. for k, _ := range reqPara2 {
  389. signParalist[(k)] = tools.IsEmpty(reqPara2[k][0])
  390. }
  391. }
  392. var entiy []string
  393. var keys []string
  394. for key := range signParalist {
  395. keys = append(keys, (key))
  396. }
  397. sort.Stable(sort.StringSlice(keys))
  398. for _, name := range keys {
  399. /*tmpStr := url.QueryEscape(signParalist[name])
  400. tmpStr = strings.ReplaceAll(tmpStr, "%28", "(")
  401. tmpStr = strings.ReplaceAll(tmpStr, "%29", ")")*/
  402. entiy = append(entiy, name+"="+signParalist[name])
  403. }
  404. newParalist := strings.Join(entiy, "&") // + token
  405. h := md5.New()
  406. h.Write([]byte(newParalist))
  407. cipherStr := h.Sum(nil)
  408. signstr := hex.EncodeToString(cipherStr) // 输出加密结果
  409. if sign != signstr {
  410. logger.Logger.Println("接口" + ctx.Request.RequestURI + "签名不一致,请检查参数值中是否有特殊字符。原始签名:" + sign + " 后台签名:" + signstr)
  411. logger.Logger.Println("接口" + ctx.Request.RequestURI + "签名参数:")
  412. logger.Logger.Println(newParalist)
  413. ctx.ResponseWriter.WriteHeader(401)
  414. //ctx.Redirect(401, "/login")
  415. return
  416. }
  417. //请求验证全部通过,更新session
  418. err = bo.UpdateSession(token)
  419. if err != nil {
  420. if uri != "/login" {
  421. logger.Logger.Error("接口" + ctx.Request.RequestURI + " token已过期:" + token)
  422. ctx.ResponseWriter.WriteHeader(401)
  423. //ctx.Redirect(401, "/login")
  424. }
  425. return
  426. }
  427. //10秒过期的随机数
  428. global.GoCahce.Set(nonce, nonce, 10*time.Second)
  429. }