@@ -0,0 +1,59 @@ | |||
package hdl | |||
import ( | |||
"applet/app/db" | |||
"applet/app/e" | |||
"applet/app/lib/validate" | |||
"applet/app/md" | |||
"applet/app/svc" | |||
"applet/app/utils" | |||
"code.fnuoos.com/EggPlanet/egg_models.git/src/implement" | |||
"fmt" | |||
"github.com/gin-gonic/gin" | |||
) | |||
// Login 登陆 | |||
// @Summary 登陆 | |||
// @Tags 登录 | |||
// @Description 登入 | |||
// @Accept json | |||
// @Produce json | |||
// @Param req body md.LoginReq true "用户名密码" | |||
// @Success 200 {object} md.LoginResponse "token" | |||
// @Failure 400 {object} md.Response "具体错误" | |||
// @Router /api/login [post] | |||
func Login(c *gin.Context) { | |||
var req md.LoginReq | |||
err := c.ShouldBindJSON(&req) | |||
if err != nil { | |||
err = validate.HandleValidateErr(err) | |||
err1 := err.(e.E) | |||
e.OutErr(c, err1.Code, err1.Error()) | |||
return | |||
} | |||
adminDb := implement.NewAdminDb(db.Db) | |||
admin, err := adminDb.GetAdminByUserName(req.UserName) | |||
if err != nil { | |||
e.OutErr(c, e.ERR_DB_ORM, err) | |||
return | |||
} | |||
if admin == nil { | |||
e.OutErr(c, e.ERR_NO_DATA, "账号不存在!") | |||
return | |||
} | |||
if utils.Md5(req.PassWord) != admin.Password { | |||
e.OutErr(c, e.ERR_INVALID_ARGS, "密码错误") | |||
return | |||
} | |||
ip := utils.GetIP(c.Request) | |||
key := fmt.Sprintf(md.JwtTokenKey, ip, utils.AnyToString(admin.AdmId)) | |||
token, err := svc.HandleLoginToken(key, admin) | |||
if err != nil { | |||
e.OutErr(c, e.ERR, err.Error()) | |||
return | |||
} | |||
e.OutSuc(c, md.LoginResponse{ | |||
Token: token, | |||
}, nil) | |||
return | |||
} |
@@ -2,7 +2,6 @@ package alipay | |||
import ( | |||
"applet/app/cfg" | |||
"applet/app/pay/md" | |||
"applet/app/utils/logx" | |||
"fmt" | |||
@@ -14,7 +14,7 @@ func GenToken(admId int, username string) (string, error) { | |||
Username: username, | |||
StandardClaims: jwt.StandardClaims{ | |||
ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // 过期时间 | |||
Issuer: "smart_canteen", // 签发人 | |||
Issuer: "egg_admin", // 签发人 | |||
}, | |||
} | |||
// 使用指定的签名方法创建签名对象 | |||
@@ -0,0 +1,33 @@ | |||
package validate | |||
import ( | |||
"applet/app/e" | |||
"applet/app/utils" | |||
"applet/app/utils/logx" | |||
"encoding/json" | |||
"fmt" | |||
"github.com/go-playground/validator/v10" | |||
) | |||
func HandleValidateErr(err error) error { | |||
switch err.(type) { | |||
case *json.UnmarshalTypeError: | |||
return e.NewErr(e.ERR_UNMARSHAL, "参数格式错误") | |||
case validator.ValidationErrors: | |||
errs := err.(validator.ValidationErrors) | |||
transMsgMap := errs.Translate(utils.ValidatorTrans) // utils.ValidatorTrans \app\utils\validator_err_trans.go::ValidatorTransInit初始化获得 | |||
transMsgOne := transMsgMap[GetOneKeyOfMapString(transMsgMap)] | |||
return e.NewErr(e.ERR_INVALID_ARGS, transMsgOne) | |||
default: | |||
_ = logx.Error(err) | |||
return e.NewErr(e.ERR, fmt.Sprintf("validate request params, err:%v\n", err)) | |||
} | |||
} | |||
// GetOneKeyOfMapString 取出Map的一个key | |||
func GetOneKeyOfMapString(collection map[string]string) string { | |||
for k := range collection { | |||
return k | |||
} | |||
return "" | |||
} |
@@ -2,6 +2,10 @@ package md | |||
// 缓存key统一管理, %s格式化为masterId | |||
const ( | |||
JwtTokenKey = "%s:egg_admin_jwt_token:%s" // jwt, 占位符:ip, admin:id | |||
JwtTokenCacheTime = 3600 * 4 | |||
AppCfgCacheKey = "cfg_cache:%s" // 占位符: key的第一个字母 | |||
CfgCacheTime = 86400 | |||
@@ -0,0 +1,11 @@ | |||
package md | |||
type LoginReq struct { | |||
UserName string `json:"username" binding:"required" example:"登录账号"` | |||
PassWord string `json:"password" binding:"required" example:"登录密码"` | |||
Code string `json:"code" example:"验证码"` | |||
} | |||
type LoginResponse struct { | |||
Token string `json:"token"` | |||
} |
@@ -48,6 +48,7 @@ func Init() *gin.Engine { | |||
func route(r *gin.RouterGroup) { | |||
r.GET("/test", hdl.Demo) | |||
r.POST("/login", hdl.Login) | |||
rInstitutionalManagement(r.Group("/institutionalManagement")) | |||
r.Use(mw.Auth) // 以下接口需要JWT验证 | |||
@@ -0,0 +1,33 @@ | |||
package svc | |||
import ( | |||
"applet/app/lib/auth" | |||
"applet/app/md" | |||
"applet/app/utils/cache" | |||
"applet/app/utils/logx" | |||
"code.fnuoos.com/EggPlanet/egg_models.git/src/model" | |||
) | |||
func HandleLoginToken(cacheKey string, admin *model.Admin) (string, error) { | |||
// 获取之前生成的token | |||
token, err := cache.GetString(cacheKey) | |||
if err != nil { | |||
_ = logx.Error(err) | |||
} | |||
// 没有获取到 | |||
if err != nil || token == "" { | |||
// 生成token | |||
token, err = auth.GenToken(admin.AdmId, admin.Username) | |||
if err != nil { | |||
return "", err | |||
} | |||
// 缓存token | |||
_, err = cache.SetEx(cacheKey, token, md.JwtTokenCacheTime) | |||
if err != nil { | |||
return "", err | |||
} | |||
return token, nil | |||
} | |||
return token, nil | |||
} |
@@ -0,0 +1,146 @@ | |||
package utils | |||
import ( | |||
"errors" | |||
"math" | |||
"net" | |||
"net/http" | |||
"strings" | |||
) | |||
func GetIP(r *http.Request) string { | |||
ip := ClientPublicIP(r) | |||
if ip == "" { | |||
ip = ClientIP(r) | |||
} | |||
if ip == "" { | |||
ip = "0000" | |||
} | |||
return ip | |||
} | |||
// HasLocalIPddr 检测 IP 地址字符串是否是内网地址 | |||
// Deprecated: 此为一个错误名称错误拼写的函数,计划在将来移除,请使用 HasLocalIPAddr 函数 | |||
func HasLocalIPddr(ip string) bool { | |||
return HasLocalIPAddr(ip) | |||
} | |||
// HasLocalIPAddr 检测 IP 地址字符串是否是内网地址 | |||
func HasLocalIPAddr(ip string) bool { | |||
return HasLocalIP(net.ParseIP(ip)) | |||
} | |||
// HasLocalIP 检测 IP 地址是否是内网地址 | |||
// 通过直接对比ip段范围效率更高,详见:https://github.com/thinkeridea/go-extend/issues/2 | |||
func HasLocalIP(ip net.IP) bool { | |||
if ip.IsLoopback() { | |||
return true | |||
} | |||
ip4 := ip.To4() | |||
if ip4 == nil { | |||
return false | |||
} | |||
return ip4[0] == 10 || // 10.0.0.0/8 | |||
(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) || // 172.16.0.0/12 | |||
(ip4[0] == 169 && ip4[1] == 254) || // 169.254.0.0/16 | |||
(ip4[0] == 192 && ip4[1] == 168) // 192.168.0.0/16 | |||
} | |||
// ClientIP 尽最大努力实现获取客户端 IP 的算法。 | |||
// 解析 X-Real-IP 和 X-Forwarded-For 以便于反向代理(nginx 或 haproxy)可以正常工作。 | |||
func ClientIP(r *http.Request) string { | |||
ip := strings.TrimSpace(strings.Split(r.Header.Get("X-Forwarded-For"), ",")[0]) | |||
if ip != "" { | |||
return ip | |||
} | |||
ip = strings.TrimSpace(r.Header.Get("X-Real-Ip")) | |||
if ip != "" { | |||
return ip | |||
} | |||
if ip, _, err := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr)); err == nil { | |||
return ip | |||
} | |||
return "" | |||
} | |||
// ClientPublicIP 尽最大努力实现获取客户端公网 IP 的算法。 | |||
// 解析 X-Real-IP 和 X-Forwarded-For 以便于反向代理(nginx 或 haproxy)可以正常工作。 | |||
func ClientPublicIP(r *http.Request) string { | |||
var ip string | |||
for _, ip = range strings.Split(r.Header.Get("X-Forwarded-For"), ",") { | |||
if ip = strings.TrimSpace(ip); ip != "" && !HasLocalIPAddr(ip) { | |||
return ip | |||
} | |||
} | |||
if ip = strings.TrimSpace(r.Header.Get("X-Real-Ip")); ip != "" && !HasLocalIPAddr(ip) { | |||
return ip | |||
} | |||
if ip = RemoteIP(r); !HasLocalIPAddr(ip) { | |||
return ip | |||
} | |||
return "" | |||
} | |||
// RemoteIP 通过 RemoteAddr 获取 IP 地址, 只是一个快速解析方法。 | |||
func RemoteIP(r *http.Request) string { | |||
ip, _, _ := net.SplitHostPort(r.RemoteAddr) | |||
return ip | |||
} | |||
// IPString2Long 把ip字符串转为数值 | |||
func IPString2Long(ip string) (uint, error) { | |||
b := net.ParseIP(ip).To4() | |||
if b == nil { | |||
return 0, errors.New("invalid ipv4 format") | |||
} | |||
return uint(b[3]) | uint(b[2])<<8 | uint(b[1])<<16 | uint(b[0])<<24, nil | |||
} | |||
// Long2IPString 把数值转为ip字符串 | |||
func Long2IPString(i uint) (string, error) { | |||
if i > math.MaxUint32 { | |||
return "", errors.New("beyond the scope of ipv4") | |||
} | |||
ip := make(net.IP, net.IPv4len) | |||
ip[0] = byte(i >> 24) | |||
ip[1] = byte(i >> 16) | |||
ip[2] = byte(i >> 8) | |||
ip[3] = byte(i) | |||
return ip.String(), nil | |||
} | |||
// IP2Long 把net.IP转为数值 | |||
func IP2Long(ip net.IP) (uint, error) { | |||
b := ip.To4() | |||
if b == nil { | |||
return 0, errors.New("invalid ipv4 format") | |||
} | |||
return uint(b[3]) | uint(b[2])<<8 | uint(b[1])<<16 | uint(b[0])<<24, nil | |||
} | |||
// Long2IP 把数值转为net.IP | |||
func Long2IP(i uint) (net.IP, error) { | |||
if i > math.MaxUint32 { | |||
return nil, errors.New("beyond the scope of ipv4") | |||
} | |||
ip := make(net.IP, net.IPv4len) | |||
ip[0] = byte(i >> 24) | |||
ip[1] = byte(i >> 16) | |||
ip[2] = byte(i >> 8) | |||
ip[3] = byte(i) | |||
return ip, nil | |||
} |