Files
bl/modules/base/service/base.bbs.go
昔念 ba60b03bbf ```
feat(login): 添加Bcrypt密码哈希功能并集成用户认证

- 引入golang.org/x/crypto/bcrypt包用于密码哈希处理
- 实现HashPassword函数对密码进行Bcrypt哈希
- 实现CheckPasswordHash函数验证密码与哈希匹配
- 添加示例代码演示密码哈希和验证功能

feat(login): 集成外部用户信息服务

- 实现GetUserInfo方法调用外部服务获取用户信息
- 添加用户信息展示的示例代码
- 集成用户登录验证流程

fix
2025-12-31 16:20:01 +08:00

172 lines
5.3 KiB
Go

package service
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/cookiejar"
"net/url"
"strings"
)
// TokenResponse 用来解析第一次请求返回的 JSON
type TokenResponse struct {
Token string `json:"token"`
UserID int `json:"userId"`
}
// UserResponse 最外层响应
type UserResponse struct {
Data UserData `json:"data"`
Included []Group `json:"included"`
}
// UserData 用户数据
type UserData struct {
Type string `json:"type"`
ID string `json:"id"`
Attributes UserAttributes `json:"attributes"`
Relationships Relationships `json:"relationships"`
}
// UserAttributes 用户详细属性
type UserAttributes struct {
Username string `json:"username"`
DisplayName string `json:"displayName"`
AvatarUrl string `json:"avatarUrl"`
Slug string `json:"slug"`
JoinTime string `json:"joinTime"`
DiscussionCount int `json:"discussionCount"`
CommentCount int `json:"commentCount"`
CanEdit bool `json:"canEdit"`
CanEditCredentials bool `json:"canEditCredentials"`
CanEditGroups bool `json:"canEditGroups"`
CanDelete bool `json:"canDelete"`
LastSeenAt string `json:"lastSeenAt"`
Achievements []string `json:"achievements"`
Followed *bool `json:"followed"` // 可能为 null
FollowerCount int `json:"followerCount"`
FollowingCount int `json:"followingCount"`
Money float64 `json:"money"`
CanEditMoney bool `json:"canEditMoney"`
CanSuspend bool `json:"canSuspend"`
LastCheckinTime string `json:"lastCheckinTime"`
TotalContinuousCheckIn int `json:"totalContinuousCheckIn"`
CheckInCompatibleExtensions []string `json:"checkInCompatibleExtensions"`
CanCheckin bool `json:"canCheckin"`
CanCheckinContinuous bool `json:"canCheckinContinuous"`
CanBeFollowed bool `json:"canBeFollowed"`
FofUploadUploadCountCurrent int `json:"fof-upload-uploadCountCurrent"`
FofUploadUploadCountAll int `json:"fof-upload-uploadCountAll"`
BestAnswerCount int `json:"bestAnswerCount"`
IsBanned bool `json:"isBanned"`
CanBanIP bool `json:"canBanIP"`
CanEditNickname bool `json:"canEditNickname"`
}
// Relationships 用户关系
type Relationships struct {
Groups RelationshipData `json:"groups"`
Achievements RelationshipData `json:"achievements"`
}
// RelationshipData 关系数据
type RelationshipData struct {
Data []RelationshipItem `json:"data"`
}
// RelationshipItem 关系项
type RelationshipItem struct {
Type string `json:"type"`
ID string `json:"id"`
}
// Group 组信息
type Group struct {
Type string `json:"type"`
ID string `json:"id"`
Attributes GroupAttributes `json:"attributes"`
}
// GroupAttributes 组属性
type GroupAttributes struct {
NameSingular string `json:"nameSingular"`
NamePlural string `json:"namePlural"`
Color string `json:"color"`
Icon string `json:"icon"`
IsHidden int `json:"isHidden"`
}
// GetUserInfo 输入用户名和密码,返回用户信息结构体
func GetUserInfo(username, password string) (*UserResponse, error) {
// 创建带 Cookie 存储的 HTTP 客户端
jar, err := cookiejar.New(nil)
if err != nil {
return nil, fmt.Errorf("创建 CookieJar 失败: %w", err)
}
client := &http.Client{Jar: jar}
// 1. POST 获取 token
tokenURL := "http://bs.seersun.com/api/token"
formData := url.Values{}
formData.Set("identification", username)
formData.Set("password", password)
req, err := http.NewRequest("POST", tokenURL, strings.NewReader(formData.Encode()))
if err != nil {
return nil, fmt.Errorf("创建请求失败: %w", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("发送请求失败: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %w", err)
}
// 解析 token
var tokenResp TokenResponse
err = json.Unmarshal(body, &tokenResp)
if err != nil {
return nil, fmt.Errorf("解析 token 失败: %w", err)
}
// 提取 CSRF Token
csrfToken := resp.Header.Get("X-CSRF-Token")
// 2. GET 获取该用户的详细信息
usersURL := fmt.Sprintf("http://bs.seersun.com/api/users/%d", tokenResp.UserID)
req2, err := http.NewRequest("GET", usersURL, nil)
if err != nil {
return nil, fmt.Errorf("创建请求失败: %w", err)
}
req2.Header.Set("Authentication", tokenResp.Token)
req2.Header.Set("X-CSRF-Token", csrfToken)
resp2, err := client.Do(req2)
if err != nil {
return nil, fmt.Errorf("发送请求失败: %w", err)
}
defer resp2.Body.Close()
body2, err := io.ReadAll(resp2.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %w", err)
}
// 解析用户信息
var userResp UserResponse
err = json.Unmarshal(body2, &userResp)
if err != nil {
return nil, fmt.Errorf("解析用户信息失败: %w", err)
}
return &userResp, nil
}