From 2455f0d897bee95d96455cea72fa01fd49d82154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E8=B6=85?= <17805310388@139.com> Date: Wed, 10 Jan 2024 16:05:15 +0800 Subject: [PATCH] add models and jwt --- .vscode/launch.json | 15 ++++ conf/conf.ini | 22 +++++ conf/config.go | 65 ++++++++++++++ go.mod | 11 ++- go.sum | 35 ++++++++ handlers/response.go | 82 +++++++++++++++++ handlers/users.go | 127 ++++++++++++++++++++++++++ main.go | 55 +++++++++++- models/init.go | 56 ++++++++++++ models/users.go | 68 ++++++++++++++ pkg/jwt/auth.go | 77 ++++++++++++++++ pkg/util/request.go | 44 +++++++++ pkg/util/sonyflake.go | 183 ++++++++++++++++++++++++++++++++++++++ pkg/util/util.go | 42 +++++++++ pkg/util/uuid.go | 19 ++++ serializers/pagination.go | 30 +++++++ serializers/users.go | 27 ++++++ templates/login.tmpl | 33 +++++++ templates/page1.tmpl | 3 + 19 files changed, 990 insertions(+), 4 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 conf/conf.ini create mode 100644 conf/config.go create mode 100644 handlers/response.go create mode 100644 handlers/users.go create mode 100644 models/init.go create mode 100644 models/users.go create mode 100644 pkg/jwt/auth.go create mode 100644 pkg/util/request.go create mode 100644 pkg/util/sonyflake.go create mode 100644 pkg/util/util.go create mode 100644 pkg/util/uuid.go create mode 100644 serializers/pagination.go create mode 100644 serializers/users.go create mode 100644 templates/login.tmpl diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..608d3c6 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}" + } + ] +} \ No newline at end of file diff --git a/conf/conf.ini b/conf/conf.ini new file mode 100644 index 0000000..da0fa6e --- /dev/null +++ b/conf/conf.ini @@ -0,0 +1,22 @@ +[mysql] +Type = mysql +Host = 127.0.0.1 +Port = 3306 +User = root +Password = root +DB = login +Charset = utf8mb4 +;Prefix = gin_ + +[jwt] +SecretKey = \x13\xbf\xd2 1\xce\x8b\xc1\t\xc1=\xec\x07\x93\xd4\x9e\xbco\xb0Z + +[project] +StaticUrlMapPath = {"assets/static/": "static/", "assets/docs/": "docs/", "media/": "media/"} +TemplateGlob = templates/**/* +MediaFilePath = "media/upload/" + +[server] +Port = 7890 +ReadTimeout = 60 +WriteTimeout = 60 diff --git a/conf/config.go b/conf/config.go new file mode 100644 index 0000000..fa3618b --- /dev/null +++ b/conf/config.go @@ -0,0 +1,65 @@ +/* +@Time : 2020/6/28 21:48 +@Author : xuyiqing +@File : config.py +*/ + +package conf + +import ( + "github.com/go-ini/ini" + "time" +) + +type SqlDataBase struct { + Type string + Host string + Port string + User string + Password string + DB string + Charset string + Prefix string +} + +type Jwt struct { + SecretKey string +} + +type Project struct { + StaticUrlMapPath string + TemplateGlob string + MediaFilePath string +} + +type Server struct { + Port string + ReadTimeout time.Duration + WriteTimeout time.Duration +} + +var ( + DataBase = &SqlDataBase{} + JwtSecretKey = &Jwt{} + ProjectCfg = &Project{} + HttpServer = &Server{} +) + +func SetUp() { + cfg, err := ini.Load("conf/conf.ini") + if err != nil { + panic(err) + } + if err := cfg.Section("mysql").MapTo(DataBase); err != nil { + panic(err) + } + if err := cfg.Section("jwt").MapTo(JwtSecretKey); err != nil { + panic(err) + } + if err := cfg.Section("project").MapTo(ProjectCfg); err != nil { + panic(err) + } + if err := cfg.Section("server").MapTo(HttpServer); err != nil { + panic(err) + } +} diff --git a/go.mod b/go.mod index ff4d4eb..29e985d 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,13 @@ module go_blog go 1.20 -require github.com/gin-gonic/gin v1.9.1 +require ( + github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/gin-gonic/gin v1.9.1 + github.com/go-ini/ini v1.67.0 + github.com/jinzhu/gorm v1.9.16 + golang.org/x/crypto v0.9.0 +) require ( github.com/bytedance/sonic v1.9.1 // indirect @@ -12,7 +18,9 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/leodido/go-urn v1.2.4 // indirect @@ -23,7 +31,6 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.9.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect diff --git a/go.sum b/go.sum index 0e0c33a..2f30501 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= +github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= @@ -7,12 +9,20 @@ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583j github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= +github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= @@ -20,12 +30,22 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= +github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= +github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -33,8 +53,12 @@ github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZX github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= +github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= +github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -62,14 +86,25 @@ github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= diff --git a/handlers/response.go b/handlers/response.go new file mode 100644 index 0000000..02f0a3b --- /dev/null +++ b/handlers/response.go @@ -0,0 +1,82 @@ +/* +@Time : 2020/6/28 22:37 +@Author : xuyiqing +@File : response.py +*/ + +package handlers + +import ( + "github.com/gin-gonic/gin" +) + +const ( + Success = 200 + BadRequest = 400 + Unauthenticated = 401 + NoPermisson = 403 + NotFund = 404 + ServerError = 500 +) + +type Response struct { + Ctx *gin.Context +} + +// 定义基础返回结构体 +type JsonResponse struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data interface{} `json:"data,omitempty"` + Pager interface{} `json:"pager,omitempty"` +} + +func (resp *Response) Response(data interface{}, pager interface{}) { + resp.Ctx.JSON(Success, JsonResponse{ + Code: Success, + Msg: "返回成功", + Data: data, + Pager: pager, + }) +} + +// 400错误请求 +func (resp *Response) BadRequest(msg string) { + resp.Ctx.AbortWithStatusJSON(Success, JsonResponse{ + Code: BadRequest, + Msg: msg, + }) +} + + +// 401未登录验证 +func (resp *Response) Unauthenticated(msg string) { + resp.Ctx.AbortWithStatusJSON(Success, JsonResponse{ + Code: Unauthenticated, + Msg: msg, + }) +} + +// 403没有权限 +func (resp *Response) NoPermisson(msg string) { + resp.Ctx.AbortWithStatusJSON(Success, JsonResponse{ + Code: NoPermisson, + Msg: msg, + }) +} + +// 404资源不存在 +func (resp *Response) NotFund(msg string) { + resp.Ctx.AbortWithStatusJSON(Success, JsonResponse{ + Code: NotFund, + Msg: msg, + }) +} + +// 500服务器出错 +func (resp *Response) ServerError(msg string) { + resp.Ctx.AbortWithStatusJSON(200, JsonResponse{ + Code: ServerError, + Msg: msg, + }) +} diff --git a/handlers/users.go b/handlers/users.go new file mode 100644 index 0000000..13f5f13 --- /dev/null +++ b/handlers/users.go @@ -0,0 +1,127 @@ +/* +@Time : 2020/6/28 21:40 +@Author : xuyiqing +@File : users.py +*/ + +package handlers + +import ( + "fmt" + "go_blog/models" + "go_blog/pkg/jwt" + "go_blog/pkg/util" + "go_blog/serializers" + + "github.com/gin-gonic/gin" +) + +// 登录 +func UsersLoginHandler(ctx *gin.Context) { + response := Response{Ctx: ctx} + var loginUser serializers.Login + if err := ctx.ShouldBind(&loginUser); err != nil { + panic(err) + } + user := loginUser.GetUser() + isLoginUser := user.CheckPassword() + if !isLoginUser { + response.BadRequest("密码错误") + return + } + token, err := jwt.GenToken(user.ID, user.Username) + if err != nil { + panic(err) + } + data, _ := util.PrecisionLost(user) + data["token"] = token + response.Response(data, nil) + return +} + +// 注册 +func UsersRegisterHandler(ctx *gin.Context) { + response := Response{Ctx: ctx} + var registerUser serializers.Login + if err := ctx.ShouldBind(®isterUser); err != nil { + panic(err) + } + user := registerUser.GetUser() + status := user.CheckDuplicateUsername() + if status == false { + response.BadRequest("用户名已存在") + return + } + if err := user.SetPassword(user.Password); err != nil { + panic(err) + } + user.IsActive = true + models.DB.Create(&user) + response.Response(nil, nil) +} + +// 修改用户信息 +func UsersSetInfoHandler(ctx *gin.Context) { + response := Response{Ctx: ctx} + jsonData, err := util.GetBodyData(ctx) + if err != nil { + response.BadRequest("参数解析失败") + return + } + fmt.Println(jsonData) + if jsonData == nil { + response.BadRequest("获取不到参数") + return + } + currentUser := jwt.AssertUser(ctx) + if currentUser != nil { + models.DB.Model(¤tUser).Updates(jsonData) + response.Response(currentUser, nil) + return + } +} + +// 修改密码 +func UsersSetPwdHandler(ctx *gin.Context) { + response := Response{Ctx: ctx} + currentUser := jwt.AssertUser(ctx) + if currentUser == nil { + response.Unauthenticated("未验证登录") + return + } + var user serializers.Account + if err := ctx.ShouldBindJSON(&user); err != nil { + response.BadRequest(err.Error()) + return + } + if user.Username != currentUser.Username { + response.BadRequest("当前登录用户用户名与输入用户名不符") + return + } + if user.OldPwd == user.NewPwd { + response.BadRequest("两次输入的密码相同") + return + } + if isPwd := currentUser.IsPasswordEqual(user.OldPwd); !isPwd { + response.BadRequest("原密码错误") + return + } + if err := currentUser.SetPassword(user.NewPwd); err != nil { + response.BadRequest(err.Error()) + return + } + models.DB.Save(¤tUser) + response.Response(nil, nil) +} + +func UsersListHandler(ctx *gin.Context) { + response := Response{Ctx: ctx} + var pager serializers.Pager + pager.InitPager(ctx) + var users []models.Account + db := models.DB.Model(&users) + db.Count(&pager.Total) + db.Offset(pager.OffSet).Limit(pager.PageSize).Find(&users) + pager.GetPager() + response.Response(users, pager) +} diff --git a/main.go b/main.go index 8913ca4..c37051f 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,38 @@ package main -import "github.com/gin-gonic/gin" +import ( + "flag" + "go_blog/conf" + "go_blog/handlers" + "go_blog/models" + + "github.com/gin-gonic/gin" +) + +type UserInfo struct { + Name string + Gender string + Age int + Password string +} + +var host string +var port string +var isDebugMode bool +var isErrMsg bool +var isOrmDebug bool + +func init() { + flag.StringVar(&host, "h", "127.0.0.1", "主机") + flag.StringVar(&port, "p", "", "监听端口") + flag.BoolVar(&isDebugMode, "debug", true, "是否开启debug") + flag.BoolVar(&isErrMsg, "err", true, "是否返回错误信息") + flag.BoolVar(&isOrmDebug, "orm", true, "是否开启gorm的debug信息") + flag.Parse() + + conf.SetUp() + models.SetUp(isOrmDebug) +} func main() { r := gin.Default() @@ -9,9 +41,28 @@ func main() { r.GET("/", func(c *gin.Context) { c.HTML(200, "index.tmpl", nil) }) + + user := UserInfo{ + Name: "user", + Gender: "male", + Age: 18, + Password: "nothings", + } r.GET("/page/:id", func(c *gin.Context) { id := c.Param("id") - c.HTML(200, "page"+id+".tmpl", nil) + c.HTML(200, "page"+id+".tmpl", map[string]interface{}{ + "title": "这个是titile,传入templates中的", + "user": user, + }) }) + + r.GET("/login", func(c *gin.Context) { + c.HTML(200, "login.tmpl", map[string]interface{}{ + "title": "这个是titile,传入templates中的", + "user": user, + }) + }) + + r.POST("/login", handlers.UsersLoginHandler) r.Run(":8080") } diff --git a/models/init.go b/models/init.go new file mode 100644 index 0000000..bf70759 --- /dev/null +++ b/models/init.go @@ -0,0 +1,56 @@ +/* +@Time : 2020/6/28 21:46 +@Author : xuyiqing +@File : init.py +*/ + +package models + +import ( + "fmt" + "go_blog/conf" + "go_blog/pkg/util" + "time" + + "github.com/jinzhu/gorm" + _ "github.com/jinzhu/gorm/dialects/mysql" +) + +var DB *gorm.DB + +func SetUp(isOrmDebug bool) { + conUri := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=True&loc=Local", + conf.DataBase.User, + conf.DataBase.Password, + conf.DataBase.Host, + conf.DataBase.Port, + conf.DataBase.DB, + conf.DataBase.Charset) + db, err := gorm.Open(conf.DataBase.Type, conUri) + if err != nil { + panic(err) + } + DB = db + DB.LogMode(isOrmDebug) + gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string { + return conf.DataBase.Prefix + defaultTableName + } + + DB.AutoMigrate(&Account{}) + +} + +type BaseModel struct { + ID uint64 `gorm:"primary_key'" json:"id"` + CreatedAt time.Time `json:"-"` + UpdatedAt time.Time `json:"-"` + DeletedAt *time.Time `sql:"index" json:"-"` +} + +// 生成全局唯一ID +func (m *BaseModel) BeforeCreate(scope *gorm.Scope) error { + if m.ID == 0 { + m.ID = util.GenSonyFlakeId() + } + return nil +} diff --git a/models/users.go b/models/users.go new file mode 100644 index 0000000..3a09783 --- /dev/null +++ b/models/users.go @@ -0,0 +1,68 @@ +/* +@Time : 2020/6/28 22:01 +@Author : xuyiqing +@File : users.py +*/ + +package models + +import ( + "golang.org/x/crypto/bcrypt" +) + +const PasswordCryptLevel = 12 + +type Account struct { + BaseModel + Username string `gorm:"column:username;not null;unique_index;comment:'用户名'" json:"username" form:"username"` + Password string `gorm:"column:password;comment:'密码'" form:"password" json:"-"` + Name string `form:"name" json:"name"` + IsActive bool `json:"-"` +} + +func (a *Account) TableName() string { + return "user_accounts" +} + +func (a *Account) GetUserByID(id uint) *Account { + DB.Model(&Account{}).First(a, id) + if a.ID > 0 { + return a + } else { + return nil + } +} + +// 设置密码加密 +func (a *Account) SetPassword(password string) error { + p, err := bcrypt.GenerateFromPassword([]byte(password), PasswordCryptLevel) + if err != nil { + return err + } + a.Password = string(p) + return nil +} + +// 验证登录帐户密码合法性 +func (a *Account) CheckPassword() bool { + password := a.Password + DB.Where("username = ?", a.Username).First(&a) + err := bcrypt.CompareHashAndPassword([]byte(a.Password), []byte(password)) + return err == nil +} + +// 验证登录帐户密码合法性 +func (a *Account) IsPasswordEqual(password string) bool { + err := bcrypt.CompareHashAndPassword([]byte(a.Password), []byte(password)) + return err == nil +} + +// 验证用户民重复 +func (a *Account) CheckDuplicateUsername() bool { + var count int + if DB.Model(&Account{}).Where("username=?", a.Username).Count(&count); count > 0 { + return false + } else { + return true + } +} diff --git a/pkg/jwt/auth.go b/pkg/jwt/auth.go new file mode 100644 index 0000000..77bf25e --- /dev/null +++ b/pkg/jwt/auth.go @@ -0,0 +1,77 @@ +/* +@Time : 2020/6/29 9:05 +@Author : xuyiqing +@File : auth.py +*/ + +package jwt + +import ( + "go_blog/conf" + "go_blog/models" + "time" + + "github.com/dgrijalva/jwt-go" + "github.com/gin-gonic/gin" +) + +// 定义jwt载荷 +type UserClaims struct { + jwt.StandardClaims + ID uint64 `json:"id"` + Username string `json:"username"` +} + +// 根据payload查询user返回 +func (c *UserClaims) GetUserByID() *models.Account { + var user models.Account + models.DB.Model(&models.Account{}).First(&user, c.ID) + if user.ID > 0 { + return &user + } else { + return nil + } +} + +// 生成jwt token字符串 +func GenToken(id uint64, username string) (string, error) { + expiredTime := time.Now().Add(time.Hour * time.Duration(24)).Unix() + claims := UserClaims{ + jwt.StandardClaims{ + ExpiresAt: expiredTime, + }, + id, + username, + } + tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + token, err := tokenClaims.SignedString([]byte(conf.JwtSecretKey.SecretKey)) + return token, err +} + +// 验证token合法性 +func ValidateJwtToken(token string) (*UserClaims, error) { + tokenClaims, err := jwt.ParseWithClaims(token, &UserClaims{}, func(token *jwt.Token) (interface{}, error) { + return []byte(conf.JwtSecretKey.SecretKey), nil + }) + + if tokenClaims != nil { + if claims, ok := tokenClaims.Claims.(*UserClaims); ok && tokenClaims.Valid { + return claims, nil + } + } + return nil, err +} + +// 断言设定ctx的当前用户 +func AssertUser(ctx *gin.Context) *models.Account { + currentUser, isExists := ctx.Get("CurrentUser") + if !isExists { + return nil + } + user, ok := currentUser.(*models.Account) + if ok { + return user + } else { + return nil + } +} diff --git a/pkg/util/request.go b/pkg/util/request.go new file mode 100644 index 0000000..8199f7d --- /dev/null +++ b/pkg/util/request.go @@ -0,0 +1,44 @@ +/* +@Time : 2020/6/29 14:40 +@Author : xuyiqing +@File : body.py +*/ + +package util + +import ( + "encoding/json" + "fmt" + "github.com/gin-gonic/gin" + "io" + "strings" +) + +// 反序列化request.body中的json数据为map +func GetBodyData(ctx *gin.Context) (map[string]interface{}, error) { + bdata := make([]byte, 1024) + length, err := ctx.Request.Body.Read(bdata) + if err != nil && err != io.EOF { + return nil, err + } + var data map[string]interface{} + str := string(bdata[:length]) + decoder := json.NewDecoder(strings.NewReader(str)) + decoder.UseNumber() + err1 := decoder.Decode(&data) + if err1 != nil { + return nil, err + } + return data, nil +} + +// 构建文件url连接主机端口全链接 "https://192.168.11.121:7889/meida/upload/..." +func BuildAbsoluteUri(ctx *gin.Context, filePath string) string { + host := ctx.Request.Host + schema := ctx.Request.Header.Get("X-Forwarded-Proto") + if schema == "https" { + return fmt.Sprintf("https://%s/%s", host, filePath) + } else { + return fmt.Sprintf("http://%s/%s", host, filePath) + } +} diff --git a/pkg/util/sonyflake.go b/pkg/util/sonyflake.go new file mode 100644 index 0000000..afd872a --- /dev/null +++ b/pkg/util/sonyflake.go @@ -0,0 +1,183 @@ +// Package sonyflake implements Sonyflake, a distributed unique ID generator inspired by Twitter's Snowflake. +// +// A Sonyflake ID is composed of +// 39 bits for time in units of 10 msec +// 8 bits for a sequence number +// 16 bits for a machine id +package util + +import ( + "errors" + "net" + "sync" + "time" +) + +// These constants are the bit lengths of Sonyflake ID parts. +const ( + BitLenTime = 41 // bit length of time + BitLenSequence = 10 // bit length of sequence number + BitLenMachineID = 53 - BitLenTime - BitLenSequence // bit length of machine id +) + +// Settings configures Sonyflake: +// +// StartTime is the time since which the Sonyflake time is defined as the elapsed time. +// If StartTime is 0, the start time of the Sonyflake is set to "2014-09-01 00:00:00 +0000 UTC". +// If StartTime is ahead of the current time, Sonyflake is not created. +// +// MachineID returns the unique ID of the Sonyflake instance. +// If MachineID returns an error, Sonyflake is not created. +// If MachineID is nil, default MachineID is used. +// Default MachineID returns the lower 16 bits of the private IP address. +// +// CheckMachineID validates the uniqueness of the machine ID. +// If CheckMachineID returns false, Sonyflake is not created. +// If CheckMachineID is nil, no validation is done. +type Settings struct { + StartTime time.Time + MachineID func() (uint16, error) + CheckMachineID func(uint16) bool +} + +// Sonyflake is a distributed unique ID generator. +type Sonyflake struct { + mutex *sync.Mutex + startTime int64 + elapsedTime int64 + sequence uint16 + machineID uint16 +} + +// NewSonyflake returns a new Sonyflake configured with the given Settings. +// NewSonyflake returns nil in the following cases: +// - Settings.StartTime is ahead of the current time. +// - Settings.MachineID returns an error. +// - Settings.CheckMachineID returns false. +func NewSonyflake(st Settings) *Sonyflake { + sf := new(Sonyflake) + sf.mutex = new(sync.Mutex) + sf.sequence = uint16(1<= current + sf.sequence = (sf.sequence + 1) & maskSequence + if sf.sequence == 0 { + sf.elapsedTime++ + overtime := sf.elapsedTime - current + time.Sleep(sleepTime((overtime))) + } + } + + return sf.toID() +} + +const sonyflakeTimeUnit = 1e7 // nsec, i.e. 10 msec + +func toSonyflakeTime(t time.Time) int64 { + return t.UTC().UnixNano() / sonyflakeTimeUnit +} + +func currentElapsedTime(startTime int64) int64 { + return toSonyflakeTime(time.Now()) - startTime +} + +func sleepTime(overtime int64) time.Duration { + return time.Duration(overtime)*10*time.Millisecond - + time.Duration(time.Now().UTC().UnixNano()%sonyflakeTimeUnit)*time.Nanosecond +} + +func (sf *Sonyflake) toID() (uint64, error) { + if sf.elapsedTime >= 1<= 16 && ip[1] < 32) || ip[0] == 192 && ip[1] == 168) +} + +func lower16BitPrivateIP() (uint16, error) { + ip, err := privateIPv4() + if err != nil { + return 0, err + } + + return uint16(ip[2])<<8 + uint16(ip[3]), nil +} + +// Decompose returns a set of Sonyflake ID parts. +func Decompose(id uint64) map[string]uint64 { + const maskSequence = uint64((1<> 63 + time := id >> (BitLenSequence + BitLenMachineID) + sequence := id & maskSequence >> BitLenMachineID + machineID := id & maskMachineID + return map[string]uint64{ + "id": id, + "msb": msb, + "time": time, + "sequence": sequence, + "machine-id": machineID, + } +} diff --git a/pkg/util/util.go b/pkg/util/util.go new file mode 100644 index 0000000..dbfd8da --- /dev/null +++ b/pkg/util/util.go @@ -0,0 +1,42 @@ +/* +@Time : 2020/7/15 23:27 +@Author : xuyiqing +@File : util.py +*/ + +package util + +import ( + "bytes" + "encoding/json" + "os" +) + + +// 判断所给路径文件/文件夹是否存在 +func FileOrDirExists(path string) bool { + _, err := os.Stat(path) //os.Stat获取文件信息 + if err != nil { + if os.IsExist(err) { + return true + } + return false + } + return true +} + +// 解决json字符串整型精度缺失 +func PrecisionLost(data interface{}) (map[string]interface{}, error) { + bdata, err := json.Marshal(data) + if err != nil { + return nil, err + } + var val map[string]interface{} + d := json.NewDecoder(bytes.NewBuffer(bdata)) + d.UseNumber() + err = d.Decode(&val) + if err != nil { + return nil, err + } + return val, nil +} diff --git a/pkg/util/uuid.go b/pkg/util/uuid.go new file mode 100644 index 0000000..b7cd5d2 --- /dev/null +++ b/pkg/util/uuid.go @@ -0,0 +1,19 @@ +package util + +import ( + "fmt" + "time" +) +var t = time.Unix(1594909810, 0) + +var flake = NewSonyflake(Settings{ + +}) + +func GenSonyFlakeId() uint64 { + uuid, err := flake.NextID() + if err != nil { + fmt.Println(err) + } + return uuid +} diff --git a/serializers/pagination.go b/serializers/pagination.go new file mode 100644 index 0000000..2f0d369 --- /dev/null +++ b/serializers/pagination.go @@ -0,0 +1,30 @@ +/* +@Time : 2020/7/16 23:44 +@Author : xuyiqing +@File : common.py +*/ + +package serializers + +import ( + "github.com/gin-gonic/gin" + "strconv" +) + +type Pager struct { + Page int `json:"page" form:"page"` + PageSize int `json:"pageSize" form:"pageSize"` + OffSet int `json:"-"` + Total int `json:"total"` + MaxPage int `json:"maxPage"` +} + +func (p *Pager) InitPager(ctx *gin.Context) { + p.Page, _ = strconv.Atoi(ctx.DefaultQuery("page", "1")) + p.PageSize, _ = strconv.Atoi(ctx.DefaultQuery("pageSize", "10")) + p.OffSet = (p.Page - 1) * p.PageSize +} + +func (p *Pager) GetPager() { + p.MaxPage = int(p.Total / p.PageSize) + 1 +} diff --git a/serializers/users.go b/serializers/users.go new file mode 100644 index 0000000..0f48626 --- /dev/null +++ b/serializers/users.go @@ -0,0 +1,27 @@ +/* +@Time : 2020/6/28 22:16 +@Author : xuyiqing +@File : users.py +*/ + +package serializers + +import "go_blog/models" + +type Login struct { + Username string `form:"usernmae"; json:"username"` + Password string `form:"password"; json:"password"` +} + +func (l *Login) GetUser() *models.Account { + return &models.Account{ + Username: l.Username, + Password: l.Password, + } +} + +type Account struct { + Username string `form:"username" json:"username"` + OldPwd string `form:"oldPwd" json:"oldPwd"` + NewPwd string `form:"newPwd"json:"newPwd"` +} diff --git a/templates/login.tmpl b/templates/login.tmpl new file mode 100644 index 0000000..35e10e3 --- /dev/null +++ b/templates/login.tmpl @@ -0,0 +1,33 @@ + + + + + diff --git a/templates/page1.tmpl b/templates/page1.tmpl index 4701732..c66295e 100644 --- a/templates/page1.tmpl +++ b/templates/page1.tmpl @@ -8,5 +8,8 @@

Page 1

This is the content of page 1.

+

{{.title}}

+

{{.user.Name}}

+

{{.user.Age}}