加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
main.go 6.36 KB
一键复制 编辑 原始数据 按行查看 历史
李强 提交于 2023-05-05 16:14 . first commit
package main
import (
"embed"
"flag"
"fmt"
"io/fs"
"iptables-admin/common"
"iptables-admin/router"
"iptables-admin/utils"
"net/http"
"os"
"regexp"
"strings"
"time"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
)
//go:embed web/dist
var embedFrontend embed.FS
var (
username string // IPT_WEB_USERNAME
password string // IPT_WEB_PASSWORD
port string // IPT_WEB_PORT
verifyArgs *regexp.Regexp
)
func init() {
flag.StringVar(&username, "u", "admin", "login username")
flag.StringVar(&password, "p", "admin", "login password")
flag.StringVar(&port, "a", "9999", "http listen on")
flag.Parse()
if v := os.Getenv("IPT_WEB_USERNAME"); len(v) > 0 {
username = v
}
if v := os.Getenv("IPT_WEB_PASSWORD"); len(v) > 0 {
password = v
}
if v := os.Getenv("IPT_WEB_PORT"); len(v) > 0 {
port = v
}
// 验证表名链名,防止注入
verifyArgs, _ = regexp.Compile(`^[0-9A-z-_]+$`)
}
func main() {
r := gin.Default()
initRouter(r)
r.Use(Cors())
r.Use(argsFilter())
router := router.NewRouter()
noauth := r.Group("../api2")
noauth.POST("/login", login)
auth := r.Group("../api", auth())
auth.GET("/version", router.Version)
auth.POST("/listRule", router.ListRule)
auth.POST("/listExec", router.ListExec)
auth.POST("/flushRule", router.FlushRule)
auth.POST("/deleteRule", router.DeleteRule)
auth.POST("/flushMetrics", router.FlushMetrics)
auth.POST("/getRuleInfo", router.GetRuleInfo)
auth.POST("/flushEmptyCustomChain", router.FlushEmptyCustomChain)
auth.POST("/export", router.Export)
auth.POST("/import", router.Import)
auth.POST("/exec", router.Exec)
r.Run(":" + port)
}
func login(c *gin.Context) {
var user utils.User
err := c.ShouldBind(&user)
if err != nil {
fmt.Println(err)
}
if user.Username == username && user.Password == password {
token, err := utils.GenToken(user)
if err != nil {
fmt.Println(err)
}
c.JSON(200, common.OK(token))
} else {
c.JSON(200, common.Error(201, "用户名或密码不正确"))
}
}
func initRouter(r *gin.Engine) {
r.Use(cors.New(cors.Config{
AllowOrigins: []string{"*"},
AllowMethods: []string{"GET", "POST", "DELETE", "PUT", "HEAD", "OPTIONS"},
AllowHeaders: []string{"Origin", "authorization", "content-type"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
MaxAge: 12 * time.Hour,
}))
r.GET("/", func(c *gin.Context) {
c.Status(http.StatusFound)
c.Writer.Header().Set("Location", "./dashbord/")
})
front, err := fs.Sub(embedFrontend, "web/dist")
if err != nil {
fmt.Println(err)
}
HTTPCacheForUI(r)
r.StaticFS("/dashbord/", http.FS(front))
r.NoRoute(func(c *gin.Context) {
fmt.Println("=========>c.Request.RequestURI", c.Request.RequestURI)
if strings.HasPrefix(c.Request.RequestURI, "/dashbord/") {
path := strings.TrimPrefix(c.Request.RequestURI, "/dashbord/")
locationPath := strings.Repeat("../", strings.Count(path, "/"))
c.Status(http.StatusFound)
c.Writer.Header().Set("Location", "./"+locationPath)
}
})
}
func HTTPCacheForUI(app *gin.Engine) {
app.Use(func(c *gin.Context) {
if c.Request.Method == "GET" || c.Request.Method == "HEAD" {
if strings.Contains(c.Request.RequestURI, "/dashbord/static/") {
c.Writer.Header().Set("cache-control", "public, max-age=2592000")
c.Writer.Header().Set("expires", time.Now().Add(time.Hour*24*30).Format(time.RFC1123))
if strings.Contains(c.Request.RequestURI, ".js") {
c.Writer.Header().Set("content-type", "application/javascript")
}
if strings.Contains(c.Request.RequestURI, ".css") {
c.Writer.Header().Set("content-type", "text/css; charset=utf-8")
}
}
}
c.Next()
})
}
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
origin := c.Request.Header.Get("Origin") //请求头部
if origin != "" {
//接收客户端发送的origin (重要!)
//Access-Control-Allow-Origin是必须的,他的值要么是请求Origin字段的值,要么是一个*, 表示接受任意域名的请求
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
//服务器支持的所有跨域请求的方法
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
//允许跨域设置可以返回其他子段,可以自定义字段
//该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。
//如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。
c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token, session")
// 允许浏览器(客户端)可以解析的头部 (重要)
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
//设置缓存时间
//该字段可选,用来指定本次预检请求的有效期,单位为秒。有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。
c.Header("Access-Control-Max-Age", "172800")
//允许客户端传递校验信息比如 cookie (重要)
c.Header("Access-Control-Allow-Credentials", "true")
}
//允许类型校验
if method == "OPTIONS" {
c.JSON(http.StatusOK, "ok!")
}
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
c.Next()
}
}
func argsFilter() gin.HandlerFunc {
return func(c *gin.Context) {
table := c.DefaultQuery("table", "")
if len(table) > 0 && !verifyArgs.MatchString(table) {
c.JSON(200, common.Error(200, "参数错误"))
return
}
chain := c.DefaultQuery("chain", "")
if len(chain) > 0 && !verifyArgs.MatchString(chain) {
c.JSON(200, common.Error(200, "参数错误"))
return
}
c.Next()
}
}
func auth() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.PostForm("token")
if token == "" {
token = c.DefaultQuery("token", "")
}
if token == "" {
token = c.Request.Header.Get("token")
}
if token == "" {
c.Abort()
c.JSON(200, common.Error(204, "非法访问"))
return
}
_, err := utils.GetTokenUser(token)
if err != nil {
//token过期
c.Abort()
c.JSON(200, common.Error(204, "token已过期"))
return
} else {
c.Next()
}
}
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化