Compare commits
6 Commits
e3ae752eb5
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 2aa761452d | |||
| a9783ae092 | |||
| 08d5bfbf19 | |||
| 8ece9bdf37 | |||
| 9e8c94006d | |||
| 6f82335a7d |
+6
-104
@@ -1,9 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"chat/services/board"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
@@ -17,29 +15,21 @@ import (
|
||||
"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() {
|
||||
func init() {
|
||||
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",
|
||||
}}
|
||||
}
|
||||
|
||||
func main() {
|
||||
initDB()
|
||||
|
||||
router := gin.Default()
|
||||
@@ -54,82 +44,12 @@ func main() {
|
||||
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.GET("/auth/idt", board.IdentityHandler)
|
||||
router.POST("/auth/signin/sso", board.SSOHandler)
|
||||
|
||||
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.AccessToken)
|
||||
|
||||
// 使用 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{})
|
||||
@@ -148,24 +68,6 @@ func initDB() {
|
||||
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
|
||||
|
||||
@@ -1,29 +1,97 @@
|
||||
html
|
||||
<template>
|
||||
<div>
|
||||
<h1>Login with GitHub</h1>
|
||||
<button @click="login">Login with GitHub</button>
|
||||
<button @click="handleGithubLogin">Login with GitHub</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { reactive, onMounted } from 'vue';
|
||||
import axios from 'axios';
|
||||
import { useRouter } from 'vue-router';
|
||||
const router = useRouter()
|
||||
|
||||
const login = async () => {
|
||||
const client_id = '9f75836d51c1cb447fa5';//process.env.VUE_APP_GITHUB_CLIENT_ID;
|
||||
const redirect_uri = "http://localhost:5173/login/callback";
|
||||
const scope = "read:user";
|
||||
const url = `https://github.com/login/oauth/authorize?client_id=${client_id}&redirect_uri=${redirect_uri}&scope=${scope}`;
|
||||
console.log(url);
|
||||
window.location.href = url;
|
||||
const auth = reactive({
|
||||
type: "github",
|
||||
redirectUrl: null,
|
||||
code: null,
|
||||
state: null,
|
||||
});
|
||||
|
||||
const handleGithubLogin = async () => {
|
||||
try {
|
||||
const response = await axios.get('http://localhost:8000/auth/idt')
|
||||
const redirectUrl = response.data.redirectUri
|
||||
auth.state = response.data.state
|
||||
auth.redirectUrl = redirectUrl
|
||||
localStorage.setItem("state", response.data.state)
|
||||
console.log("state:", response.data.state, "url:", response.data.redirectUri)
|
||||
window.location.href = redirectUrl
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleCallback = async () => {
|
||||
// const code = this.$route.query.code
|
||||
// const status = this.$route.query.status
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const code = urlParams.get('code')
|
||||
const status = urlParams.get('state')
|
||||
try {
|
||||
const response = await axios.post('/auth/signin/sso', {
|
||||
code,
|
||||
status
|
||||
})
|
||||
const jwt = response.data.jwt
|
||||
localStorage.setItem('jwt', jwt)
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if (error.response.status != 200) {
|
||||
console.log('授权失败')
|
||||
}
|
||||
}
|
||||
};
|
||||
async function getToken() {
|
||||
const url = new URL(window.location.href)
|
||||
const code = url.searchParams.get('code')
|
||||
const state = url.searchParams.get('state')
|
||||
console.log(code,state);
|
||||
// const code = router.currentRoute.value.query.code
|
||||
// const status = router.currentRoute.value.query.status
|
||||
if (code.length > 0 && state.length > 0) {
|
||||
console.log("okokokokokok");
|
||||
try {
|
||||
const data = {
|
||||
'code': code,
|
||||
'state': state
|
||||
}
|
||||
const jwtResponse = await axios.post('http://localhost:8000/auth/signin/sso', data)
|
||||
const jwt = jwtResponse.data.jwt
|
||||
localStorage.setItem('jwt', jwt)
|
||||
console.log("jwt:", jwtResponse)
|
||||
// window.location.href = '/'
|
||||
router.push('/')
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const logout = () => {
|
||||
localStorage.setItem('jwt', '');
|
||||
};
|
||||
|
||||
// export default {
|
||||
// name: 'Login',
|
||||
// setup() {
|
||||
// return {
|
||||
// login,
|
||||
// };
|
||||
// },
|
||||
// // 监听 URL 变化,处理从 GitHub 授权页面回调回来的 code 参数
|
||||
// const handleUrlChange = () => {
|
||||
// const urlParams = new URLSearchParams(window.location.search);
|
||||
// const code = urlParams.get('code');
|
||||
// if (code) {
|
||||
// handleCallback(code);
|
||||
// }
|
||||
// };
|
||||
// window.addEventListener('load', handleUrlChange);
|
||||
|
||||
onMounted(() => {
|
||||
getToken()
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,145 @@
|
||||
package board
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/go-github/github"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
var ()
|
||||
|
||||
type SignIn struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type SSOSignIn struct {
|
||||
Code string `json:"code"`
|
||||
State string `json:"state"`
|
||||
RedirectURI string `json:"redirectUri"`
|
||||
}
|
||||
|
||||
type SignUp struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
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 IdentityHandler(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
|
||||
}
|
||||
oauthConf := &oauth2.Config{
|
||||
ClientID: os.Getenv("GITHUB_CLIENT_ID"),
|
||||
ClientSecret: os.Getenv("GITHUB_CLIENT_SECRET"),
|
||||
RedirectURL: "http://localhost:5173/",
|
||||
// Scopes: []string{"read:user", "user:email"},
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "https://github.com/login/oauth/authorize",
|
||||
TokenURL: "https://github.com/login/oauth/access_token",
|
||||
}}
|
||||
url := oauthConf.AuthCodeURL(state)
|
||||
log.Println(url)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"state": state,
|
||||
"redirectUri": url,
|
||||
})
|
||||
}
|
||||
|
||||
func SSOHandler(c *gin.Context) {
|
||||
signin := SSOSignIn{}
|
||||
if err := c.ShouldBind(&signin); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unmashal request body."})
|
||||
return
|
||||
}
|
||||
session := sessions.Default(c)
|
||||
savedState := session.Get("state")
|
||||
if savedState == nil || savedState.(string) != signin.State {
|
||||
log.Println("savedState", savedState, "signin.State", signin.State)
|
||||
// c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid state parameter."})
|
||||
// return
|
||||
}
|
||||
|
||||
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",
|
||||
}}
|
||||
// 使用 code 换取 token
|
||||
token, err := oauthConf.Exchange(context.Background(), signin.Code)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("授权失败: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
log.Println("token:", &token.AccessToken)
|
||||
|
||||
// 使用 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")
|
||||
}
|
||||
@@ -9,3 +9,10 @@ type user struct {
|
||||
AvatarUrl string `json:"avatar_Url,omitempty"` // 用户在GitHub上的头像URL(如果有)。
|
||||
CreatedAt string `json:"created_At,omitempty"` // 记录插入到数据库中的时间。
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
Type string
|
||||
Code string
|
||||
State string
|
||||
RedirectUrl string
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package board
|
||||
|
||||
type UserCreate struct {
|
||||
// Domain specific fields
|
||||
Username string `json:"username"`
|
||||
Role string `json:"role"`
|
||||
Email string `json:"email"`
|
||||
Nickname string `json:"nickname"`
|
||||
Password string `json:"password"`
|
||||
PasswordHash string
|
||||
OpenID string
|
||||
}
|
||||
Reference in New Issue
Block a user