123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- package main
- /*
- * 本检测工具基于SCD文档管理系统第二版本开发
- * 本版本中引入了crc校验及ccd提取外部程序,该程序由C++编写,在部署时需要根据目标机器进行源码编译。其源程序在scdcrc目录下,编译时可参考目录的编译说明。
- * 本版本中引入了java开发的xml校验(根据xsd校验)外部程序,该程序由java编写,在部署时需要在目标机器上安装jdk1.8。务必(!!!)并将操作系统安装或者设置为中文语言,设置命令如下:
- * 1:全局配置修改。vi /etc/sysconfig/i18n 输入LANG="zh_CN.UTF-8"或者LANG="en_US.UTF-8",执行source /etc/sysconfig/i18n,重启电脑生效
- * 2:临时修改。export LANG="zh_CN.UTF-8" 或者 export LANG="en_US.UTF-8"
- * 3: 查看liunx系统当前语言。命令: echo $LANG
- */
- // ***************** 重新生成api接口文档,终端运行生成命令:bee run -gendoc=true -downdoc=true ***************
- // ***************** 如果生成swagger文档时失败,可运行:bee generate docs生成,可查看详情生成过程 ***************
- // ***************** 其他异常问题可尝试执行:swag init ***************
- // @title SCD检测工具API
- // @version 1.0.0
- // @description SCD文档管理系统接口文档。本文档仅用于本项目的前端接口说明
- // @termsOfService http://swagger.io/terms/
- // @contact.name liling
- // @contact.url http://www.swagger.io/support
- // @contact.email 3116246@qq.com
- // @license.name none
- // @license.url -
- // @host localhost:9527
- // @BasePath /api
- // @securityDefinitions.token token AUTH
- import (
- "bytes"
- "crypto/md5"
- "encoding/binary"
- "encoding/hex"
- "flag"
- "fmt"
- "io"
- "io/fs"
- "net"
- "net/http"
- "net/url"
- "os"
- "os/exec"
- "path/filepath"
- "scd_check_tools/arrayex"
- "scd_check_tools/conf"
- _ "scd_check_tools/conf"
- "scd_check_tools/db"
- "scd_check_tools/email"
- "scd_check_tools/global"
- "scd_check_tools/logger"
- "scd_check_tools/models/bo"
- "scd_check_tools/models/enum"
- "scd_check_tools/monitor"
- "scd_check_tools/sms"
- "scd_check_tools/test"
- "scd_check_tools/upgrade"
- "syscall"
- //"regexp"
- "runtime"
- "scd_check_tools/mqtt"
- "scd_check_tools/tools"
- "sort"
- "strconv"
- "strings"
- "time"
- "github.com/astaxie/beego"
- //"github.com/astaxie/beego/logs"
- "github.com/astaxie/beego/context"
- _ "scd_check_tools/routers"
- //"github.com/shopspring/decimal"
- _ "scd_check_tools/docs"
- _ "scd_check_tools/models/enum"
- "github.com/pkg/browser"
- )
- var (
- cpunum = runtime.NumCPU() - 1
- )
- var (
- openBrowser = flag.Bool("open", true, "Open browser automatically")
- )
- func main() {
- runtime.GOMAXPROCS(cpunum)
- flag.Parse()
- logger.Logger.Init()
- logger.Logger.Println(tools.NowTime() + " 当前操作系统:" + string(runtime.GOOS) + " " + runtime.GOARCH)
- conf.LoadAppConf()
- dbtype := tools.IsEmpty(conf.GlobalConfig["dbtype"])
- runmode := tools.IsEmpty(conf.GlobalConfig["runmode"])
- if runmode == "" {
- runmode = "dev"
- }
- if dbtype == "" || dbtype == "mysql" {
- db.ConnDB("conf/mysql-" + runmode + ".cnf")
- } else if dbtype == "sqlite" {
- db.ConnSqlite("conf/db.cnf")
- }
- //new(test.UnitTest).TestIntAddrParse("RPIT/GOINGGIO1.SPCSO1.stVal")
- //new(test.UnitTest).TestDoiNameRex("(断路器)&(跳|合|闸){1,}&(位置)", "断路器跳闸位置")
- //new(test.UnitTest).TestDoiNameRex("(测量|计量)&(电流.*C)|(C.*电流)|(IC1)", "测量电流C相")
- go mqtt.Start()
- if runmode == "dev" {
- //自动生成接口权限定义
- new(ApiDoc).Run()
- }
- new(ApiDoc).CacheApiDoc()
- bo.LoadSysParam()
- //缓存系统代码定义
- bo.LoadAndCacheGlobalCode()
- monitor.StartDataMonitor()
- monitor.StartSystem()
- new(bo.TaskMgr).ChangeErr2Normal()
- //初始化短信对象
- smspf := conf.GlobalConfig["sms.platform"]
- if smspf != "" {
- smsSend := sms.GetSmsInstance(smspf)
- smsSend.AppId = conf.GlobalConfig["sms."+smspf+".appid"]
- smsSend.AppKey = conf.GlobalConfig["sms."+smspf+".appkey"]
- smsSend.SignName = conf.GlobalConfig["sms.signname"]
- }
- //启动邮件发送对象
- email.EmailConfig.Load()
- //发送邮件调用参考:
- //err := new(email.Send).SendEmail("3116246@qq.com", "测试", "这是邮件发送测试")
- logger.Logger.Println("****** 当前程序运行版本:" + upgrade.GetVersion() + " 运行端口:" + conf.GlobalConfig["appport"] + " 运行环境:" + runmode + " ******")
- logger.Logger.Println("如果需要重新生成api接口文档,终端运行生成命令:bee run -gendoc=true -downdoc=true")
- go test.Start("")
- 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
- })
- }
- }()
- if string(runtime.GOOS) == "windows" {
- if runmode == "prod" {
- go func() {
- killcmd := exec.Command("taskkill.exe", "/f", "/im", "Web2Cs.exe")
- killcmd.Start()
- //先杀掉残留的Web2Cs.exe进程
- time.Sleep(1 * time.Second)
- //dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
- //cmd := exec.Command("cmd.exe", "/c", "start "+dir+"/static/pc/Web2Cs.exe")
- //cmd.Start()
- //隐藏CMD窗口
- //如果编译的非windows版本,需要注释下面的代码
- kernel32 := syscall.NewLazyDLL("kernel32.dll")
- user32 := syscall.NewLazyDLL("user32.dll")
- getConsoleWindow := kernel32.NewProc("GetConsoleWindow")
- hideWin := user32.NewProc("ShowWindowAsync")
- winHander, _, err := getConsoleWindow.Call()
- if winHander == 0 {
- logger.Logger.Error(err)
- return
- }
- code, _, err := hideWin.Call(winHander, 0)
- if code != 1 {
- logger.Logger.Error(err)
- return
- }
- }()
- //检查Web2Cs.exe是否运行中,未运行时退出当前程序
- go func() {
- /*
- for {
- time.Sleep(3 * time.Second)
- param := []string{"/C", "wmic process list brief| findstr Web2Cs.exe"}
- cmd := exec.Command("cmd", param...)
- var stdout, stderr bytes.Buffer
- cmd.Stdout = &stdout
- cmd.Stderr = &stderr
- err := cmd.Run()
- if err != nil {
- logger.Logger.Error(err)
- }
- resultOut, _ := string(stdout.Bytes()), string(stderr.Bytes())
- if strings.ReplaceAll(resultOut, " ", "") == "" {
- break
- }
- }
- os.Exit(0)
- */
- }()
- }
- }
- if conf.GlobalConfig["appport"] != "" {
- go bo.CheckSystemUser()
- beego.SetLevel(beego.LevelDebug)
- //beego.SetLogger(logs.AdapterFile, "{\"filename\": \"beego_log.log\"}")
- beego.SetStaticPath("/", "static/index.html")
- beego.SetStaticPath("/login", "static/index.html")
- //beego.SetStaticPath("/logout", "static/login.html")
- beego.InsertFilter("/*", beego.BeforeRouter, AuthFilter)
- if *openBrowser {
- go func() {
- time.Sleep(100 * time.Millisecond)
- browser.OpenURL("http://127.0.0.1:" + conf.GlobalConfig["appport"])
- }()
- }
- beego.Run(":" + conf.GlobalConfig["appport"])
- }
- }
- func BytesToInt(bys []byte) int {
- bytebuff := bytes.NewBuffer(bys)
- var data int64
- binary.Read(bytebuff, binary.BigEndian, &data)
- return int(data)
- }
- var staticFile = ".css,.html,.js,.png,.jpg,.gif,.svg,.ico,.ttf,.eot,.woff,.woff2,.map"
- var AuthFilter = func(ctx *context.Context) {
- //fmt.Println(ctx.Request.URL.Path)
- ps := strings.Split(ctx.Request.URL.Path, ".")
- if strings.Index(staticFile, "."+ps[len(ps)-1]) > -1 {
- http.ServeFile(ctx.ResponseWriter, ctx.Request, "static/"+ctx.Request.URL.Path)
- return
- }
- 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.AccessedIps.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.AccessedIps.Store(ip, 1)
- }
- 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" || uri == "/api/keep-alive" || arrayex.IndexOf(global.NoAuthRouter, uri) > -1 {
- return
- }
- var token string
- token = ctx.Request.Header.Get("Authorization")
- if token == "" {
- logger.Logger.Println("接口" + ctx.Request.RequestURI + "token标识缺失")
- ctx.ResponseWriter.WriteHeader(401)
- return
- }
- token = strings.ReplaceAll(strings.ReplaceAll(token, "Bearer ", ""), " ", "")
- if token == "" {
- logger.Logger.Println("接口" + ctx.Request.RequestURI + "token标识无效")
- ctx.ResponseWriter.WriteHeader(401)
- return
- }
- //校验该token有没有当前接口的访问权限
- if !bo.HasApiAccess(token, uri) {
- logger.Logger.Println("接口" + ctx.Request.RequestURI + "不能被token:" + token + "访问")
- ctx.ResponseWriter.WriteHeader(401)
- return
- }
- nonce := ctx.Request.Header.Get("auth_nonce")
- //判断是不是特殊的token(后台layui接口不能校验)
- if strings.HasPrefix(nonce, "police-s-admin-") && len(nonce) == 26 {
- return
- }
- //验证请求时间戳和随机数,防数据重放
- tim := ctx.Request.Header.Get("auth_time")
- if tim == "" {
- logger.Logger.Println("接口" + ctx.Request.RequestURI + " auth_time标识缺失")
- ctx.ResponseWriter.WriteHeader(401)
- //ctx.Redirect(301, "/login")
- return
- }
- nowhaomiao := time.Now().Unix()
- tim2 := tim[0:10]
- timint, er := strconv.Atoi(tim2)
- //接口请求有效时长为2秒内
- //log.Println(fmt.Sprintf("now:%d tim:%d", nowhaomiao, timint))
- if er != nil || (int(nowhaomiao)-timint) > 4 {
- logger.Logger.Println("接口" + ctx.Request.RequestURI + "请求超时。" + tim2 + ":" + strconv.Itoa(int(nowhaomiao)))
- ctx.ResponseWriter.WriteHeader(401)
- //ctx.Redirect(301, "/login")
- return
- }
- if nonce == "" {
- logger.Logger.Println("接口" + ctx.Request.RequestURI + " 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("接口" + ctx.Request.RequestURI + " auth_nonce已存在,本次请求可能是接口重放攻击")
- ctx.ResponseWriter.WriteHeader(401)
- //ctx.Redirect(301, "/login")
- return
- }
- sign := ctx.Request.Header.Get("sign")
- if sign == "" {
- logger.Logger.Println("接口" + ctx.Request.RequestURI + "签名缺失")
- 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("接口" + ctx.Request.RequestURI + "签名不一致,请检查参数值中是否有特殊字符。原始签名:" + sign + " 后台签名:" + signstr)
- logger.Logger.Println("接口" + ctx.Request.RequestURI + "签名参数:")
- logger.Logger.Println(newParalist)
- ctx.ResponseWriter.WriteHeader(401)
- //ctx.Redirect(401, "/login")
- return
- }
- //请求验证全部通过,更新session
- err = bo.UpdateSession(token)
- if err != nil {
- if uri != "/login" {
- logger.Logger.Error("接口" + ctx.Request.RequestURI + " token已过期:" + token)
- ctx.ResponseWriter.WriteHeader(401)
- //ctx.Redirect(401, "/login")
- }
- return
- }
- //3秒过期的随机数
- global.GoCahce.Set(nonce, nonce, 3*time.Second)
- }
|