✨ add qq oauth2 login
This commit is contained in:
@@ -50,6 +50,12 @@ type GiteeUser struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetGiteeRedirectUrl 获取Gitee登录地址
|
// GetGiteeRedirectUrl 获取Gitee登录地址
|
||||||
|
// @Summary 获取Gitee登录地址
|
||||||
|
// @Description 获取Gitee登录地址
|
||||||
|
// @Tags OAuth
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {string} string "登录地址"
|
||||||
|
// @Router /api/oauth/gitee/get_url [get]
|
||||||
func (OAuthAPI) GetGiteeRedirectUrl(c *gin.Context) {
|
func (OAuthAPI) GetGiteeRedirectUrl(c *gin.Context) {
|
||||||
clientID := global.CONFIG.OAuth.Gitee.ClientID
|
clientID := global.CONFIG.OAuth.Gitee.ClientID
|
||||||
redirectURI := global.CONFIG.OAuth.Gitee.RedirectURI
|
redirectURI := global.CONFIG.OAuth.Gitee.RedirectURI
|
||||||
@@ -123,6 +129,11 @@ func GetGiteeUserInfo(token *Token) (map[string]interface{}, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GiteeCallback 处理Gitee回调
|
// GiteeCallback 处理Gitee回调
|
||||||
|
// @Summary 处理Gitee回调
|
||||||
|
// @Description 处理Gitee回调
|
||||||
|
// @Tags OAuth
|
||||||
|
// @Produce json
|
||||||
|
// @Router /api/oauth/gitee/callback [get]
|
||||||
func (OAuthAPI) GiteeCallback(c *gin.Context) {
|
func (OAuthAPI) GiteeCallback(c *gin.Context) {
|
||||||
var err error
|
var err error
|
||||||
// 获取 code
|
// 获取 code
|
||||||
|
@@ -16,9 +16,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Token struct {
|
|
||||||
AccessToken string `json:"access_token"`
|
|
||||||
}
|
|
||||||
type GitHubUser struct {
|
type GitHubUser struct {
|
||||||
AvatarURL string `json:"avatar_url"`
|
AvatarURL string `json:"avatar_url"`
|
||||||
Bio interface{} `json:"bio"`
|
Bio interface{} `json:"bio"`
|
||||||
@@ -56,10 +53,17 @@ type GitHubUser struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetRedirectUrl 获取github登录url
|
// GetRedirectUrl 获取github登录url
|
||||||
|
// @Summary 获取github登录url
|
||||||
|
// @Description 获取github登录url
|
||||||
|
// @Tags OAuth
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {string} string "登录url"
|
||||||
|
// @Router /api/oauth/github/get_url [get]
|
||||||
func (OAuthAPI) GetRedirectUrl(c *gin.Context) {
|
func (OAuthAPI) GetRedirectUrl(c *gin.Context) {
|
||||||
|
state := c.Query("state")
|
||||||
clientId := global.CONFIG.OAuth.Github.ClientID
|
clientId := global.CONFIG.OAuth.Github.ClientID
|
||||||
redirectUrl := global.CONFIG.OAuth.Github.RedirectURI
|
redirectUrl := global.CONFIG.OAuth.Github.RedirectURI
|
||||||
url := "https://github.com/login/oauth/authorize?client_id=" + clientId + "&redirect_uri=" + redirectUrl
|
url := "https://github.com/login/oauth/authorize?client_id=" + clientId + "&redirect_uri=" + redirectUrl + "&state=" + state
|
||||||
result.OkWithData(url, c)
|
result.OkWithData(url, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -129,6 +133,13 @@ func GetUserInfo(token *Token) (map[string]interface{}, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Callback 登录回调函数
|
// Callback 登录回调函数
|
||||||
|
// @Summary 登录回调函数
|
||||||
|
// @Description 登录回调函数
|
||||||
|
// @Tags OAuth
|
||||||
|
// @Produce json
|
||||||
|
// @Param code query string true "code"
|
||||||
|
// @Success 200 {string} string "登录成功"
|
||||||
|
// @Router /api/oauth/github/callback [get]
|
||||||
func (OAuthAPI) Callback(c *gin.Context) {
|
func (OAuthAPI) Callback(c *gin.Context) {
|
||||||
var err error
|
var err error
|
||||||
// 获取 code
|
// 获取 code
|
||||||
|
@@ -20,6 +20,10 @@ var rolePermissionService = service.Service.RolePermissionService
|
|||||||
var permissionServiceService = service.Service.PermissionService
|
var permissionServiceService = service.Service.PermissionService
|
||||||
var roleService = service.Service.RoleService
|
var roleService = service.Service.RoleService
|
||||||
|
|
||||||
|
type Token struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
}
|
||||||
|
|
||||||
var script = `
|
var script = `
|
||||||
<script>
|
<script>
|
||||||
window.opener.postMessage('%s', '%s');
|
window.opener.postMessage('%s', '%s');
|
||||||
@@ -58,8 +62,11 @@ func HandelUserLogin(user model.ScaAuthUser) (bool, map[string]interface{}) {
|
|||||||
if er != nil {
|
if er != nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
accessToken, refreshToken, expiresAt := utils.GenerateAccessTokenAndRefreshToken(utils.JWTPayload{UserID: user.UID, RoleID: ids})
|
accessToken, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: user.UID, RoleID: ids})
|
||||||
|
if err != nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
refreshToken, expiresAt := utils.GenerateRefreshToken(utils.RefreshJWTPayload{UserID: user.UID, RoleID: ids}, time.Hour*24*7)
|
||||||
data := dto.ResponseData{
|
data := dto.ResponseData{
|
||||||
AccessToken: accessToken,
|
AccessToken: accessToken,
|
||||||
RefreshToken: refreshToken,
|
RefreshToken: refreshToken,
|
||||||
|
@@ -1 +1,160 @@
|
|||||||
package oauth_api
|
package oauth_api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
ginI18n "github.com/gin-contrib/i18n"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/http"
|
||||||
|
"schisandra-cloud-album/common/result"
|
||||||
|
"schisandra-cloud-album/global"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthQQme struct {
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
OpenID string `json:"openid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQQRedirectUrl 获取登录地址
|
||||||
|
// @Summary 获取QQ登录地址
|
||||||
|
// @Description 获取QQ登录地址
|
||||||
|
// @Tags 登录
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {string} string "登录地址"
|
||||||
|
// @Router /api/oauth/qq/get_url [get]
|
||||||
|
func (OAuthAPI) GetQQRedirectUrl(c *gin.Context) {
|
||||||
|
state := c.Query("state")
|
||||||
|
clientId := global.CONFIG.OAuth.QQ.ClientID
|
||||||
|
redirectURI := global.CONFIG.OAuth.QQ.RedirectURI
|
||||||
|
url := "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=" + clientId + "&redirect_uri=" + redirectURI + "&state=" + state
|
||||||
|
result.OkWithData(url, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQQTokenAuthUrl 通过code获取token认证url
|
||||||
|
func GetQQTokenAuthUrl(code string) string {
|
||||||
|
clientId := global.CONFIG.OAuth.QQ.ClientID
|
||||||
|
clientSecret := global.CONFIG.OAuth.QQ.ClientSecret
|
||||||
|
redirectURI := global.CONFIG.OAuth.QQ.RedirectURI
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s",
|
||||||
|
clientId, clientSecret, code, redirectURI,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQQToken 获取 token
|
||||||
|
func GetQQToken(url string) (*Token, error) {
|
||||||
|
|
||||||
|
// 形成请求
|
||||||
|
var req *http.Request
|
||||||
|
var err error
|
||||||
|
if req, err = http.NewRequest(http.MethodGet, url, nil); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("accept", "application/json")
|
||||||
|
|
||||||
|
// 发送请求并获得响应
|
||||||
|
var httpClient = http.Client{}
|
||||||
|
var res *http.Response
|
||||||
|
if res, err = httpClient.Do(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将响应体解析为 token,并返回
|
||||||
|
var token Token
|
||||||
|
if err = json.NewDecoder(res.Body).Decode(&token); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQQUserOpenID 获取用户 openid
|
||||||
|
func GetQQUserOpenID(token *Token) (*AuthQQme, error) {
|
||||||
|
|
||||||
|
// 形成请求
|
||||||
|
var userInfoUrl = "https://graph.qq.com/oauth2.0/me" // github用户信息获取接口
|
||||||
|
var req *http.Request
|
||||||
|
var err error
|
||||||
|
if req, err = http.NewRequest(http.MethodGet, userInfoUrl, nil); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("accept", "application/json")
|
||||||
|
req.Header.Set("Authorization", fmt.Sprintf("token %s", token.AccessToken))
|
||||||
|
// 发送请求并获取响应
|
||||||
|
var client = http.Client{}
|
||||||
|
var res *http.Response
|
||||||
|
if res, err = client.Do(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将响应体解析为 AuthQQme,并返回
|
||||||
|
var authQQme AuthQQme
|
||||||
|
if err = json.NewDecoder(res.Body).Decode(&authQQme); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &authQQme, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQQUserUserInfo 获取用户信息
|
||||||
|
func GetQQUserUserInfo(token *Token, openId string) (map[string]interface{}, error) {
|
||||||
|
|
||||||
|
clientId := global.CONFIG.OAuth.QQ.ClientID
|
||||||
|
// 形成请求
|
||||||
|
var userInfoUrl = "https://graph.qq.com/user/get_user_info??access_token=" + token.AccessToken + "&oauth_consumer_key=" + clientId + "&openid=" + openId
|
||||||
|
var req *http.Request
|
||||||
|
var err error
|
||||||
|
if req, err = http.NewRequest(http.MethodGet, userInfoUrl, nil); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("accept", "application/json")
|
||||||
|
//req.Header.Set("Authorization", fmt.Sprintf("token %s", token.AccessToken))
|
||||||
|
// 发送请求并获取响应
|
||||||
|
var client = http.Client{}
|
||||||
|
var res *http.Response
|
||||||
|
if res, err = client.Do(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将响应的数据写入 userInfo 中,并返回
|
||||||
|
var userInfo = make(map[string]interface{})
|
||||||
|
if err = json.NewDecoder(res.Body).Decode(&userInfo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return userInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// QQCallback QQ登录回调
|
||||||
|
// @Summary QQ登录回调
|
||||||
|
// @Description QQ登录回调
|
||||||
|
// @Tags 登录
|
||||||
|
// @Produce json
|
||||||
|
// @Router /api/oauth/qq/callback [get]
|
||||||
|
func (OAuthAPI) QQCallback(c *gin.Context) {
|
||||||
|
var err error
|
||||||
|
// 获取 code
|
||||||
|
var code = c.Query("code")
|
||||||
|
if code == "" {
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 通过 code, 获取 token
|
||||||
|
var tokenAuthUrl = GetQQTokenAuthUrl(code)
|
||||||
|
var token *Token
|
||||||
|
if token, err = GetQQToken(tokenAuthUrl); err != nil {
|
||||||
|
global.LOG.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
authQQme, err := GetQQUserOpenID(token)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过token,获取用户信息
|
||||||
|
var userInfo map[string]interface{}
|
||||||
|
if userInfo, err = GetQQUserUserInfo(token, authQQme.OpenID); err != nil {
|
||||||
|
global.LOG.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result.OkWithData(userInfo, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@@ -33,10 +33,19 @@ import (
|
|||||||
// @Summary 生成客户端ID
|
// @Summary 生成客户端ID
|
||||||
// @Description 生成客户端ID
|
// @Description 生成客户端ID
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} result.Result{data=string} "客户端ID"
|
|
||||||
// @Router /api/oauth/generate_client_id [get]
|
// @Router /api/oauth/generate_client_id [get]
|
||||||
func (OAuthAPI) GenerateClientId(c *gin.Context) {
|
func (OAuthAPI) GenerateClientId(c *gin.Context) {
|
||||||
ip := c.ClientIP()
|
// 尝试从 X-Real-IP 头部获取真实 IP
|
||||||
|
ip := c.GetHeader("X-Real-IP")
|
||||||
|
|
||||||
|
// 如果 X-Real-IP 头部不存在,则尝试从 X-Forwarded-For 头部获取
|
||||||
|
if ip == "" {
|
||||||
|
ip = c.GetHeader("X-Forwarded-For")
|
||||||
|
}
|
||||||
|
// 如果两者都不存在,则使用默认的 ClientIP 方法获取 IP
|
||||||
|
if ip == "" {
|
||||||
|
ip = c.ClientIP()
|
||||||
|
}
|
||||||
clientId := redis.Get(constant.UserLoginClientRedisKey + ip).Val()
|
clientId := redis.Get(constant.UserLoginClientRedisKey + ip).Val()
|
||||||
if clientId != "" {
|
if clientId != "" {
|
||||||
result.OkWithData(clientId, c)
|
result.OkWithData(clientId, c)
|
||||||
@@ -52,7 +61,6 @@ func (OAuthAPI) GenerateClientId(c *gin.Context) {
|
|||||||
// @Summary 微信回调验证
|
// @Summary 微信回调验证
|
||||||
// @Description 微信回调验证
|
// @Description 微信回调验证
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} result.Result{data=string} "验证结果"
|
|
||||||
// @Router /api/oauth/callback_notify [POST]
|
// @Router /api/oauth/callback_notify [POST]
|
||||||
func (OAuthAPI) CallbackNotify(c *gin.Context) {
|
func (OAuthAPI) CallbackNotify(c *gin.Context) {
|
||||||
rs, err := global.Wechat.Server.Notify(c.Request, func(event contract.EventInterface) interface{} {
|
rs, err := global.Wechat.Server.Notify(c.Request, func(event contract.EventInterface) interface{} {
|
||||||
@@ -126,7 +134,6 @@ func (OAuthAPI) CallbackNotify(c *gin.Context) {
|
|||||||
// @Summary 微信回调验证
|
// @Summary 微信回调验证
|
||||||
// @Description 微信回调验证
|
// @Description 微信回调验证
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} result.Result{data=string} "验证结果"
|
|
||||||
// @Router /api/oauth/callback_verify [get]
|
// @Router /api/oauth/callback_verify [get]
|
||||||
func (OAuthAPI) CallbackVerify(c *gin.Context) {
|
func (OAuthAPI) CallbackVerify(c *gin.Context) {
|
||||||
rs, err := global.Wechat.Server.VerifyURL(c.Request)
|
rs, err := global.Wechat.Server.VerifyURL(c.Request)
|
||||||
@@ -141,11 +148,20 @@ func (OAuthAPI) CallbackVerify(c *gin.Context) {
|
|||||||
// @Description 获取临时二维码
|
// @Description 获取临时二维码
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param client_id query string true "客户端ID"
|
// @Param client_id query string true "客户端ID"
|
||||||
// @Success 200 {object} result.Result{data=string} "临时二维码"
|
|
||||||
// @Router /api/oauth/get_temp_qrcode [get]
|
// @Router /api/oauth/get_temp_qrcode [get]
|
||||||
func (OAuthAPI) GetTempQrCode(c *gin.Context) {
|
func (OAuthAPI) GetTempQrCode(c *gin.Context) {
|
||||||
clientId := c.Query("client_id")
|
clientId := c.Query("client_id")
|
||||||
ip := c.ClientIP()
|
// 尝试从 X-Real-IP 头部获取真实 IP
|
||||||
|
ip := c.GetHeader("X-Real-IP")
|
||||||
|
|
||||||
|
// 如果 X-Real-IP 头部不存在,则尝试从 X-Forwarded-For 头部获取
|
||||||
|
if ip == "" {
|
||||||
|
ip = c.GetHeader("X-Forwarded-For")
|
||||||
|
}
|
||||||
|
// 如果两者都不存在,则使用默认的 ClientIP 方法获取 IP
|
||||||
|
if ip == "" {
|
||||||
|
ip = c.ClientIP()
|
||||||
|
}
|
||||||
if clientId == "" {
|
if clientId == "" {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
||||||
return
|
return
|
||||||
@@ -264,8 +280,11 @@ func handelUserLogin(user model.ScaAuthUser, clientId string) bool {
|
|||||||
if er != nil {
|
if er != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
accessToken, refreshToken, expiresAt := utils.GenerateAccessTokenAndRefreshToken(utils.JWTPayload{UserID: user.UID, RoleID: ids})
|
accessToken, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: user.UID, RoleID: ids})
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
refreshToken, expiresAt := utils.GenerateRefreshToken(utils.RefreshJWTPayload{UserID: user.UID, RoleID: ids}, time.Hour*24*7)
|
||||||
data := dto.ResponseData{
|
data := dto.ResponseData{
|
||||||
AccessToken: accessToken,
|
AccessToken: accessToken,
|
||||||
RefreshToken: refreshToken,
|
RefreshToken: refreshToken,
|
||||||
@@ -282,9 +301,19 @@ func handelUserLogin(user model.ScaAuthUser, clientId string) bool {
|
|||||||
"data": data,
|
"data": data,
|
||||||
"success": true,
|
"success": true,
|
||||||
}
|
}
|
||||||
res := websocket_api.SendMessageData(clientId, responseData)
|
tokenData, err := json.Marshal(responseData)
|
||||||
if !res {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
// gws方式发送消息
|
||||||
|
err = websocket_api.Handler.SendMessageToClient(clientId, tokenData)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// gorilla websocket方式发送消息
|
||||||
|
//res := websocket_api.SendMessageData(clientId, responseData)
|
||||||
|
//if !res {
|
||||||
|
// return false
|
||||||
|
//}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@@ -9,14 +9,16 @@ type RefreshTokenRequest struct {
|
|||||||
|
|
||||||
// PhoneLoginRequest 手机号登录请求
|
// PhoneLoginRequest 手机号登录请求
|
||||||
type PhoneLoginRequest struct {
|
type PhoneLoginRequest struct {
|
||||||
Phone string `json:"phone"`
|
Phone string `json:"phone"`
|
||||||
Captcha string `json:"captcha"`
|
Captcha string `json:"captcha"`
|
||||||
|
AutoLogin bool `json:"auto_login"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccountLoginRequest 账号登录请求
|
// AccountLoginRequest 账号登录请求
|
||||||
type AccountLoginRequest struct {
|
type AccountLoginRequest struct {
|
||||||
Account string `json:"account"`
|
Account string `json:"account"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
|
AutoLogin bool `json:"auto_login"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddUserRequest 新增用户请求
|
// AddUserRequest 新增用户请求
|
||||||
|
@@ -187,7 +187,7 @@ func (UserAPI) AccountLogin(c *gin.Context) {
|
|||||||
} else {
|
} else {
|
||||||
verify := utils.Verify(*user.Password, password)
|
verify := utils.Verify(*user.Password, password)
|
||||||
if verify {
|
if verify {
|
||||||
handelUserLogin(user, c)
|
handelUserLogin(user, accountLoginRequest.AutoLogin, c)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordError"), c)
|
||||||
@@ -204,7 +204,7 @@ func (UserAPI) AccountLogin(c *gin.Context) {
|
|||||||
} else {
|
} else {
|
||||||
verify := utils.Verify(*user.Password, password)
|
verify := utils.Verify(*user.Password, password)
|
||||||
if verify {
|
if verify {
|
||||||
handelUserLogin(user, c)
|
handelUserLogin(user, accountLoginRequest.AutoLogin, c)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordError"), c)
|
||||||
@@ -221,7 +221,7 @@ func (UserAPI) AccountLogin(c *gin.Context) {
|
|||||||
} else {
|
} else {
|
||||||
verify := utils.Verify(*user.Password, password)
|
verify := utils.Verify(*user.Password, password)
|
||||||
if verify {
|
if verify {
|
||||||
handelUserLogin(user, c)
|
handelUserLogin(user, accountLoginRequest.AutoLogin, c)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordError"), c)
|
||||||
@@ -287,7 +287,7 @@ func (UserAPI) PhoneLogin(c *gin.Context) {
|
|||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
handelUserLogin(addUser, c)
|
handelUserLogin(addUser, request.AutoLogin, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -300,7 +300,7 @@ func (UserAPI) PhoneLogin(c *gin.Context) {
|
|||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaError"), c)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
handelUserLogin(user, c)
|
handelUserLogin(user, request.AutoLogin, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,20 +327,20 @@ func (UserAPI) RefreshHandler(c *gin.Context) {
|
|||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
parseRefreshToken, isUpd, err := utils.ParseToken(refreshToken)
|
parseRefreshToken, isUpd, err := utils.ParseRefreshToken(refreshToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorln(err)
|
global.LOG.Errorln(err)
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if isUpd {
|
if isUpd {
|
||||||
accessTokenString, err := utils.GenerateAccessToken(utils.JWTPayload{UserID: parseRefreshToken.UserID, RoleID: parseRefreshToken.RoleID})
|
accessTokenString, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: parseRefreshToken.UserID, RoleID: parseRefreshToken.RoleID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
wrong := redis.Get(constant.UserLoginTokenRedisKey + *parseRefreshToken.UserID).Err()
|
token := redis.Get(constant.UserLoginTokenRedisKey + *parseRefreshToken.UserID).Val()
|
||||||
if wrong != nil {
|
if token == "" {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -360,7 +360,7 @@ func (UserAPI) RefreshHandler(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handelUserLogin 处理用户登录
|
// handelUserLogin 处理用户登录
|
||||||
func handelUserLogin(user model.ScaAuthUser, c *gin.Context) {
|
func handelUserLogin(user model.ScaAuthUser, autoLogin bool, c *gin.Context) {
|
||||||
ids, err := userRoleService.GetUserRoleIdsByUserId(user.ID)
|
ids, err := userRoleService.GetUserRoleIdsByUserId(user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
||||||
@@ -396,15 +396,25 @@ func handelUserLogin(user model.ScaAuthUser, c *gin.Context) {
|
|||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
accessToken, refreshToken, expiresAt := utils.GenerateAccessTokenAndRefreshToken(utils.JWTPayload{UserID: user.UID, RoleID: ids})
|
accessToken, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: user.UID, RoleID: ids})
|
||||||
|
if err != nil {
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var days time.Duration
|
||||||
|
if autoLogin {
|
||||||
|
days = time.Hour * 24 * 7
|
||||||
|
} else {
|
||||||
|
days = time.Hour * 24 * 1
|
||||||
|
}
|
||||||
|
refreshToken, expiresAt := utils.GenerateRefreshToken(utils.RefreshJWTPayload{UserID: user.UID, RoleID: ids}, days)
|
||||||
data := dto.ResponseData{
|
data := dto.ResponseData{
|
||||||
AccessToken: accessToken,
|
AccessToken: accessToken,
|
||||||
RefreshToken: refreshToken,
|
RefreshToken: refreshToken,
|
||||||
ExpiresAt: expiresAt,
|
ExpiresAt: expiresAt,
|
||||||
UID: user.UID,
|
UID: user.UID,
|
||||||
}
|
}
|
||||||
fail := redis.Set(constant.UserLoginTokenRedisKey+*user.UID, data, time.Hour*24*7).Err()
|
fail := redis.Set(constant.UserLoginTokenRedisKey+*user.UID, data, time.Hour*24*1).Err()
|
||||||
if fail != nil {
|
if fail != nil {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
||||||
return
|
return
|
||||||
|
@@ -14,9 +14,11 @@ const (
|
|||||||
HeartbeatWaitTimeout = 10 * time.Second // 心跳等待超时时间
|
HeartbeatWaitTimeout = 10 * time.Second // 心跳等待超时时间
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var Handler = NewWebSocket()
|
||||||
|
|
||||||
func (WebsocketAPI) NewGWSServer(c *gin.Context) {
|
func (WebsocketAPI) NewGWSServer(c *gin.Context) {
|
||||||
var handler = NewWebSocket()
|
|
||||||
upgrader := gws.NewUpgrader(handler, &gws.ServerOption{
|
upgrader := gws.NewUpgrader(Handler, &gws.ServerOption{
|
||||||
HandshakeTimeout: 5 * time.Second, // 握手超时时间
|
HandshakeTimeout: 5 * time.Second, // 握手超时时间
|
||||||
ReadBufferSize: 1024, // 读缓冲区大小
|
ReadBufferSize: 1024, // 读缓冲区大小
|
||||||
ParallelEnabled: true, // 开启并行消息处理
|
ParallelEnabled: true, // 开启并行消息处理
|
||||||
|
@@ -4,6 +4,7 @@ package config
|
|||||||
type OAuth struct {
|
type OAuth struct {
|
||||||
Github Github `yaml:"github"`
|
Github Github `yaml:"github"`
|
||||||
Gitee Gitee `yaml:"gitee"`
|
Gitee Gitee `yaml:"gitee"`
|
||||||
|
QQ QQ `yaml:"qq"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Github and GiteeConfig are the configuration of Github and Gitee OAuth.
|
// Github and GiteeConfig are the configuration of Github and Gitee OAuth.
|
||||||
@@ -19,3 +20,10 @@ type Gitee struct {
|
|||||||
ClientSecret string `yaml:"client-secret"`
|
ClientSecret string `yaml:"client-secret"`
|
||||||
RedirectURI string `yaml:"redirect-uri"`
|
RedirectURI string `yaml:"redirect-uri"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QQ is the configuration of QQ OAuth.
|
||||||
|
type QQ struct {
|
||||||
|
ClientID string `yaml:"client-id"`
|
||||||
|
ClientSecret string `yaml:"client-secret"`
|
||||||
|
RedirectURI string `yaml:"redirect-uri"`
|
||||||
|
}
|
||||||
|
208
docs/docs.go
208
docs/docs.go
@@ -324,6 +324,170 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/oauth/callback_notify": {
|
||||||
|
"post": {
|
||||||
|
"description": "微信回调验证",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "微信回调验证",
|
||||||
|
"responses": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/callback_verify": {
|
||||||
|
"get": {
|
||||||
|
"description": "微信回调验证",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "微信回调验证",
|
||||||
|
"responses": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/generate_client_id": {
|
||||||
|
"get": {
|
||||||
|
"description": "生成客户端ID",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "生成客户端ID",
|
||||||
|
"responses": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/get_temp_qrcode": {
|
||||||
|
"get": {
|
||||||
|
"description": "获取临时二维码",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "获取临时二维码",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "客户端ID",
|
||||||
|
"name": "client_id",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/gitee/callback": {
|
||||||
|
"get": {
|
||||||
|
"description": "处理Gitee回调",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"OAuth"
|
||||||
|
],
|
||||||
|
"summary": "处理Gitee回调",
|
||||||
|
"responses": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/gitee/get_url": {
|
||||||
|
"get": {
|
||||||
|
"description": "获取Gitee登录地址",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"OAuth"
|
||||||
|
],
|
||||||
|
"summary": "获取Gitee登录地址",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "登录地址",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/github/callback": {
|
||||||
|
"get": {
|
||||||
|
"description": "登录回调函数",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"OAuth"
|
||||||
|
],
|
||||||
|
"summary": "登录回调函数",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "code",
|
||||||
|
"name": "code",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "登录成功",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/github/get_url": {
|
||||||
|
"get": {
|
||||||
|
"description": "获取github登录url",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"OAuth"
|
||||||
|
],
|
||||||
|
"summary": "获取github登录url",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "登录url",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/qq/callback": {
|
||||||
|
"get": {
|
||||||
|
"description": "QQ登录回调",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"登录"
|
||||||
|
],
|
||||||
|
"summary": "QQ登录回调",
|
||||||
|
"responses": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/qq/get_url": {
|
||||||
|
"get": {
|
||||||
|
"description": "获取QQ登录地址",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"登录"
|
||||||
|
],
|
||||||
|
"summary": "获取QQ登录地址",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "登录地址",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/sms/ali/send": {
|
"/api/sms/ali/send": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "发送短信验证码",
|
"description": "发送短信验证码",
|
||||||
@@ -500,6 +664,33 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/api/user/reset_password": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"鉴权模块"
|
||||||
|
],
|
||||||
|
"summary": "重置密码",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "用户信息",
|
||||||
|
"name": "user",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.ResetPasswordRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
@@ -527,6 +718,23 @@ const docTemplate = `{
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"dto.ResetPasswordRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"captcha": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"repassword": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
@@ -313,6 +313,170 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/oauth/callback_notify": {
|
||||||
|
"post": {
|
||||||
|
"description": "微信回调验证",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "微信回调验证",
|
||||||
|
"responses": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/callback_verify": {
|
||||||
|
"get": {
|
||||||
|
"description": "微信回调验证",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "微信回调验证",
|
||||||
|
"responses": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/generate_client_id": {
|
||||||
|
"get": {
|
||||||
|
"description": "生成客户端ID",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "生成客户端ID",
|
||||||
|
"responses": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/get_temp_qrcode": {
|
||||||
|
"get": {
|
||||||
|
"description": "获取临时二维码",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "获取临时二维码",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "客户端ID",
|
||||||
|
"name": "client_id",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/gitee/callback": {
|
||||||
|
"get": {
|
||||||
|
"description": "处理Gitee回调",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"OAuth"
|
||||||
|
],
|
||||||
|
"summary": "处理Gitee回调",
|
||||||
|
"responses": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/gitee/get_url": {
|
||||||
|
"get": {
|
||||||
|
"description": "获取Gitee登录地址",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"OAuth"
|
||||||
|
],
|
||||||
|
"summary": "获取Gitee登录地址",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "登录地址",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/github/callback": {
|
||||||
|
"get": {
|
||||||
|
"description": "登录回调函数",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"OAuth"
|
||||||
|
],
|
||||||
|
"summary": "登录回调函数",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "code",
|
||||||
|
"name": "code",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "登录成功",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/github/get_url": {
|
||||||
|
"get": {
|
||||||
|
"description": "获取github登录url",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"OAuth"
|
||||||
|
],
|
||||||
|
"summary": "获取github登录url",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "登录url",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/qq/callback": {
|
||||||
|
"get": {
|
||||||
|
"description": "QQ登录回调",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"登录"
|
||||||
|
],
|
||||||
|
"summary": "QQ登录回调",
|
||||||
|
"responses": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/oauth/qq/get_url": {
|
||||||
|
"get": {
|
||||||
|
"description": "获取QQ登录地址",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"登录"
|
||||||
|
],
|
||||||
|
"summary": "获取QQ登录地址",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "登录地址",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/sms/ali/send": {
|
"/api/sms/ali/send": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "发送短信验证码",
|
"description": "发送短信验证码",
|
||||||
@@ -489,6 +653,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/api/user/reset_password": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"鉴权模块"
|
||||||
|
],
|
||||||
|
"summary": "重置密码",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "用户信息",
|
||||||
|
"name": "user",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.ResetPasswordRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
@@ -516,6 +707,23 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"dto.ResetPasswordRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"captcha": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"repassword": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -15,6 +15,17 @@ definitions:
|
|||||||
username:
|
username:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
dto.ResetPasswordRequest:
|
||||||
|
properties:
|
||||||
|
captcha:
|
||||||
|
type: string
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
phone:
|
||||||
|
type: string
|
||||||
|
repassword:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
info:
|
info:
|
||||||
contact: {}
|
contact: {}
|
||||||
paths:
|
paths:
|
||||||
@@ -219,6 +230,116 @@ paths:
|
|||||||
summary: 生成基础文字验证码
|
summary: 生成基础文字验证码
|
||||||
tags:
|
tags:
|
||||||
- 基础文字验证码
|
- 基础文字验证码
|
||||||
|
/api/oauth/callback_notify:
|
||||||
|
post:
|
||||||
|
description: 微信回调验证
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses: {}
|
||||||
|
summary: 微信回调验证
|
||||||
|
/api/oauth/callback_verify:
|
||||||
|
get:
|
||||||
|
description: 微信回调验证
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses: {}
|
||||||
|
summary: 微信回调验证
|
||||||
|
/api/oauth/generate_client_id:
|
||||||
|
get:
|
||||||
|
description: 生成客户端ID
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses: {}
|
||||||
|
summary: 生成客户端ID
|
||||||
|
/api/oauth/get_temp_qrcode:
|
||||||
|
get:
|
||||||
|
description: 获取临时二维码
|
||||||
|
parameters:
|
||||||
|
- description: 客户端ID
|
||||||
|
in: query
|
||||||
|
name: client_id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses: {}
|
||||||
|
summary: 获取临时二维码
|
||||||
|
/api/oauth/gitee/callback:
|
||||||
|
get:
|
||||||
|
description: 处理Gitee回调
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses: {}
|
||||||
|
summary: 处理Gitee回调
|
||||||
|
tags:
|
||||||
|
- OAuth
|
||||||
|
/api/oauth/gitee/get_url:
|
||||||
|
get:
|
||||||
|
description: 获取Gitee登录地址
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: 登录地址
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
summary: 获取Gitee登录地址
|
||||||
|
tags:
|
||||||
|
- OAuth
|
||||||
|
/api/oauth/github/callback:
|
||||||
|
get:
|
||||||
|
description: 登录回调函数
|
||||||
|
parameters:
|
||||||
|
- description: code
|
||||||
|
in: query
|
||||||
|
name: code
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: 登录成功
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
summary: 登录回调函数
|
||||||
|
tags:
|
||||||
|
- OAuth
|
||||||
|
/api/oauth/github/get_url:
|
||||||
|
get:
|
||||||
|
description: 获取github登录url
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: 登录url
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
summary: 获取github登录url
|
||||||
|
tags:
|
||||||
|
- OAuth
|
||||||
|
/api/oauth/qq/callback:
|
||||||
|
get:
|
||||||
|
description: QQ登录回调
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses: {}
|
||||||
|
summary: QQ登录回调
|
||||||
|
tags:
|
||||||
|
- 登录
|
||||||
|
/api/oauth/qq/get_url:
|
||||||
|
get:
|
||||||
|
description: 获取QQ登录地址
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: 登录地址
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
summary: 获取QQ登录地址
|
||||||
|
tags:
|
||||||
|
- 登录
|
||||||
/api/sms/ali/send:
|
/api/sms/ali/send:
|
||||||
get:
|
get:
|
||||||
description: 发送短信验证码
|
description: 发送短信验证码
|
||||||
@@ -335,4 +456,21 @@ paths:
|
|||||||
summary: 手机号登录/注册
|
summary: 手机号登录/注册
|
||||||
tags:
|
tags:
|
||||||
- 鉴权模块
|
- 鉴权模块
|
||||||
|
/api/user/reset_password:
|
||||||
|
post:
|
||||||
|
parameters:
|
||||||
|
- description: 用户信息
|
||||||
|
in: body
|
||||||
|
name: user
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.ResetPasswordRequest'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
summary: 重置密码
|
||||||
|
tags:
|
||||||
|
- 鉴权模块
|
||||||
swagger: "2.0"
|
swagger: "2.0"
|
||||||
|
@@ -26,7 +26,7 @@ func JWTAuthMiddleware() gin.HandlerFunc {
|
|||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "AuthVerifyFailed"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "AuthVerifyFailed"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
parseToken, isUpd, err := utils.ParseToken(accessToken)
|
parseToken, isUpd, err := utils.ParseAccessToken(accessToken)
|
||||||
if err != nil || !isUpd {
|
if err != nil || !isUpd {
|
||||||
c.Abort()
|
c.Abort()
|
||||||
result.FailWithCodeAndMessage(401, ginI18n.MustGetMessage(c, "AuthVerifyExpired"), c)
|
result.FailWithCodeAndMessage(401, ginI18n.MustGetMessage(c, "AuthVerifyExpired"), c)
|
||||||
|
@@ -18,7 +18,7 @@ type ScaAuthUserDevice struct {
|
|||||||
CreatedTime *time.Time `gorm:"column:created_time;type:datetime;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_time"` // 创建时间
|
CreatedTime *time.Time `gorm:"column:created_time;type:datetime;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_time"` // 创建时间
|
||||||
UpdateBy *string `gorm:"column:update_by;type:varchar(32);comment:更新人" json:"update_by"` // 更新人
|
UpdateBy *string `gorm:"column:update_by;type:varchar(32);comment:更新人" json:"update_by"` // 更新人
|
||||||
UpdateTime *time.Time `gorm:"column:update_time;type:datetime;default:CURRENT_TIMESTAMP;comment:更新时间" json:"update_time"` // 更新时间
|
UpdateTime *time.Time `gorm:"column:update_time;type:datetime;default:CURRENT_TIMESTAMP;comment:更新时间" json:"update_time"` // 更新时间
|
||||||
Deleted *int64 `gorm:"column:deleted;type:int(11);comment:是否删除" json:"deleted"` // 是否删除
|
Deleted *int64 `gorm:"column:deleted;type:int(11);default:0;comment:是否删除" json:"deleted"` // 是否删除
|
||||||
Browser *string `gorm:"column:browser;type:varchar(255);comment:浏览器" json:"browser"` // 浏览器
|
Browser *string `gorm:"column:browser;type:varchar(255);comment:浏览器" json:"browser"` // 浏览器
|
||||||
OperatingSystem *string `gorm:"column:operating_system;type:varchar(255);comment:操作系统" json:"operating_system"` // 操作系统
|
OperatingSystem *string `gorm:"column:operating_system;type:varchar(255);comment:操作系统" json:"operating_system"` // 操作系统
|
||||||
BrowserVersion *string `gorm:"column:browser_version;type:varchar(255);comment:浏览器版本" json:"browser_version"` // 浏览器版本
|
BrowserVersion *string `gorm:"column:browser_version;type:varchar(255);comment:浏览器版本" json:"browser_version"` // 浏览器版本
|
||||||
|
@@ -10,11 +10,13 @@ var oauth = api.Api.OAuthApi
|
|||||||
func OauthRouter(router *gin.RouterGroup) {
|
func OauthRouter(router *gin.RouterGroup) {
|
||||||
group := router.Group("/oauth")
|
group := router.Group("/oauth")
|
||||||
{
|
{
|
||||||
group.GET("/generate_client_id", oauth.GenerateClientId)
|
wechatRouter := group.Group("/wechat")
|
||||||
group.GET("/get_temp_qrcode", oauth.GetTempQrCode)
|
{
|
||||||
//group.GET("/callback", oauth.CallbackVerify)
|
wechatRouter.GET("/generate_client_id", oauth.GenerateClientId)
|
||||||
group.POST("/callback", oauth.CallbackNotify)
|
wechatRouter.GET("/get_temp_qrcode", oauth.GetTempQrCode)
|
||||||
|
//wechatRouter.GET("/callback", oauth.CallbackVerify)
|
||||||
|
wechatRouter.POST("/callback", oauth.CallbackNotify)
|
||||||
|
}
|
||||||
githubRouter := group.Group("/github")
|
githubRouter := group.Group("/github")
|
||||||
{
|
{
|
||||||
githubRouter.GET("/get_url", oauth.GetRedirectUrl)
|
githubRouter.GET("/get_url", oauth.GetRedirectUrl)
|
||||||
@@ -25,6 +27,11 @@ func OauthRouter(router *gin.RouterGroup) {
|
|||||||
giteeRouter.GET("/get_url", oauth.GetGiteeRedirectUrl)
|
giteeRouter.GET("/get_url", oauth.GetGiteeRedirectUrl)
|
||||||
giteeRouter.GET("/callback", oauth.GiteeCallback)
|
giteeRouter.GET("/callback", oauth.GiteeCallback)
|
||||||
}
|
}
|
||||||
|
qqRouter := group.Group("/qq")
|
||||||
|
{
|
||||||
|
qqRouter.GET("/get_url", oauth.GetQQRedirectUrl)
|
||||||
|
qqRouter.GET("/callback", oauth.QQCallback)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
78
utils/jwt.go
78
utils/jwt.go
@@ -8,23 +8,29 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type JWTPayload struct {
|
type RefreshJWTPayload struct {
|
||||||
UserID *string `json:"user_id"`
|
UserID *string `json:"user_id"`
|
||||||
RoleID []*int64 `json:"role_id"`
|
RoleID []*int64 `json:"role_id"`
|
||||||
|
Type *string `json:"type" default:"refresh"`
|
||||||
|
}
|
||||||
|
type AccessJWTPayload struct {
|
||||||
|
UserID *string `json:"user_id"`
|
||||||
|
RoleID []*int64 `json:"role_id"`
|
||||||
|
Type *string `json:"type" default:"access"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type JWTClaims struct {
|
type JWTClaims struct {
|
||||||
JWTPayload
|
AccessJWTPayload
|
||||||
|
RefreshJWTPayload
|
||||||
jwt.RegisteredClaims
|
jwt.RegisteredClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
var MySecret []byte
|
var MySecret []byte
|
||||||
|
|
||||||
// GenerateAccessToken generates a JWT token with the given payload
|
// GenerateAccessToken generates a JWT token with the given payload
|
||||||
func GenerateAccessToken(payload JWTPayload) (string, error) {
|
func GenerateAccessToken(payload AccessJWTPayload) (string, error) {
|
||||||
MySecret = []byte(global.CONFIG.JWT.Secret)
|
MySecret = []byte(global.CONFIG.JWT.Secret)
|
||||||
claims := JWTClaims{
|
claims := JWTClaims{
|
||||||
JWTPayload: payload,
|
AccessJWTPayload: payload,
|
||||||
RegisteredClaims: jwt.RegisteredClaims{
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 2)),
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 2)),
|
||||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
@@ -44,55 +50,34 @@ func GenerateAccessToken(payload JWTPayload) (string, error) {
|
|||||||
return accessToken, nil
|
return accessToken, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateAccessTokenAndRefreshToken generates a JWT token with the given payload, and returns the accessToken and refreshToken
|
// GenerateRefreshToken generates a JWT token with the given payload, and returns the accessToken and refreshToken
|
||||||
func GenerateAccessTokenAndRefreshToken(payload JWTPayload) (string, string, int64) {
|
func GenerateRefreshToken(payload RefreshJWTPayload, days time.Duration) (string, int64) {
|
||||||
MySecret = []byte(global.CONFIG.JWT.Secret)
|
MySecret = []byte(global.CONFIG.JWT.Secret)
|
||||||
// accessToken 的数据
|
|
||||||
accessClaims := JWTClaims{
|
|
||||||
JWTPayload: payload,
|
|
||||||
RegisteredClaims: jwt.RegisteredClaims{
|
|
||||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 2)),
|
|
||||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
|
||||||
NotBefore: jwt.NewNumericDate(time.Now()),
|
|
||||||
Issuer: global.CONFIG.JWT.Issuer,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
refreshClaims := JWTClaims{
|
refreshClaims := JWTClaims{
|
||||||
JWTPayload: payload,
|
RefreshJWTPayload: payload,
|
||||||
RegisteredClaims: jwt.RegisteredClaims{
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24 * 7)), // 7天
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(days)), // 7天
|
||||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
NotBefore: jwt.NewNumericDate(time.Now()),
|
NotBefore: jwt.NewNumericDate(time.Now()),
|
||||||
Issuer: global.CONFIG.JWT.Issuer,
|
Issuer: global.CONFIG.JWT.Issuer,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, accessClaims)
|
|
||||||
refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, refreshClaims)
|
refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, refreshClaims)
|
||||||
accessTokenString, err := accessToken.SignedString(MySecret)
|
|
||||||
if err != nil {
|
|
||||||
global.LOG.Error(err)
|
|
||||||
return "", "", 0
|
|
||||||
}
|
|
||||||
refreshTokenString, err := refreshToken.SignedString(MySecret)
|
refreshTokenString, err := refreshToken.SignedString(MySecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Error(err)
|
global.LOG.Error(err)
|
||||||
return "", "", 0
|
return "", 0
|
||||||
}
|
|
||||||
accessTokenEncrypted, err := aes.AesCtrEncryptHex([]byte(accessTokenString), []byte(global.CONFIG.Encrypt.Key), []byte(global.CONFIG.Encrypt.IV))
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return "", "", 0
|
|
||||||
}
|
}
|
||||||
refreshTokenEncrypted, err := aes.AesCtrEncryptHex([]byte(refreshTokenString), []byte(global.CONFIG.Encrypt.Key), []byte(global.CONFIG.Encrypt.IV))
|
refreshTokenEncrypted, err := aes.AesCtrEncryptHex([]byte(refreshTokenString), []byte(global.CONFIG.Encrypt.Key), []byte(global.CONFIG.Encrypt.IV))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return "", "", 0
|
return "", 0
|
||||||
}
|
}
|
||||||
return accessTokenEncrypted, refreshTokenEncrypted, refreshClaims.ExpiresAt.Time.Unix()
|
return refreshTokenEncrypted, refreshClaims.ExpiresAt.Time.Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseToken parses a JWT token and returns the payload
|
// ParseAccessToken parses a JWT token and returns the payload
|
||||||
func ParseToken(tokenString string) (*JWTPayload, bool, error) {
|
func ParseAccessToken(tokenString string) (*AccessJWTPayload, bool, error) {
|
||||||
MySecret = []byte(global.CONFIG.JWT.Secret)
|
MySecret = []byte(global.CONFIG.JWT.Secret)
|
||||||
plaintext, err := aes.AesCtrDecryptByHex(tokenString, []byte(global.CONFIG.Encrypt.Key), []byte(global.CONFIG.Encrypt.IV))
|
plaintext, err := aes.AesCtrDecryptByHex(tokenString, []byte(global.CONFIG.Encrypt.Key), []byte(global.CONFIG.Encrypt.IV))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -107,7 +92,28 @@ func ParseToken(tokenString string) (*JWTPayload, bool, error) {
|
|||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
if claims, ok := token.Claims.(*JWTClaims); ok && token.Valid {
|
if claims, ok := token.Claims.(*JWTClaims); ok && token.Valid {
|
||||||
return &claims.JWTPayload, true, nil
|
return &claims.AccessJWTPayload, true, nil
|
||||||
|
}
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseRefreshToken parses a JWT token and returns the payload
|
||||||
|
func ParseRefreshToken(tokenString string) (*RefreshJWTPayload, bool, error) {
|
||||||
|
MySecret = []byte(global.CONFIG.JWT.Secret)
|
||||||
|
plaintext, err := aes.AesCtrDecryptByHex(tokenString, []byte(global.CONFIG.Encrypt.Key), []byte(global.CONFIG.Encrypt.IV))
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Error(err)
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
token, err := jwt.ParseWithClaims(string(plaintext), &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
return MySecret, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Error(err)
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
if claims, ok := token.Claims.(*JWTClaims); ok && token.Valid {
|
||||||
|
return &claims.RefreshJWTPayload, true, nil
|
||||||
}
|
}
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user