diff --git a/controller/user.go b/controller/user.go index 014f974f..2c32213a 100644 --- a/controller/user.go +++ b/controller/user.go @@ -243,6 +243,42 @@ func GetUser(c *gin.Context) { return } +func GenerateAccessToken(c *gin.Context) { + id := c.GetInt("id") + user, err := model.GetUserById(id, true) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": err.Error(), + }) + return + } + user.AccessToken = common.GetUUID() + + if model.DB.Where("token = ?", user.AccessToken).First(user).RowsAffected != 0 { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": "请重试,系统生成的 UUID 竟然重复了!", + }) + return + } + + if err := user.Update(false); err != nil { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": err.Error(), + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "success": true, + "message": "", + "data": user.AccessToken, + }) + return +} + func GetSelf(c *gin.Context) { id := c.GetInt("id") user, err := model.GetUserById(id, false) diff --git a/middleware/auth.go b/middleware/auth.go index 64fdb0a1..915114a7 100644 --- a/middleware/auth.go +++ b/middleware/auth.go @@ -16,12 +16,31 @@ func authHelper(c *gin.Context, minRole int) { id := session.Get("id") status := session.Get("status") if username == nil { - c.JSON(http.StatusUnauthorized, gin.H{ - "success": false, - "message": "无权进行此操作,未登录", - }) - c.Abort() - return + // Check access token + accessToken := c.Request.Header.Get("Authorization") + if accessToken == "" { + c.JSON(http.StatusUnauthorized, gin.H{ + "success": false, + "message": "无权进行此操作,未登录且未提供 access token", + }) + c.Abort() + return + } + user := model.ValidateAccessToken(accessToken) + if user != nil && user.Username != "" { + // Token is valid + username = user.Username + role = user.Role + id = user.Id + status = user.Status + } else { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": "无权进行此操作,access token 无效", + }) + c.Abort() + return + } } if status.(int) == common.UserStatusDisabled { c.JSON(http.StatusOK, gin.H{ diff --git a/model/user.go b/model/user.go index 4ad3b682..2bd0e0eb 100644 --- a/model/user.go +++ b/model/user.go @@ -3,6 +3,7 @@ package model import ( "errors" "one-api/common" + "strings" ) // User if you add sensitive fields, don't forget to clean them in setupLogin function. @@ -19,6 +20,7 @@ type User struct { WeChatId string `json:"wechat_id" gorm:"column:wechat_id;index"` VerificationCode string `json:"verification_code" gorm:"-:all"` // this field is only for Email verification, don't save it to database! Balance int `json:"balance" gorm:"type:int;default:0"` + AccessToken string `json:"access_token" gorm:"column:access_token;uniqueIndex"` // this token is for system management } func GetMaxUserId() int { @@ -188,3 +190,15 @@ func IsAdmin(userId int) bool { } return user.Role >= common.RoleAdminUser } + +func ValidateAccessToken(token string) (user *User) { + if token == "" { + return nil + } + token = strings.Replace(token, "Bearer ", "", 1) + user = &User{} + if DB.Where("access_token = ?", token).First(user).RowsAffected == 1 { + return user + } + return nil +} diff --git a/router/api-router.go b/router/api-router.go index b90a4105..ded70500 100644 --- a/router/api-router.go +++ b/router/api-router.go @@ -35,6 +35,7 @@ func SetApiRouter(router *gin.Engine) { selfRoute.GET("/self", controller.GetSelf) selfRoute.PUT("/self", controller.UpdateSelf) selfRoute.DELETE("/self", controller.DeleteSelf) + selfRoute.GET("/token", controller.GenerateAccessToken) } adminRoute := userRoute.Group("/") diff --git a/web/src/components/PersonalSetting.js b/web/src/components/PersonalSetting.js index e012efa4..63077ec7 100644 --- a/web/src/components/PersonalSetting.js +++ b/web/src/components/PersonalSetting.js @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { Button, Divider, Form, Header, Image, Modal } from 'semantic-ui-react'; +import { Button, Divider, Form, Header, Image, Message, Modal } from 'semantic-ui-react'; import { Link } from 'react-router-dom'; import { API, copy, showError, showInfo, showSuccess } from '../helpers'; import Turnstile from 'react-turnstile'; @@ -34,6 +34,17 @@ const PersonalSetting = () => { setInputs((inputs) => ({ ...inputs, [name]: value })); }; + const generateAccessToken = async () => { + const res = await API.get('/api/user/token'); + const { success, message, data } = res.data; + if (success) { + await copy(data); + showSuccess(`令牌已重置并已复制到剪贴板:${data}`); + } else { + showError(message); + } + }; + const bindWeChat = async () => { if (inputs.wechat_verification_code === '') return; const res = await API.get( @@ -92,9 +103,13 @@ const PersonalSetting = () => { return (