♻️ refactored login-related code
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
package constant
|
||||
|
||||
const SESSION_KEY = "SESSION"
|
3
app/core/api/common/constant/uid_key.go
Normal file
3
app/core/api/common/constant/uid_key.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package constant
|
||||
|
||||
const UID_HEADER_KEY = "X-UID"
|
@@ -19,7 +19,7 @@ func GenerateAccessToken(secret string, payload AccessJWTPayload) string {
|
||||
claims := AccessJWTClaims{
|
||||
AccessJWTPayload: payload,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 30)),
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 15)),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
NotBefore: jwt.NewNumericDate(time.Now()),
|
||||
},
|
||||
|
48
app/core/api/common/jwt/websocket_token.go
Normal file
48
app/core/api/common/jwt/websocket_token.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
type WebsocketJWTPayload struct {
|
||||
UserID string `json:"user_id"`
|
||||
Type string `json:"type"`
|
||||
Expr string `json:"expr"`
|
||||
}
|
||||
type WebsocketJWTClaims struct {
|
||||
AccessJWTPayload
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
func GenerateWebsocketToken(secret string, payload AccessJWTPayload) string {
|
||||
claims := AccessJWTClaims{
|
||||
AccessJWTPayload: payload,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 15)),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
NotBefore: jwt.NewNumericDate(time.Now()),
|
||||
},
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
accessToken, err := token.SignedString([]byte(secret))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return accessToken
|
||||
}
|
||||
|
||||
// ParseWebsocketToken parses a JWT token and returns the payload
|
||||
func ParseWebsocketToken(secret string, tokenString string) (*AccessJWTPayload, bool) {
|
||||
token, err := jwt.ParseWithClaims(tokenString, &AccessJWTClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(secret), nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
if claims, ok := token.Claims.(*AccessJWTClaims); ok && token.Valid {
|
||||
return &claims.AccessJWTPayload, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
@@ -7,7 +7,7 @@ func CORSMiddleware() func(http.Header) {
|
||||
header.Set("Access-Control-Allow-Origin", "*")
|
||||
header.Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH")
|
||||
header.Set("Access-Control-Expose-Headers", "Content-Length, Content-Type")
|
||||
header.Set("Access-Control-Allow-Headers", "Content-Type,Authorization,Accept-Language,Origin,X-Content-Security")
|
||||
header.Set("Access-Control-Allow-Headers", "Content-Type,Authorization,Accept-Language,Origin,X-Content-Security,X-UID")
|
||||
header.Set("Access-Control-Allow-Credentials", "true")
|
||||
}
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
func UnauthorizedCallbackMiddleware() func(w http.ResponseWriter, r *http.Request, err error) {
|
||||
return func(w http.ResponseWriter, r *http.Request, err error) {
|
||||
// httpx.WriteJson(w, http.StatusUnauthorized, response.ErrorWithCodeMessage(http.StatusUnauthorized, "Unauthorized"))
|
||||
httpx.OkJsonCtx(r.Context(), w, response.ErrorWithCodeMessage(http.StatusUnauthorized, "Unauthorized"))
|
||||
httpx.OkJsonCtx(r.Context(), w, response.ErrorWithCodeMessage(http.StatusUnauthorized, err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@@ -31,8 +31,9 @@ type (
|
||||
Password string `json:"password"`
|
||||
Repassword string `json:"repassword"`
|
||||
}
|
||||
UserDeviceRequest {
|
||||
AccessToken string `json:"access_token"`
|
||||
WechatOffiaccountLoginRequest {
|
||||
Openid string `json:"openid"`
|
||||
ClientId string `json:"client_id"`
|
||||
}
|
||||
// 登录响应参数
|
||||
LoginResponse {
|
||||
@@ -170,8 +171,13 @@ service core {
|
||||
@handler resetPassword
|
||||
post /reset/password (ResetPasswordRequest) returns (Response)
|
||||
|
||||
@handler getUserDevice
|
||||
post /device (UserDeviceRequest) returns (Response)
|
||||
// 微信公众号登录
|
||||
@handler wechatOffiaccountLogin
|
||||
post /wechat/offiaccount/login (WechatOffiaccountLoginRequest) returns (Response)
|
||||
|
||||
// 获取微信公众号二维码
|
||||
@handler getWechatOffiaccountQrcode
|
||||
post /wechat/offiaccount/qrcode (OAuthWechatRequest) returns (Response)
|
||||
}
|
||||
|
||||
@server (
|
||||
@@ -249,11 +255,12 @@ service core {
|
||||
@handler qqCallback
|
||||
get /qq/callback (OAuthCallbackRequest) returns (string)
|
||||
|
||||
@handler wechatCallback
|
||||
get /wechat/callback
|
||||
@handler wechatOffiaccountCallback
|
||||
post /wechat/offiaccount/callback
|
||||
|
||||
@handler getWechatQrcode
|
||||
get /wechat/qrcode (OAuthWechatRequest) returns (Response)
|
||||
// important!
|
||||
@handler wechatOffiaccountCallbackVerify
|
||||
get /wechat/offiaccount/callback
|
||||
}
|
||||
|
||||
@server (
|
||||
@@ -301,7 +308,7 @@ service core {
|
||||
timeout: 10s // 超时时间
|
||||
maxBytes: 1048576 // 最大请求大小
|
||||
signature: false // 是否开启签名验证
|
||||
middleware: SecurityHeadersMiddleware,CasbinVerifyMiddleware // 注册中间件
|
||||
middleware: SecurityHeadersMiddleware,CasbinVerifyMiddleware,AuthorizationMiddleware // 注册中间件
|
||||
MaxConns: true // 是否开启最大连接数限制
|
||||
Recover: true // 是否开启自动恢复
|
||||
jwt: Auth // 是否开启jwt验证
|
||||
|
@@ -1,20 +1,20 @@
|
||||
package oauth
|
||||
|
||||
import (
|
||||
"github.com/ArtisanCloud/PowerLibs/v3/http/helper"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/response"
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/logic/oauth"
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
|
||||
)
|
||||
|
||||
func WechatCallbackHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
func WechatOffiaccountCallbackHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := oauth.NewWechatCallbackLogic(r.Context(), svcCtx)
|
||||
err := l.WechatCallback(w, r)
|
||||
l := oauth.NewWechatOffiaccountCallbackLogic(r.Context(), svcCtx)
|
||||
res, err := l.WechatOffiaccountCallback(r)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
httpx.WriteJsonCtx(
|
||||
@@ -23,7 +23,7 @@ func WechatCallbackHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
http.StatusInternalServerError,
|
||||
response.ErrorWithI18n(r.Context(), "system.error"))
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
_ = helper.HttpResponseSend(res, w)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package oauth
|
||||
|
||||
import (
|
||||
"github.com/ArtisanCloud/PowerLibs/v3/http/helper"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/response"
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/logic/oauth"
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
|
||||
)
|
||||
|
||||
func WechatOffiaccountCallbackVerifyHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := oauth.NewWechatOffiaccountCallbackVerifyLogic(r.Context(), svcCtx)
|
||||
res, err := l.WechatOffiaccountCallbackVerify(r)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
httpx.WriteJsonCtx(
|
||||
r.Context(),
|
||||
w,
|
||||
http.StatusInternalServerError,
|
||||
response.ErrorWithI18n(r.Context(), "system.error"))
|
||||
} else {
|
||||
_ = helper.HttpResponseSend(res, w)
|
||||
}
|
||||
}
|
||||
}
|
@@ -61,7 +61,7 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
|
||||
server.AddRoutes(
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware},
|
||||
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.AuthorizationMiddleware},
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
@@ -141,14 +141,14 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
Handler: oauth.GetQqOauthUrlHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/wechat/callback",
|
||||
Handler: oauth.WechatCallbackHandler(serverCtx),
|
||||
Method: http.MethodPost,
|
||||
Path: "/wechat/offiaccount/callback",
|
||||
Handler: oauth.WechatOffiaccountCallbackHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/wechat/qrcode",
|
||||
Handler: oauth.GetWechatQrcodeHandler(serverCtx),
|
||||
Path: "/wechat/offiaccount/callback",
|
||||
Handler: oauth.WechatOffiaccountCallbackVerifyHandler(serverCtx),
|
||||
},
|
||||
}...,
|
||||
),
|
||||
@@ -220,11 +220,6 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware},
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/device",
|
||||
Handler: user.GetUserDeviceHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/login",
|
||||
@@ -240,6 +235,16 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
Path: "/reset/password",
|
||||
Handler: user.ResetPasswordHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/wechat/offiaccount/login",
|
||||
Handler: user.WechatOffiaccountLoginHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/wechat/offiaccount/qrcode",
|
||||
Handler: user.GetWechatOffiaccountQrcodeHandler(serverCtx),
|
||||
},
|
||||
}...,
|
||||
),
|
||||
rest.WithSignature(serverCtx.Config.Signature),
|
||||
|
@@ -1,18 +1,17 @@
|
||||
package oauth
|
||||
package user
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"net/http"
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/logic/user"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/response"
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/logic/oauth"
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/types"
|
||||
)
|
||||
|
||||
func GetWechatQrcodeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
func GetWechatOffiaccountQrcodeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.OAuthWechatRequest
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
@@ -20,8 +19,8 @@ func GetWechatQrcodeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
l := oauth.NewGetWechatQrcodeLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetWechatQrcode(r, &req)
|
||||
l := user.NewGetWechatOffiaccountQrcodeLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetWechatOffiaccountQrcode(r, &req)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
httpx.WriteJsonCtx(
|
@@ -11,16 +11,16 @@ import (
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/types"
|
||||
)
|
||||
|
||||
func GetUserDeviceHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
func WechatOffiaccountLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.UserDeviceRequest
|
||||
var req types.WechatOffiaccountLoginRequest
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := user.NewGetUserDeviceLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetUserDevice(r, w, &req)
|
||||
l := user.NewWechatOffiaccountLoginLogic(r.Context(), svcCtx)
|
||||
resp, err := l.WechatOffiaccountLogin(r, &req)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
httpx.WriteJsonCtx(
|
@@ -149,7 +149,7 @@ func (l *GiteeCallbackLogic) GiteeCallback(w http.ResponseWriter, r *http.Reques
|
||||
_ = tx.Rollback()
|
||||
return "", err
|
||||
}
|
||||
data, err := HandleOauthLoginResponse(addUser, l.svcCtx, r, w, l.ctx)
|
||||
data, err := HandleOauthLoginResponse(addUser, l.svcCtx, r, l.ctx)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return "", err
|
||||
@@ -167,7 +167,7 @@ func (l *GiteeCallbackLogic) GiteeCallback(w http.ResponseWriter, r *http.Reques
|
||||
return "", err
|
||||
}
|
||||
|
||||
data, err := HandleOauthLoginResponse(authUserInfo, l.svcCtx, r, w, l.ctx)
|
||||
data, err := HandleOauthLoginResponse(authUserInfo, l.svcCtx, r, l.ctx)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return "", err
|
||||
@@ -180,13 +180,12 @@ func (l *GiteeCallbackLogic) GiteeCallback(w http.ResponseWriter, r *http.Reques
|
||||
}
|
||||
|
||||
// HandleOauthLoginResponse 处理登录响应
|
||||
func HandleOauthLoginResponse(scaAuthUser *model.ScaAuthUser, svcCtx *svc.ServiceContext, r *http.Request, w http.ResponseWriter, ctx context.Context) (string, error) {
|
||||
data, err := user.HandleUserLogin(scaAuthUser, svcCtx, true, r, w, ctx)
|
||||
func HandleOauthLoginResponse(scaAuthUser *model.ScaAuthUser, svcCtx *svc.ServiceContext, r *http.Request, ctx context.Context) (string, error) {
|
||||
data, err := user.HandleLoginJWT(scaAuthUser, svcCtx, true, r, ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
responseData := response.SuccessWithData(data)
|
||||
marshalData, err := json.Marshal(responseData)
|
||||
marshalData, err := json.Marshal(response.SuccessWithData(data))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@@ -149,7 +149,7 @@ func (l *GithubCallbackLogic) GithubCallback(w http.ResponseWriter, r *http.Requ
|
||||
return "", err
|
||||
}
|
||||
|
||||
data, err := HandleOauthLoginResponse(addUser, l.svcCtx, r, w, l.ctx)
|
||||
data, err := HandleOauthLoginResponse(addUser, l.svcCtx, r, l.ctx)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return "", err
|
||||
@@ -167,7 +167,7 @@ func (l *GithubCallbackLogic) GithubCallback(w http.ResponseWriter, r *http.Requ
|
||||
return "", err
|
||||
}
|
||||
|
||||
data, err := HandleOauthLoginResponse(authUserInfo, l.svcCtx, r, w, l.ctx)
|
||||
data, err := HandleOauthLoginResponse(authUserInfo, l.svcCtx, r, l.ctx)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return "", err
|
||||
|
@@ -149,7 +149,7 @@ func (l *QqCallbackLogic) QqCallback(w http.ResponseWriter, r *http.Request, req
|
||||
return "", err
|
||||
}
|
||||
|
||||
data, err := HandleOauthLoginResponse(addUser, l.svcCtx, r, w, l.ctx)
|
||||
data, err := HandleOauthLoginResponse(addUser, l.svcCtx, r, l.ctx)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return "", err
|
||||
@@ -167,7 +167,7 @@ func (l *QqCallbackLogic) QqCallback(w http.ResponseWriter, r *http.Request, req
|
||||
return "", err
|
||||
}
|
||||
|
||||
data, err := HandleOauthLoginResponse(authUserInfo, l.svcCtx, r, w, l.ctx)
|
||||
data, err := HandleOauthLoginResponse(authUserInfo, l.svcCtx, r, l.ctx)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return "", err
|
||||
|
@@ -1,208 +0,0 @@
|
||||
package oauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ArtisanCloud/PowerLibs/v3/http/helper"
|
||||
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/contract"
|
||||
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/messages"
|
||||
models2 "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/models"
|
||||
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount/server/handlers/models"
|
||||
"github.com/yitter/idgenerator-go/idgen"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/constant"
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/i18n"
|
||||
randomname "schisandra-album-cloud-microservices/app/core/api/common/random_name"
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/utils"
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/logic/user"
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/logic/websocket"
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
|
||||
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/model"
|
||||
)
|
||||
|
||||
type WechatCallbackLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewWechatCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WechatCallbackLogic {
|
||||
return &WechatCallbackLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *WechatCallbackLogic) WechatCallback(w http.ResponseWriter, r *http.Request) error {
|
||||
_, err := l.svcCtx.WechatPublic.Server.VerifyURL(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rs, err := l.svcCtx.WechatPublic.Server.Notify(r, func(event contract.EventInterface) interface{} {
|
||||
switch event.GetMsgType() {
|
||||
case models2.CALLBACK_MSG_TYPE_EVENT:
|
||||
switch event.GetEvent() {
|
||||
case models.CALLBACK_EVENT_SUBSCRIBE:
|
||||
msg := models.EventSubscribe{}
|
||||
err = event.ReadMessage(&msg)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return err
|
||||
}
|
||||
key := strings.TrimPrefix(msg.EventKey, "qrscene_")
|
||||
err = l.HandlerWechatLogin(msg.FromUserName, key, w, r)
|
||||
if err != nil {
|
||||
return messages.NewText(i18n.FormatText(l.ctx, "login.loginFailed"))
|
||||
}
|
||||
return messages.NewText(i18n.FormatText(l.ctx, "login.loginSuccess"))
|
||||
|
||||
case models.CALLBACK_EVENT_UNSUBSCRIBE:
|
||||
msg := models.EventUnSubscribe{}
|
||||
err = event.ReadMessage(&msg)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return err
|
||||
}
|
||||
return messages.NewText("ok")
|
||||
|
||||
case models.CALLBACK_EVENT_SCAN:
|
||||
msg := models.EventScan{}
|
||||
err = event.ReadMessage(&msg)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return err
|
||||
}
|
||||
err = l.HandlerWechatLogin(msg.FromUserName, msg.EventKey, w, r)
|
||||
if err != nil {
|
||||
return messages.NewText(i18n.FormatText(l.ctx, "login.loginFailed"))
|
||||
}
|
||||
return messages.NewText(i18n.FormatText(l.ctx, "login.loginSuccess"))
|
||||
|
||||
}
|
||||
|
||||
case models2.CALLBACK_MSG_TYPE_TEXT:
|
||||
msg := models.MessageText{}
|
||||
err = event.ReadMessage(&msg)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
return messages.NewText("ok")
|
||||
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = helper.HttpResponseSend(rs, w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandlerWechatLogin 处理微信登录
|
||||
func (l *WechatCallbackLogic) HandlerWechatLogin(openId string, clientId string, w http.ResponseWriter, r *http.Request) error {
|
||||
if openId == "" {
|
||||
return errors.New("openId is empty")
|
||||
}
|
||||
tx := l.svcCtx.DB.Begin()
|
||||
|
||||
userSocial := l.svcCtx.DB.ScaAuthUserSocial
|
||||
socialUser, err := tx.ScaAuthUserSocial.Where(userSocial.OpenID.Eq(openId), userSocial.Source.Eq(constant.OAuthSourceWechat)).First()
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
|
||||
if socialUser == nil {
|
||||
// 创建用户
|
||||
uid := idgen.NextId()
|
||||
uidStr := strconv.FormatInt(uid, 10)
|
||||
avatar := utils.GenerateAvatar(uidStr)
|
||||
name := randomname.GenerateName()
|
||||
|
||||
male := constant.Male
|
||||
addUser := &model.ScaAuthUser{
|
||||
UID: uidStr,
|
||||
Avatar: avatar,
|
||||
Username: openId,
|
||||
Nickname: name,
|
||||
Gender: male,
|
||||
}
|
||||
err = tx.ScaAuthUser.Create(addUser)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
wechatUser := constant.OAuthSourceWechat
|
||||
newSocialUser := &model.ScaAuthUserSocial{
|
||||
UserID: uidStr,
|
||||
OpenID: openId,
|
||||
Source: wechatUser,
|
||||
}
|
||||
err = tx.ScaAuthUserSocial.Create(newSocialUser)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
if res, err := l.svcCtx.CasbinEnforcer.AddRoleForUser(uidStr, constant.User); !res || err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := user.HandleUserLogin(addUser, l.svcCtx, true, r, w, l.ctx)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
marshal, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
err = websocket.QrcodeWebSocketHandler.SendMessageToClient(clientId, marshal)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
authUser := l.svcCtx.DB.ScaAuthUser
|
||||
|
||||
authUserInfo, err := tx.ScaAuthUser.Where(authUser.UID.Eq(socialUser.UserID)).First()
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := user.HandleUserLogin(authUserInfo, l.svcCtx, true, r, w, l.ctx)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
marshal, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
err = websocket.QrcodeWebSocketHandler.SendMessageToClient(clientId, marshal)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -0,0 +1,112 @@
|
||||
package oauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/contract"
|
||||
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/messages"
|
||||
models2 "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/models"
|
||||
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount/server/handlers/models"
|
||||
"net/http"
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/i18n"
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/response"
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/logic/websocket"
|
||||
"strings"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
|
||||
)
|
||||
|
||||
type WechatOffiaccountCallbackLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
type MessageData struct {
|
||||
Openid string `json:"openid"`
|
||||
ClientId string `json:"client_id"`
|
||||
}
|
||||
|
||||
func NewWechatOffiaccountCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WechatOffiaccountCallbackLogic {
|
||||
return &WechatOffiaccountCallbackLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *WechatOffiaccountCallbackLogic) WechatOffiaccountCallback(r *http.Request) (*http.Response, error) {
|
||||
rs, err := l.svcCtx.WechatOfficial.Server.Notify(r, func(event contract.EventInterface) interface{} {
|
||||
switch event.GetMsgType() {
|
||||
case models2.CALLBACK_MSG_TYPE_EVENT:
|
||||
switch event.GetEvent() {
|
||||
case models.CALLBACK_EVENT_SUBSCRIBE:
|
||||
msg := models.EventSubscribe{}
|
||||
err := event.ReadMessage(&msg)
|
||||
if err != nil {
|
||||
logx.Error(err.Error())
|
||||
return "error"
|
||||
}
|
||||
key := strings.TrimPrefix(msg.EventKey, "qrscene_")
|
||||
err = l.SendMessage(msg.FromUserName, key)
|
||||
if err != nil {
|
||||
return messages.NewText(i18n.FormatText(l.ctx, "login.loginFailed"))
|
||||
}
|
||||
return messages.NewText(i18n.FormatText(l.ctx, "login.loginSuccess"))
|
||||
|
||||
case models.CALLBACK_EVENT_UNSUBSCRIBE:
|
||||
msg := models.EventUnSubscribe{}
|
||||
err := event.ReadMessage(&msg)
|
||||
if err != nil {
|
||||
logx.Error(err.Error())
|
||||
return "error"
|
||||
}
|
||||
return messages.NewText("ok")
|
||||
|
||||
case models.CALLBACK_EVENT_SCAN:
|
||||
msg := models.EventScan{}
|
||||
err := event.ReadMessage(&msg)
|
||||
if err != nil {
|
||||
logx.Error(err.Error())
|
||||
return "error"
|
||||
}
|
||||
err = l.SendMessage(msg.FromUserName, msg.EventKey)
|
||||
if err != nil {
|
||||
return messages.NewText(i18n.FormatText(l.ctx, "login.loginFailed"))
|
||||
}
|
||||
return messages.NewText(i18n.FormatText(l.ctx, "login.loginSuccess"))
|
||||
|
||||
}
|
||||
|
||||
case models2.CALLBACK_MSG_TYPE_TEXT:
|
||||
msg := models.MessageText{}
|
||||
err := event.ReadMessage(&msg)
|
||||
if err != nil {
|
||||
logx.Error(err.Error())
|
||||
return "error"
|
||||
}
|
||||
}
|
||||
return messages.NewText("ok")
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
// SendMessage 发送消息到客户端
|
||||
func (l *WechatOffiaccountCallbackLogic) SendMessage(openId string, clientId string) error {
|
||||
messageData := MessageData{
|
||||
Openid: openId,
|
||||
ClientId: clientId,
|
||||
}
|
||||
jsonData, err := json.Marshal(response.SuccessWithData(messageData))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = websocket.QrcodeWebSocketHandler.SendMessageToClient(clientId, jsonData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package oauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
|
||||
)
|
||||
|
||||
type WechatOffiaccountCallbackVerifyLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewWechatOffiaccountCallbackVerifyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WechatOffiaccountCallbackVerifyLogic {
|
||||
return &WechatOffiaccountCallbackVerifyLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *WechatOffiaccountCallbackVerifyLogic) WechatOffiaccountCallbackVerify(r *http.Request) (*http.Response, error) {
|
||||
rs, err := l.svcCtx.WechatOfficial.Server.VerifyURL(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rs, nil
|
||||
}
|
@@ -30,12 +30,8 @@ func NewRefreshTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Refr
|
||||
}
|
||||
|
||||
func (l *RefreshTokenLogic) RefreshToken(r *http.Request) (resp *types.Response, err error) {
|
||||
session, err := l.svcCtx.Session.Get(r, constant.SESSION_KEY)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userId, ok := session.Values["user_id"].(string)
|
||||
if !ok {
|
||||
userId := r.Header.Get(constant.UID_HEADER_KEY)
|
||||
if userId == "" {
|
||||
return response.ErrorWithCode(403), nil
|
||||
}
|
||||
tokenData := l.svcCtx.RedisClient.Get(l.ctx, constant.UserTokenPrefix+userId).Val()
|
||||
@@ -47,6 +43,9 @@ func (l *RefreshTokenLogic) RefreshToken(r *http.Request) (resp *types.Response,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if redisTokenData.Revoked {
|
||||
return response.ErrorWithCode(403), nil
|
||||
}
|
||||
refreshToken, result := jwt.ParseRefreshToken(l.svcCtx.Config.Auth.AccessSecret, redisTokenData.RefreshToken)
|
||||
if !result {
|
||||
return response.ErrorWithCode(403), nil
|
||||
@@ -62,6 +61,7 @@ func (l *RefreshTokenLogic) RefreshToken(r *http.Request) (resp *types.Response,
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: redisTokenData.RefreshToken,
|
||||
UID: refreshToken.UserID,
|
||||
Revoked: false,
|
||||
}
|
||||
err = l.svcCtx.RedisClient.Set(l.ctx, constant.UserTokenPrefix+refreshToken.UserID, redisToken, time.Hour*24*7).Err()
|
||||
if err != nil {
|
||||
|
@@ -3,7 +3,8 @@ package user
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/rbcervilla/redisstore/v9"
|
||||
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
||||
"github.com/mssola/useragent"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@@ -65,19 +66,20 @@ func (l *AccountLoginLogic) AccountLogin(w http.ResponseWriter, r *http.Request,
|
||||
if !utils.Verify(userInfo.Password, req.Password) {
|
||||
return response.ErrorWithI18n(l.ctx, "login.invalidPassword"), nil
|
||||
}
|
||||
data, err := HandleUserLogin(userInfo, l.svcCtx, req.AutoLogin, r, w, l.ctx)
|
||||
data, err := HandleLoginJWT(userInfo, l.svcCtx, req.AutoLogin, r, l.ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 记录用户登录设备
|
||||
if err = GetUserLoginDevice(userInfo.UID, r, l.svcCtx.Ip2Region, l.svcCtx.DB); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.SuccessWithData(data), nil
|
||||
}
|
||||
|
||||
// HandleUserLogin 处理用户登录
|
||||
func HandleUserLogin(user *model.ScaAuthUser, svcCtx *svc.ServiceContext, autoLogin bool, r *http.Request, w http.ResponseWriter, ctx context.Context) (*types.LoginResponse, error) {
|
||||
// HandleLoginJWT 处理用户登录
|
||||
func HandleLoginJWT(user *model.ScaAuthUser, svcCtx *svc.ServiceContext, autoLogin bool, r *http.Request, ctx context.Context) (*types.LoginResponse, error) {
|
||||
// 获取用户登录设备
|
||||
err := GetUserLoginDevice(user.UID, r, svcCtx.Ip2Region, svcCtx.DB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 生成jwt token
|
||||
accessToken := jwt.GenerateAccessToken(svcCtx.Config.Auth.AccessSecret, jwt.AccessJWTPayload{
|
||||
UserID: user.UID,
|
||||
@@ -85,9 +87,9 @@ func HandleUserLogin(user *model.ScaAuthUser, svcCtx *svc.ServiceContext, autoLo
|
||||
})
|
||||
var days time.Duration
|
||||
if autoLogin {
|
||||
days = 7 * 24 * time.Hour
|
||||
days = 24 * time.Hour
|
||||
} else {
|
||||
days = time.Hour * 24
|
||||
days = time.Hour * 1
|
||||
}
|
||||
refreshToken := jwt.GenerateRefreshToken(svcCtx.Config.Auth.AccessSecret, jwt.RefreshJWTPayload{
|
||||
UserID: user.UID,
|
||||
@@ -106,29 +108,76 @@ func HandleUserLogin(user *model.ScaAuthUser, svcCtx *svc.ServiceContext, autoLo
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
UID: user.UID,
|
||||
Revoked: false,
|
||||
}
|
||||
err := svcCtx.RedisClient.Set(ctx, constant.UserTokenPrefix+user.UID, redisToken, days).Err()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = HandlerSession(r, w, user.UID, svcCtx.Session)
|
||||
err = svcCtx.RedisClient.Set(ctx, constant.UserTokenPrefix+user.UID, redisToken, days).Err()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &data, nil
|
||||
|
||||
}
|
||||
|
||||
// HandlerSession is a function to set the user_id in the session
|
||||
func HandlerSession(r *http.Request, w http.ResponseWriter, userID string, redisSession *redisstore.RedisStore) error {
|
||||
session, err := redisSession.Get(r, constant.SESSION_KEY)
|
||||
// GetUserLoginDevice 获取用户登录设备
|
||||
func GetUserLoginDevice(userId string, r *http.Request, ip2location *xdb.Searcher, DB *query.Query) error {
|
||||
userAgent := r.UserAgent()
|
||||
if userAgent == "" {
|
||||
return errors.New("user agent not found")
|
||||
}
|
||||
ip := utils.GetClientIP(r)
|
||||
location, err := ip2location.SearchByStr(ip)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
session.Values["user_id"] = userID
|
||||
err = session.Save(r, w)
|
||||
if err != nil {
|
||||
location = utils.RemoveZeroAndAdjust(location)
|
||||
|
||||
ua := useragent.New(userAgent)
|
||||
isBot := ua.Bot()
|
||||
browser, browserVersion := ua.Browser()
|
||||
os := ua.OS()
|
||||
mobile := ua.Mobile()
|
||||
mozilla := ua.Mozilla()
|
||||
platform := ua.Platform()
|
||||
engine, engineVersion := ua.Engine()
|
||||
var newIsBot int64 = 0
|
||||
var newIsMobile int64 = 0
|
||||
if isBot {
|
||||
newIsBot = 1
|
||||
}
|
||||
if mobile {
|
||||
newIsMobile = 1
|
||||
}
|
||||
userDevice := DB.ScaAuthUserDevice
|
||||
device, err := userDevice.Where(userDevice.UserID.Eq(userId), userDevice.IP.Eq(ip), userDevice.Agent.Eq(userAgent)).First()
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
newDevice := &model.ScaAuthUserDevice{
|
||||
UserID: userId,
|
||||
Bot: newIsBot,
|
||||
Agent: userAgent,
|
||||
Browser: browser,
|
||||
BrowserVersion: browserVersion,
|
||||
EngineName: engine,
|
||||
EngineVersion: engineVersion,
|
||||
IP: ip,
|
||||
Location: location,
|
||||
OperatingSystem: os,
|
||||
Mobile: newIsMobile,
|
||||
Mozilla: mozilla,
|
||||
Platform: platform,
|
||||
}
|
||||
if device == nil {
|
||||
// 创建新的设备记录
|
||||
err = DB.ScaAuthUserDevice.Create(newDevice)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
resultInfo, err := userDevice.Where(userDevice.ID.Eq(device.ID)).Updates(newDevice)
|
||||
if err != nil || resultInfo.RowsAffected == 0 {
|
||||
return errors.New("update device failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@@ -1,115 +0,0 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
||||
"github.com/mssola/useragent"
|
||||
"gorm.io/gorm"
|
||||
"net/http"
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/jwt"
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/response"
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/utils"
|
||||
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/model"
|
||||
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/query"
|
||||
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetUserDeviceLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetUserDeviceLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserDeviceLogic {
|
||||
return &GetUserDeviceLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetUserDeviceLogic) GetUserDevice(r *http.Request, w http.ResponseWriter, req *types.UserDeviceRequest) (resp *types.Response, err error) {
|
||||
token, ok := jwt.ParseAccessToken(l.svcCtx.Config.Auth.AccessSecret, req.AccessToken)
|
||||
if !ok {
|
||||
return response.Error(), nil
|
||||
}
|
||||
err = HandlerSession(r, w, token.UserID, l.svcCtx.Session)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = GetUserLoginDevice(token.UserID, r, l.svcCtx.Ip2Region, l.svcCtx.DB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.Success(), nil
|
||||
}
|
||||
|
||||
// GetUserLoginDevice 获取用户登录设备
|
||||
func GetUserLoginDevice(userId string, r *http.Request, ip2location *xdb.Searcher, DB *query.Query) error {
|
||||
userAgent := r.Header.Get("User-Agent")
|
||||
if userAgent == "" {
|
||||
return errors.New("user agent not found")
|
||||
}
|
||||
ip := utils.GetClientIP(r)
|
||||
location, err := ip2location.SearchByStr(ip)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
location = utils.RemoveZeroAndAdjust(location)
|
||||
|
||||
ua := useragent.New(userAgent)
|
||||
isBot := ua.Bot()
|
||||
browser, browserVersion := ua.Browser()
|
||||
os := ua.OS()
|
||||
mobile := ua.Mobile()
|
||||
mozilla := ua.Mozilla()
|
||||
platform := ua.Platform()
|
||||
engine, engineVersion := ua.Engine()
|
||||
var newIsBot int64 = 0
|
||||
var newIsMobile int64 = 0
|
||||
if isBot {
|
||||
newIsBot = 1
|
||||
}
|
||||
if mobile {
|
||||
newIsMobile = 1
|
||||
}
|
||||
userDevice := DB.ScaAuthUserDevice
|
||||
device, err := userDevice.Where(userDevice.UserID.Eq(userId), userDevice.IP.Eq(ip), userDevice.Agent.Eq(userAgent)).First()
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
newDevice := &model.ScaAuthUserDevice{
|
||||
UserID: userId,
|
||||
Bot: newIsBot,
|
||||
Agent: userAgent,
|
||||
Browser: browser,
|
||||
BrowserVersion: browserVersion,
|
||||
EngineName: engine,
|
||||
EngineVersion: engineVersion,
|
||||
IP: ip,
|
||||
Location: location,
|
||||
OperatingSystem: os,
|
||||
Mobile: newIsMobile,
|
||||
Mozilla: mozilla,
|
||||
Platform: platform,
|
||||
}
|
||||
if device == nil {
|
||||
// 创建新的设备记录
|
||||
err = DB.ScaAuthUserDevice.Create(newDevice)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
resultInfo, err := userDevice.Where(userDevice.ID.Eq(device.ID)).Updates(newDevice)
|
||||
if err != nil || resultInfo.RowsAffected == 0 {
|
||||
return errors.New("update device failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
@@ -1,37 +1,36 @@
|
||||
package oauth
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ArtisanCloud/PowerWeChat/v3/src/basicService/qrCode/response"
|
||||
|
||||
"net/http"
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/constant"
|
||||
response2 "schisandra-album-cloud-microservices/app/core/api/common/response"
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/utils"
|
||||
"time"
|
||||
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetWechatQrcodeLogic struct {
|
||||
type GetWechatOffiaccountQrcodeLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetWechatQrcodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetWechatQrcodeLogic {
|
||||
return &GetWechatQrcodeLogic{
|
||||
func NewGetWechatOffiaccountQrcodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetWechatOffiaccountQrcodeLogic {
|
||||
return &GetWechatOffiaccountQrcodeLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetWechatQrcodeLogic) GetWechatQrcode(r *http.Request, req *types.OAuthWechatRequest) (resp *types.Response, err error) {
|
||||
func (l *GetWechatOffiaccountQrcodeLogic) GetWechatOffiaccountQrcode(r *http.Request, req *types.OAuthWechatRequest) (resp *types.Response, err error) {
|
||||
ip := utils.GetClientIP(r) // 使用工具函数获取客户端IP
|
||||
key := constant.UserQrcodePrefix + ip
|
||||
|
||||
@@ -46,7 +45,7 @@ func (l *GetWechatQrcodeLogic) GetWechatQrcode(r *http.Request, req *types.OAuth
|
||||
}
|
||||
|
||||
// 生成临时二维码
|
||||
data, err := l.svcCtx.WechatPublic.QRCode.Temporary(l.ctx, req.Client_id, 7*24*3600)
|
||||
data, err := l.svcCtx.WechatOfficial.QRCode.Temporary(l.ctx, req.Client_id, 7*24*3600)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
@@ -79,32 +79,22 @@ func (l *PhoneLoginLogic) PhoneLogin(r *http.Request, w http.ResponseWriter, req
|
||||
_ = tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
data, err := HandleUserLogin(user, l.svcCtx, req.AutoLogin, r, w, l.ctx)
|
||||
data, err := HandleLoginJWT(user, l.svcCtx, req.AutoLogin, r, l.ctx)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
// 记录用户登录设备
|
||||
if err = GetUserLoginDevice(user.UID, r, l.svcCtx.Ip2Region, l.svcCtx.DB); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.SuccessWithData(data), nil
|
||||
} else {
|
||||
data, err := HandleUserLogin(userInfo, l.svcCtx, req.AutoLogin, r, w, l.ctx)
|
||||
data, err := HandleLoginJWT(userInfo, l.svcCtx, req.AutoLogin, r, l.ctx)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
// 记录用户登录设备
|
||||
if err = GetUserLoginDevice(userInfo.UID, r, l.svcCtx.Ip2Region, l.svcCtx.DB); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@@ -0,0 +1,107 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/yitter/idgenerator-go/idgen"
|
||||
"gorm.io/gorm"
|
||||
"net/http"
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/constant"
|
||||
randomname "schisandra-album-cloud-microservices/app/core/api/common/random_name"
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/response"
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/utils"
|
||||
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/model"
|
||||
"strconv"
|
||||
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
|
||||
"schisandra-album-cloud-microservices/app/core/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type WechatOffiaccountLoginLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewWechatOffiaccountLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WechatOffiaccountLoginLogic {
|
||||
return &WechatOffiaccountLoginLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *WechatOffiaccountLoginLogic) WechatOffiaccountLogin(r *http.Request, req *types.WechatOffiaccountLoginRequest) (resp *types.Response, err error) {
|
||||
tx := l.svcCtx.DB.Begin()
|
||||
userSocial := l.svcCtx.DB.ScaAuthUserSocial
|
||||
socialUser, err := tx.ScaAuthUserSocial.Where(userSocial.OpenID.Eq(req.Openid), userSocial.Source.Eq(constant.OAuthSourceWechat)).First()
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
if socialUser == nil {
|
||||
// 创建用户
|
||||
uid := idgen.NextId()
|
||||
uidStr := strconv.FormatInt(uid, 10)
|
||||
avatar := utils.GenerateAvatar(uidStr)
|
||||
name := randomname.GenerateName()
|
||||
|
||||
addUser := &model.ScaAuthUser{
|
||||
UID: uidStr,
|
||||
Avatar: avatar,
|
||||
Username: req.Openid,
|
||||
Nickname: name,
|
||||
Gender: constant.Male,
|
||||
}
|
||||
err = tx.ScaAuthUser.Create(addUser)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newSocialUser := &model.ScaAuthUserSocial{
|
||||
UserID: uidStr,
|
||||
OpenID: req.Openid,
|
||||
Source: constant.OAuthSourceWechat,
|
||||
}
|
||||
err = tx.ScaAuthUserSocial.Create(newSocialUser)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if res, err := l.svcCtx.CasbinEnforcer.AddRoleForUser(uidStr, constant.User); !res || err != nil {
|
||||
_ = tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := HandleLoginJWT(addUser, l.svcCtx, true, r, l.ctx)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
if err = tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.SuccessWithData(data), nil
|
||||
} else {
|
||||
authUser := l.svcCtx.DB.ScaAuthUser
|
||||
|
||||
authUserInfo, err := tx.ScaAuthUser.Where(authUser.UID.Eq(socialUser.UserID)).First()
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
_ = tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
data, err := HandleLoginJWT(authUserInfo, l.svcCtx, true, r, l.ctx)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
if err = tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.SuccessWithData(data), nil
|
||||
|
||||
}
|
||||
}
|
31
app/core/api/internal/middleware/authorization_middleware.go
Normal file
31
app/core/api/internal/middleware/authorization_middleware.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"net/http"
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/constant"
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/response"
|
||||
)
|
||||
|
||||
type AuthorizationMiddleware struct {
|
||||
Redis *redis.Client
|
||||
}
|
||||
|
||||
func NewAuthorizationMiddleware(redis *redis.Client) *AuthorizationMiddleware {
|
||||
return &AuthorizationMiddleware{
|
||||
Redis: redis,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AuthorizationMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userId := r.Context().Value("user_id").(string)
|
||||
redisToken := m.Redis.Get(r.Context(), constant.UserTokenPrefix+userId).Val()
|
||||
if redisToken == "" {
|
||||
httpx.OkJson(w, response.ErrorWithCodeMessage(403, "unauthorized"))
|
||||
return
|
||||
}
|
||||
next(w, r)
|
||||
}
|
||||
}
|
@@ -1,37 +1,24 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/casbin/casbin/v2"
|
||||
"net/http"
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/constant"
|
||||
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/rbcervilla/redisstore/v9"
|
||||
)
|
||||
|
||||
type CasbinVerifyMiddleware struct {
|
||||
casbin *casbin.SyncedCachedEnforcer
|
||||
session *redisstore.RedisStore
|
||||
casbin *casbin.SyncedCachedEnforcer
|
||||
}
|
||||
|
||||
func NewCasbinVerifyMiddleware(casbin *casbin.SyncedCachedEnforcer, session *redisstore.RedisStore) *CasbinVerifyMiddleware {
|
||||
func NewCasbinVerifyMiddleware(casbin *casbin.SyncedCachedEnforcer) *CasbinVerifyMiddleware {
|
||||
return &CasbinVerifyMiddleware{
|
||||
casbin: casbin,
|
||||
session: session,
|
||||
casbin: casbin,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *CasbinVerifyMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
session, err := m.session.Get(r, constant.SESSION_KEY)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
userId, ok := session.Values["user_id"].(string)
|
||||
if !ok {
|
||||
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
userId := r.Header.Get(constant.UID_HEADER_KEY)
|
||||
correct, err := m.casbin.Enforce(userId, r.URL.Path, r.Method)
|
||||
if err != nil || !correct {
|
||||
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||
|
@@ -4,7 +4,6 @@ import (
|
||||
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount"
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
||||
"github.com/rbcervilla/redisstore/v9"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/wenlng/go-captcha/v2/rotate"
|
||||
"github.com/wenlng/go-captcha/v2/slide"
|
||||
@@ -20,7 +19,6 @@ import (
|
||||
"schisandra-album-cloud-microservices/app/core/api/repository/mongodb"
|
||||
"schisandra-album-cloud-microservices/app/core/api/repository/mysql"
|
||||
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/query"
|
||||
"schisandra-album-cloud-microservices/app/core/api/repository/redis_session"
|
||||
"schisandra-album-cloud-microservices/app/core/api/repository/redisx"
|
||||
"schisandra-album-cloud-microservices/app/core/api/repository/sensitivex"
|
||||
"schisandra-album-cloud-microservices/app/core/api/repository/wechat_official"
|
||||
@@ -30,13 +28,13 @@ type ServiceContext struct {
|
||||
Config config.Config
|
||||
SecurityHeadersMiddleware rest.Middleware
|
||||
CasbinVerifyMiddleware rest.Middleware
|
||||
AuthorizationMiddleware rest.Middleware
|
||||
DB *query.Query
|
||||
RedisClient *redis.Client
|
||||
MongoClient *mongo.Database
|
||||
Session *redisstore.RedisStore
|
||||
Ip2Region *xdb.Searcher
|
||||
CasbinEnforcer *casbin.SyncedCachedEnforcer
|
||||
WechatPublic *officialAccount.OfficialAccount
|
||||
WechatOfficial *officialAccount.OfficialAccount
|
||||
Sensitive *sensitive.Manager
|
||||
RotateCaptcha rotate.Captcha
|
||||
SlideCaptcha slide.Captcha
|
||||
@@ -46,18 +44,17 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
redisClient := redisx.NewRedis(c.Redis.Host, c.Redis.Pass, c.Redis.DB)
|
||||
db, queryDB := mysql.NewMySQL(c.Mysql.DataSource, c.Mysql.MaxOpenConn, c.Mysql.MaxIdleConn, redisClient)
|
||||
casbinEnforcer := casbinx.NewCasbin(db)
|
||||
session := redis_session.NewRedisSession(redisClient)
|
||||
return &ServiceContext{
|
||||
Config: c,
|
||||
SecurityHeadersMiddleware: middleware.NewSecurityHeadersMiddleware().Handle,
|
||||
CasbinVerifyMiddleware: middleware.NewCasbinVerifyMiddleware(casbinEnforcer, session).Handle,
|
||||
CasbinVerifyMiddleware: middleware.NewCasbinVerifyMiddleware(casbinEnforcer).Handle,
|
||||
AuthorizationMiddleware: middleware.NewAuthorizationMiddleware(redisClient).Handle,
|
||||
DB: queryDB,
|
||||
RedisClient: redisClient,
|
||||
MongoClient: mongodb.NewMongoDB(c.Mongo.Uri, c.Mongo.Username, c.Mongo.Password, c.Mongo.AuthSource, c.Mongo.Database),
|
||||
Session: session,
|
||||
Ip2Region: ip2region.NewIP2Region(),
|
||||
CasbinEnforcer: casbinEnforcer,
|
||||
WechatPublic: wechat_official.NewWechatPublic(c.Wechat.AppID, c.Wechat.AppSecret, c.Wechat.Token, c.Wechat.AESKey, c.Redis.Host, c.Redis.Pass, c.Redis.DB),
|
||||
WechatOfficial: wechat_official.NewWechatPublic(c.Wechat.AppID, c.Wechat.AppSecret, c.Wechat.Token, c.Wechat.AESKey, c.Redis.Host, c.Redis.Pass, c.Redis.DB),
|
||||
Sensitive: sensitivex.NewSensitive(),
|
||||
RotateCaptcha: captcha.NewRotateCaptcha(),
|
||||
SlideCaptcha: captcha.NewSlideCaptcha(),
|
||||
|
@@ -6,6 +6,7 @@ type RedisToken struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
UID string `json:"uid"`
|
||||
Revoked bool `json:"revoked" default:"false"`
|
||||
}
|
||||
|
||||
func (res RedisToken) MarshalBinary() ([]byte, error) {
|
||||
|
@@ -119,6 +119,7 @@ type UploadRequest struct {
|
||||
UserId string `json:"user_id"`
|
||||
}
|
||||
|
||||
type UserDeviceRequest struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
type WechatOffiaccountLoginRequest struct {
|
||||
Openid string `json:"openid"`
|
||||
ClientId string `json:"client_id"`
|
||||
}
|
||||
|
@@ -1,28 +0,0 @@
|
||||
package redis_session
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/rbcervilla/redisstore/v9"
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"schisandra-album-cloud-microservices/app/core/api/common/constant"
|
||||
)
|
||||
|
||||
func NewRedisSession(client *redis.Client) *redisstore.RedisStore {
|
||||
store, err := redisstore.NewRedisStore(context.Background(), client)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
store.KeyPrefix(constant.UserSessionPrefix)
|
||||
store.Options(sessions.Options{
|
||||
Path: "/",
|
||||
MaxAge: 86400 * 7,
|
||||
HttpOnly: true,
|
||||
Secure: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
})
|
||||
return store
|
||||
}
|
Reference in New Issue
Block a user