up
This commit is contained in:
293
board/main.go
Normal file
293
board/main.go
Normal file
@@ -0,0 +1,293 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-contrib/sessions/cookie"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/google/go-github/v50/github"
|
||||
"github.com/joho/godotenv"
|
||||
"golang.org/x/oauth2"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var db *gorm.DB
|
||||
var jwtSecret = []byte("JWT_SECRET")
|
||||
var oauthConf *oauth2.Config
|
||||
|
||||
func main() {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Fatal("Error loading .env file")
|
||||
}
|
||||
oauthConf = &oauth2.Config{
|
||||
ClientID: os.Getenv("GITHUB_CLIENT_ID"),
|
||||
ClientSecret: os.Getenv("GITHUB_CLIENT_SECRET"),
|
||||
// Scopes: []string{"read:user", "user:email"},
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "https://github.com/login/oauth/authorize",
|
||||
TokenURL: "https://github.com/login/oauth/access_token",
|
||||
}}
|
||||
|
||||
initDB()
|
||||
|
||||
router := gin.Default()
|
||||
|
||||
store := cookie.NewStore([]byte("secret"))
|
||||
router.Use(sessions.Sessions("my-session", store))
|
||||
|
||||
router.Use(cors.New(cors.Config{
|
||||
AllowOrigins: []string{"*"},
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD"},
|
||||
AllowHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
|
||||
AllowCredentials: true,
|
||||
}))
|
||||
router.GET("/", func(ctx *gin.Context) { ctx.Writer.WriteString("hello world") })
|
||||
router.GET("/auth/github", githubLoginHandler)
|
||||
router.GET("/auth/github/callback", githubCallbackHandler)
|
||||
|
||||
router.Run(":8000")
|
||||
}
|
||||
|
||||
func githubLoginHandler(c *gin.Context) {
|
||||
state := generateState()
|
||||
|
||||
session := sessions.Default(c)
|
||||
session.Set("state", state)
|
||||
if err := session.Save(); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
url := oauthConf.AuthCodeURL("state")
|
||||
log.Println(url)
|
||||
c.Redirect(http.StatusFound, url)
|
||||
}
|
||||
|
||||
func githubCallbackHandler(c *gin.Context) {
|
||||
code, state := c.Query("code"), c.Query("state")
|
||||
session := sessions.Default(c)
|
||||
savedState := session.Get("state")
|
||||
if savedState == nil || savedState.(string) != state {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid state parameter."})
|
||||
return
|
||||
}
|
||||
|
||||
// 使用 code 换取 token
|
||||
token, err := oauthConf.Exchange(context.Background(), code)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("授权失败: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
log.Println("token:", token)
|
||||
|
||||
// 使用 token 获取 GitHub 用户信息
|
||||
client := github.NewClient(oauthConf.Client(context.Background(), token))
|
||||
// client := github.NewClient(oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(token)))
|
||||
user, _, err := client.Users.Get(c.Request.Context(), "")
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": fmt.Errorf("获取用户信息失败: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
// err = storeUserToDB(user)
|
||||
// if err != nil {
|
||||
// log.Println("Error storing user to DB:", err)
|
||||
// c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "Internal server error"})
|
||||
// return
|
||||
// }
|
||||
|
||||
log.Printf("%#v\n", user)
|
||||
log.Println(user.GetEmail(), user.GetName(), user.GetID(), user.GetAvatarURL())
|
||||
// 处理用户信息
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"id": user.ID,
|
||||
"login": user.Login,
|
||||
"name": user.Name,
|
||||
"email": user.Email,
|
||||
"location": user.Location,
|
||||
"scopes": token.Extra("scope"),
|
||||
})
|
||||
// jwtToken, err := generateJWTToken(*user.ID)
|
||||
// if err != nil {
|
||||
// log.Println("Error generating JWT token:", err)
|
||||
// c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "Internal server error"})
|
||||
// return
|
||||
// }
|
||||
|
||||
// c.SetCookie("token", jwtToken, 60*60*24, "/", "localhost", false, true)
|
||||
// c.Redirect(http.StatusFound, "http://152.70.110.4:8000")
|
||||
}
|
||||
|
||||
func initDB() {
|
||||
dsn := os.Getenv("MYSQL_DSN")
|
||||
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
log.Fatal("Error opening database:", err)
|
||||
}
|
||||
// db.AutoMigrate()
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
log.Fatal("Error connecting to database:", err)
|
||||
}
|
||||
sqlDB.SetMaxIdleConns(10)
|
||||
sqlDB.SetMaxOpenConns(100)
|
||||
sqlDB.SetConnMaxLifetime(time.Hour)
|
||||
|
||||
log.Println("Database connection established")
|
||||
}
|
||||
|
||||
func generateState() string {
|
||||
return generateRandomString(20)
|
||||
}
|
||||
|
||||
func generateCode() string {
|
||||
return generateRandomString(20)
|
||||
}
|
||||
|
||||
func generateRandomString(length int) string {
|
||||
byteArr := make([]byte, length)
|
||||
_, err := rand.Read(byteArr)
|
||||
if err != nil {
|
||||
log.Fatal("Error generating random string:", err)
|
||||
}
|
||||
|
||||
return base64.URLEncoding.EncodeToString(byteArr)
|
||||
}
|
||||
|
||||
func storeStateToDB(state, code string) error {
|
||||
query := "INSERT INTO oauth_state (state, code) VALUES (?, ?)"
|
||||
err := db.Exec(query, state, code).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyState(state, code string) bool {
|
||||
query := "SELECT COUNT(*) FROM oauth_state WHERE state = ? AND code = ?"
|
||||
row := db.Exec(query, state, code)
|
||||
|
||||
var count int
|
||||
err := row.Scan(&count)
|
||||
if err != nil {
|
||||
log.Println("Error verifying state:", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func storeUserToDB(user *github.User) error {
|
||||
query := "INSERT INTO users (id, login, email) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE login = VALUES(login), email = VALUES(email)"
|
||||
|
||||
err := db.Exec(query, user.GetID(), user.GetLogin(), user.GetEmail()).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generate a signed JWT containing the user ID as a claim.
|
||||
func generateJWT(userID int64) (string, error) {
|
||||
token := jwt.New(jwt.SigningMethodHS256)
|
||||
claims := jwt.MapClaims{
|
||||
"sub": userID,
|
||||
"exp": time.Now().Add(24 * time.Hour).Unix(),
|
||||
}
|
||||
token.Claims = claims
|
||||
|
||||
signedToken, err := token.SignedString(jwtSecret)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return signedToken, nil
|
||||
}
|
||||
|
||||
func verifyJWTToken(tokenString string) {
|
||||
// 验证 token
|
||||
tokenValue, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
// 该函数用于验证令牌的签名方法是否正确
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
|
||||
return jwtSecret, nil
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println("Error parsing token string:", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取声明信息
|
||||
if claims, ok := tokenValue.Claims.(jwt.MapClaims); ok && tokenValue.Valid {
|
||||
// fmt.Println("Authorized:", claims["authorized"])
|
||||
fmt.Println("User:", claims["user"])
|
||||
fmt.Println("Expiration time:", time.Unix(int64(claims["exp"].(float64)), 0))
|
||||
} else {
|
||||
fmt.Println("Invalid token.")
|
||||
}
|
||||
}
|
||||
|
||||
// Middleware to require authentication via JWT.
|
||||
func requireAuth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
tokenString := c.GetHeader("Authorization")
|
||||
if tokenString == "" {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Missing Authorization header."})
|
||||
return
|
||||
}
|
||||
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
return jwtSecret, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if err == jwt.ErrSignatureInvalid {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token signature."})
|
||||
return
|
||||
}
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token."})
|
||||
return
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok || !token.Valid {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token claims."})
|
||||
return
|
||||
}
|
||||
|
||||
userID, ok := claims["sub"].(float64)
|
||||
if !ok {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid subject claim type."})
|
||||
return
|
||||
}
|
||||
|
||||
// Use the userID to look up the user in your database or perform other authorization logic.
|
||||
// ...
|
||||
// If authorized, set the user ID on the Gin context for use by downstream handlers.
|
||||
c.Set("userID", int64(userID))
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
1
board/readme.md
Normal file
1
board/readme.md
Normal file
@@ -0,0 +1 @@
|
||||
{"id":"chatcmpl-730a5g4fJBzoGZrUwZGCw1VwpvEhc","object":"chat.completion","created":1680951573,"model":"gpt-3.5-turbo-0301","usage":{"prompt_tokens":10,"completion_tokens":10,"total_tokens":20,"pre_token_count":4096,"pre_total":41,"adjust_total":40,"final_total":1},"choices":[{"message":{"role":"assistant","content":"Hello there! How can I assist you today?"},"finish_reason":"stop","index":0}]}
|
||||
Reference in New Issue
Block a user