4 Commits 
		
	
	
		
			0d42255da7
			...
			f72b519ed2
		
	
    | Author | SHA1 | Message | Date | 
|---|---|---|---|
| 
							
							
								 | 
						f72b519ed2 | 
							
							
								
								合并dhy分支内容
							
							
							
							
							
							
								
管理端-登录、退出接口 客户端工具类-获取token  | 
						11 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  | 
						11 months ago | 
| 
							
							
								 | 
						4e47b507b0 | 
							
							
								
								管理端-登录、退出接口
							
							
							
							
							
							
								
客户端工具类-获取token  | 
						11 months ago | 
| 
							
							
								 | 
						96c960c04f | 
							
							
								
								提交登录
							
							
							
							
								
 | 
						11 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