Quellcode durchsuchen

add login api

master
dengbiao vor 2 Wochen
Ursprung
Commit
bbf3142804
9 geänderte Dateien mit 288 neuen und 2 gelöschten Zeilen
  1. +59
    -0
      app/hdl/hdl_login.go
  2. +0
    -1
      app/lib/alipay/api.go
  3. +1
    -1
      app/lib/auth/auth.go
  4. +33
    -0
      app/lib/validate/validate_comm.go
  5. +4
    -0
      app/md/app_redis_key.go
  6. +11
    -0
      app/md/md_login.go
  7. +1
    -0
      app/router/router.go
  8. +33
    -0
      app/svc/svc_login.go
  9. +146
    -0
      app/utils/ip.go

+ 59
- 0
app/hdl/hdl_login.go Datei anzeigen

@@ -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
}

+ 0
- 1
app/lib/alipay/api.go Datei anzeigen

@@ -2,7 +2,6 @@ package alipay

import (
"applet/app/cfg"
"applet/app/pay/md"
"applet/app/utils/logx"
"fmt"



+ 1
- 1
app/lib/auth/auth.go Datei anzeigen

@@ -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", // 签发人
},
}
// 使用指定的签名方法创建签名对象


+ 33
- 0
app/lib/validate/validate_comm.go Datei anzeigen

@@ -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 ""
}

+ 4
- 0
app/md/app_redis_key.go Datei anzeigen

@@ -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


+ 11
- 0
app/md/md_login.go Datei anzeigen

@@ -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"`
}

+ 1
- 0
app/router/router.go Datei anzeigen

@@ -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验证



+ 33
- 0
app/svc/svc_login.go Datei anzeigen

@@ -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
}

+ 146
- 0
app/utils/ip.go Datei anzeigen

@@ -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
}

Laden…
Abbrechen
Speichern