4 Commits
0d42255da7
...
f72b519ed2
Author | SHA1 | Message | Date |
---|---|---|---|
|
f72b519ed2 |
合并dhy分支内容
管理端-登录、退出接口 客户端工具类-获取token |
5 months ago |
|
77a1c66771 |
Merge branch 'refs/heads/dhy' into dev
# Conflicts: # link_homework/go.mod # link_homework/go.sum # link_homework/internal/cmd/cmd.go # link_homework/internal/logic/logic.go # link_homework/main.go # link_homework/manifest/config/config.yaml |
5 months ago |
|
4e47b507b0 |
管理端-登录、退出接口
客户端工具类-获取token |
5 months ago |
|
96c960c04f |
提交登录
|
5 months ago |
12 changed files with 376 additions and 27 deletions
-
1link_homework/api/v1/homework/login.go
-
5link_homework/go.mod
-
11link_homework/go.sum
-
24link_homework/internal/cmd/cmd.go
-
6link_homework/internal/consts/consts.go
-
56link_homework/internal/controller/auth/login.go
-
6link_homework/internal/logic/logic.go
-
76link_homework/internal/logic/login/login.go
-
43link_homework/internal/logic/middleware/JWTMiddleware.go
-
34link_homework/internal/service/login.go
-
26link_homework/main.go
-
115link_homework/utility/utility.go
@ -0,0 +1 @@ |
|||||
|
package homework |
@ -1 +1,7 @@ |
|||||
package consts |
package consts |
||||
|
|
||||
|
// Redis和其他配置常量
|
||||
|
const ( |
||||
|
URL_KEY = "jingwang:cms:env" // Redis中的键
|
||||
|
URL_HASH_KEY = "HLJW_BASE_URL" // Redis中的hashKey
|
||||
|
) |
@ -0,0 +1,56 @@ |
|||||
|
package auth |
||||
|
|
||||
|
import ( |
||||
|
"net/http" |
||||
|
|
||||
|
"github.com/gogf/gf/v2/frame/g" |
||||
|
"github.com/gogf/gf/v2/net/ghttp" |
||||
|
"link_homework/internal/model/dto" |
||||
|
"link_homework/internal/service" |
||||
|
) |
||||
|
|
||||
|
type LoginController struct { |
||||
|
} |
||||
|
|
||||
|
func NewLoginController() *LoginController { |
||||
|
return &LoginController{} |
||||
|
} |
||||
|
|
||||
|
// 登录接口
|
||||
|
|
||||
|
func (c *LoginController) Login(r *ghttp.Request) { |
||||
|
var ( |
||||
|
username = r.Get("username").String() |
||||
|
password = r.Get("password").String() |
||||
|
) |
||||
|
|
||||
|
token, err := service.LoginLogic().Login(r.Context(), username, password) |
||||
|
if err != nil { |
||||
|
r.Response.WriteJsonExit(dto.Error(err.Error())) |
||||
|
} |
||||
|
|
||||
|
r.Response.WriteJsonExit(dto.SuccessWithData(g.Map{ |
||||
|
"token": token, |
||||
|
})) |
||||
|
} |
||||
|
|
||||
|
// 退出接口
|
||||
|
func (c *LoginController) Logout(r *ghttp.Request) { |
||||
|
// 获取请求中的 context
|
||||
|
ctx := r.Context() |
||||
|
|
||||
|
// 从请求头中获取 token
|
||||
|
token := r.Header.Get("token") |
||||
|
if token == "" { |
||||
|
r.Response.WriteJsonExit(dto.ErrorWithCode(http.StatusUnauthorized, "Token 不能为空")) |
||||
|
} |
||||
|
|
||||
|
// 校验 Token 是否有效
|
||||
|
valid, err := service.LoginLogic().ValidateToken(ctx, token) |
||||
|
if err != nil || !valid { |
||||
|
r.Response.WriteJsonExit(dto.ErrorWithCode(http.StatusUnauthorized, "Token 无效")) |
||||
|
} |
||||
|
|
||||
|
// 返回登出成功
|
||||
|
r.Response.WriteJsonExit(dto.Success()) |
||||
|
} |
@ -0,0 +1,76 @@ |
|||||
|
package login |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"errors" |
||||
|
"time" |
||||
|
|
||||
|
"github.com/golang-jwt/jwt/v4" |
||||
|
"link_homework/internal/service" |
||||
|
) |
||||
|
|
||||
|
type sLoginLogic struct{} |
||||
|
|
||||
|
var ( |
||||
|
SecretKey = []byte("HomilyLink") // 用于签名和验证 JWT 的密钥
|
||||
|
) |
||||
|
|
||||
|
// 自定义声明结构
|
||||
|
type CustomClaims struct { |
||||
|
Username string `json:"username"` |
||||
|
jwt.RegisteredClaims |
||||
|
} |
||||
|
|
||||
|
func NewLoginLogic() *sLoginLogic { |
||||
|
return &sLoginLogic{} |
||||
|
} |
||||
|
|
||||
|
// 确保在初始化时注册该实现
|
||||
|
func init() { |
||||
|
service.RegisterLoginLogic(&sLoginLogic{}) |
||||
|
} |
||||
|
|
||||
|
// Login 方法实现用户登录并生成 Token
|
||||
|
func (l *sLoginLogic) Login(ctx context.Context, username, password string) (string, error) { |
||||
|
if username != "admin" || password != "12345" { |
||||
|
return "", errors.New("用户名或密码错误") |
||||
|
} |
||||
|
|
||||
|
// 创建 JWT Token
|
||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ |
||||
|
"username": username, |
||||
|
"exp": time.Now().Add(time.Hour * 1).Unix(), // 1 小时后过期
|
||||
|
}) |
||||
|
|
||||
|
// 签名并获取完整的编码后的字符串 token
|
||||
|
tokenString, err := token.SignedString(SecretKey) |
||||
|
if err != nil { |
||||
|
return "", err |
||||
|
} |
||||
|
|
||||
|
return tokenString, nil |
||||
|
} |
||||
|
|
||||
|
// ValidateToken 验证 Token 是否有效
|
||||
|
func (l *sLoginLogic) ValidateToken(ctx context.Context, tokenString string) (bool, error) { |
||||
|
// 解析 token
|
||||
|
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) { |
||||
|
return SecretKey, nil |
||||
|
}) |
||||
|
|
||||
|
if err != nil { |
||||
|
return false, errors.New("Token 无效: " + err.Error()) |
||||
|
} |
||||
|
|
||||
|
// 如果 token 有效,返回 true
|
||||
|
if token.Valid { |
||||
|
// 提取 CustomClaims 并打印 username
|
||||
|
//if claims, ok := token.Claims.(*CustomClaims); ok {
|
||||
|
// log.Printf("解析到的用户名: %s", claims.Username) // 打印解析到的用户名
|
||||
|
//}
|
||||
|
return true, nil |
||||
|
} |
||||
|
|
||||
|
// 如果 token 无效
|
||||
|
return false, errors.New("Token 验证失败") |
||||
|
} |
@ -0,0 +1,43 @@ |
|||||
|
package middleware |
||||
|
|
||||
|
import ( |
||||
|
"link_homework/internal/logic/login" |
||||
|
"net/http" |
||||
|
"strings" |
||||
|
|
||||
|
"github.com/gogf/gf/v2/net/ghttp" |
||||
|
"github.com/golang-jwt/jwt/v4" |
||||
|
) |
||||
|
|
||||
|
// JWT 验证中间件
|
||||
|
func JWTMiddleware(r *ghttp.Request) { |
||||
|
// 从请求头中获取 token
|
||||
|
authHeader := r.Header.Get("Authorization") |
||||
|
if authHeader == "" { |
||||
|
r.Response.WriteStatusExit(http.StatusUnauthorized, "Authorization header missing") |
||||
|
} |
||||
|
|
||||
|
// 检查 token 前缀
|
||||
|
parts := strings.SplitN(authHeader, " ", 2) |
||||
|
if len(parts) != 2 || parts[0] != "Bearer" { |
||||
|
r.Response.WriteStatusExit(http.StatusUnauthorized, "Invalid Authorization header format") |
||||
|
} |
||||
|
|
||||
|
tokenString := parts[1] |
||||
|
|
||||
|
// 解析 token
|
||||
|
token, err := jwt.ParseWithClaims(tokenString, &login.CustomClaims{}, func(token *jwt.Token) (interface{}, error) { |
||||
|
return login.SecretKey, nil |
||||
|
}) |
||||
|
|
||||
|
if err != nil || !token.Valid { |
||||
|
r.Response.WriteStatusExit(http.StatusUnauthorized, "Invalid token") |
||||
|
} |
||||
|
|
||||
|
// 将用户信息存储在上下文中
|
||||
|
if claims, ok := token.Claims.(*login.CustomClaims); ok { |
||||
|
r.SetCtxVar("username", claims.Username) |
||||
|
} |
||||
|
|
||||
|
r.Middleware.Next() |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
// ================================================================================
|
||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
|
// You can delete these comments if you wish manually maintain this interface file.
|
||||
|
// ================================================================================
|
||||
|
|
||||
|
package service |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
) |
||||
|
|
||||
|
type ( |
||||
|
ILoginLogic interface { |
||||
|
// Login 方法实现用户登录并生成 Token
|
||||
|
Login(ctx context.Context, username string, password string) (string, error) |
||||
|
// ValidateToken 验证 Token 是否有效
|
||||
|
ValidateToken(ctx context.Context, tokenString string) (bool, error) |
||||
|
} |
||||
|
) |
||||
|
|
||||
|
var ( |
||||
|
localLoginLogic ILoginLogic |
||||
|
) |
||||
|
|
||||
|
func LoginLogic() ILoginLogic { |
||||
|
if localLoginLogic == nil { |
||||
|
panic("implement not found for interface ILoginLogic, forgot register?") |
||||
|
} |
||||
|
return localLoginLogic |
||||
|
} |
||||
|
|
||||
|
func RegisterLoginLogic(i ILoginLogic) { |
||||
|
localLoginLogic = i |
||||
|
} |
@ -0,0 +1,115 @@ |
|||||
|
package utility |
||||
|
|
||||
|
import ( |
||||
|
"encoding/json" |
||||
|
"errors" |
||||
|
"fmt" |
||||
|
"github.com/gogf/gf/v2/frame/g" |
||||
|
"github.com/gogf/gf/v2/os/gctx" |
||||
|
"io/ioutil" |
||||
|
"link_homework/internal/consts" |
||||
|
"link_homework/internal/model/dto" |
||||
|
"net/http" |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
// 获取 URL
|
||||
|
func getUrl(key, hashKey string) *dto.Result { |
||||
|
ctx := gctx.New() |
||||
|
|
||||
|
// 1. 从 Redis 获取 URL
|
||||
|
redisUrl, err := g.Redis().Do(ctx, "HGET", key, hashKey) |
||||
|
if err == nil && redisUrl.String() != "" { |
||||
|
g.Log().Infof(ctx, "从 Redis 获取到 URL: %s", redisUrl.String()) |
||||
|
return dto.SuccessWithData(redisUrl.String()) |
||||
|
} |
||||
|
|
||||
|
// 2. 如果 Redis 中没有,查询数据库
|
||||
|
urlResult := selectBaseUrl(hashKey) |
||||
|
if urlResult.Code != 200 { |
||||
|
return urlResult // 数据库查询失败,返回错误
|
||||
|
} |
||||
|
url := urlResult.Data.(string) |
||||
|
|
||||
|
// 3. 将 URL 存入 Redis
|
||||
|
if _, err = g.Redis().Do(ctx, "HSET", key, hashKey, url); err != nil { |
||||
|
g.Log().Warningf(ctx, "将数据存入 Redis 失败: %v", err) |
||||
|
} |
||||
|
|
||||
|
g.Log().Infof(ctx, "将 URL 存入 Redis: %s", url) |
||||
|
return dto.SuccessWithData(url) |
||||
|
} |
||||
|
|
||||
|
// 查询数据库中的 URL
|
||||
|
func selectBaseUrl(hashKey string) *dto.Result { |
||||
|
ctx := gctx.New() |
||||
|
|
||||
|
// 查询数据库
|
||||
|
value, err := g.DB("cms").Model("env").Where("`key` = ?", hashKey).Value("value") |
||||
|
if err != nil { |
||||
|
g.Log().Errorf(ctx, "数据库查询失败, 错误: %v, key: %s", err, hashKey) |
||||
|
return dto.Error("数据库查询失败") |
||||
|
} |
||||
|
|
||||
|
if value.IsNil() || value.String() == "" { |
||||
|
g.Log().Errorf(ctx, "未找到对应数据, key: %s", hashKey) |
||||
|
return dto.Error("未找到对应数据") |
||||
|
} |
||||
|
|
||||
|
return dto.SuccessWithData(value.String()) |
||||
|
} |
||||
|
|
||||
|
// 获取 jwcode
|
||||
|
func GetJwcodeJSON(token string) (string, error) { |
||||
|
// 1. 获取基础 URL
|
||||
|
urlResult := getUrl(consts.URL_KEY, consts.URL_HASH_KEY) |
||||
|
if urlResult.Code != 200 { |
||||
|
return "", errors.New("获取基础 URL 失败: " + urlResult.Message) |
||||
|
} |
||||
|
baseUrl := urlResult.Data.(string) |
||||
|
|
||||
|
// 2. 拼接完整的 URL
|
||||
|
url := baseUrl + "/api/v2/member/info" |
||||
|
requestBody := strings.NewReader(`{"token":"` + token + `"}`) |
||||
|
|
||||
|
// 3. 创建 HTTP 请求
|
||||
|
req, err := http.NewRequest("POST", url, requestBody) |
||||
|
if err != nil { |
||||
|
return "", fmt.Errorf("HTTP 请求创建失败: %v", err) |
||||
|
} |
||||
|
req.Header.Set("Content-Type", "application/json") |
||||
|
|
||||
|
// 4. 发送请求
|
||||
|
client := &http.Client{} |
||||
|
resp, err := client.Do(req) |
||||
|
if err != nil { |
||||
|
return "", fmt.Errorf("HTTP 请求失败: %v", err) |
||||
|
} |
||||
|
defer resp.Body.Close() |
||||
|
|
||||
|
// 5. 检查 HTTP 状态码
|
||||
|
if resp.StatusCode != http.StatusOK { |
||||
|
return "", fmt.Errorf("HTTP 状态码错误: %v", resp.Status) |
||||
|
} |
||||
|
|
||||
|
// 6. 读取并解析响应体
|
||||
|
body, err := ioutil.ReadAll(resp.Body) |
||||
|
if err != nil { |
||||
|
return "", fmt.Errorf("读取响应失败: %v", err) |
||||
|
} |
||||
|
|
||||
|
// 7. 解析 JSON 数据
|
||||
|
var jsonResponse map[string]interface{} |
||||
|
if err = json.Unmarshal(body, &jsonResponse); err != nil { |
||||
|
return "", fmt.Errorf("解析 JSON 失败: %v", err) |
||||
|
} |
||||
|
|
||||
|
// 8. 提取并直接返回 jwcode
|
||||
|
if data, ok := jsonResponse["data"].(map[string]interface{}); ok { |
||||
|
if jwcode, exists := data["jwcode"].(string); exists { |
||||
|
return jwcode, nil // 直接返回 jwcode
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return "", errors.New("响应体中没有 jwcode") |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue