Compare commits

..

6 Commits

Author SHA1 Message Date
Sakurasan 2aa761452d up 2023-04-13 22:04:48 +08:00
Sakurasan a9783ae092 up 2023-04-13 02:49:39 +08:00
Sakurasan 08d5bfbf19 up 2023-04-13 01:06:15 +08:00
Sakurasan 8ece9bdf37 up 2023-04-12 22:38:03 +08:00
Sakurasan 9e8c94006d Merge branch 'master' of https://git.gopher.ink/mirrors2/api-proxy 2023-04-11 03:40:33 +08:00
Sakurasan 6f82335a7d up 2023-04-11 03:38:24 +08:00
5 changed files with 256 additions and 122 deletions
+6 -104
View File
@@ -1,9 +1,7 @@
package main package main
import ( import (
"context" "chat/services/board"
"crypto/rand"
"encoding/base64"
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
@@ -17,29 +15,21 @@ import (
"github.com/golang-jwt/jwt" "github.com/golang-jwt/jwt"
"github.com/google/go-github/v50/github" "github.com/google/go-github/v50/github"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"golang.org/x/oauth2"
"gorm.io/driver/mysql" "gorm.io/driver/mysql"
"gorm.io/gorm" "gorm.io/gorm"
) )
var db *gorm.DB var db *gorm.DB
var jwtSecret = []byte("JWT_SECRET") var jwtSecret = []byte("JWT_SECRET")
var oauthConf *oauth2.Config
func main() { func init() {
err := godotenv.Load() err := godotenv.Load()
if err != nil { if err != nil {
log.Fatal("Error loading .env file") 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() initDB()
router := gin.Default() router := gin.Default()
@@ -54,82 +44,12 @@ func main() {
AllowCredentials: true, AllowCredentials: true,
})) }))
router.GET("/", func(ctx *gin.Context) { ctx.Writer.WriteString("hello world") }) router.GET("/", func(ctx *gin.Context) { ctx.Writer.WriteString("hello world") })
router.GET("/auth/github", githubLoginHandler) router.GET("/auth/idt", board.IdentityHandler)
router.GET("/auth/github/callback", githubCallbackHandler) router.POST("/auth/signin/sso", board.SSOHandler)
router.Run(":8000") 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() { func initDB() {
dsn := os.Getenv("MYSQL_DSN") dsn := os.Getenv("MYSQL_DSN")
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
@@ -148,24 +68,6 @@ func initDB() {
log.Println("Database connection established") 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 { func storeStateToDB(state, code string) error {
query := "INSERT INTO oauth_state (state, code) VALUES (?, ?)" query := "INSERT INTO oauth_state (state, code) VALUES (?, ?)"
err := db.Exec(query, state, code).Error err := db.Exec(query, state, code).Error
+86 -18
View File
@@ -1,29 +1,97 @@
html
<template> <template>
<div> <div>
<h1>Login with GitHub</h1> <button @click="handleGithubLogin">Login with GitHub</button>
<button @click="login">Login with GitHub</button>
</div> </div>
</template> </template>
<script setup> <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 auth = reactive({
const client_id = '9f75836d51c1cb447fa5';//process.env.VUE_APP_GITHUB_CLIENT_ID; type: "github",
const redirect_uri = "http://localhost:5173/login/callback"; redirectUrl: null,
const scope = "read:user"; code: null,
const url = `https://github.com/login/oauth/authorize?client_id=${client_id}&redirect_uri=${redirect_uri}&scope=${scope}`; state: null,
console.log(url); });
window.location.href = url;
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 { // // 监听 URL 变化,处理从 GitHub 授权页面回调回来的 code 参数
// name: 'Login', // const handleUrlChange = () => {
// setup() { // const urlParams = new URLSearchParams(window.location.search);
// return { // const code = urlParams.get('code');
// login, // if (code) {
// }; // handleCallback(code);
// }, // }
// }; // };
// window.addEventListener('load', handleUrlChange);
onMounted(() => {
getToken()
})
</script> </script>
+145
View File
@@ -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")
}
+7
View File
@@ -9,3 +9,10 @@ type user struct {
AvatarUrl string `json:"avatar_Url,omitempty"` // 用户在GitHub上的头像URL(如果有)。 AvatarUrl string `json:"avatar_Url,omitempty"` // 用户在GitHub上的头像URL(如果有)。
CreatedAt string `json:"created_At,omitempty"` // 记录插入到数据库中的时间。 CreatedAt string `json:"created_At,omitempty"` // 记录插入到数据库中的时间。
} }
type Auth struct {
Type string
Code string
State string
RedirectUrl string
}
+12
View File
@@ -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
}