🐛 fix the casbin invalidation bug/adjust routing strategy
This commit is contained in:
@@ -2,6 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"schisandra-cloud-album/api/captcha_api"
|
"schisandra-cloud-album/api/captcha_api"
|
||||||
|
"schisandra-cloud-album/api/client_api"
|
||||||
"schisandra-cloud-album/api/oauth_api"
|
"schisandra-cloud-album/api/oauth_api"
|
||||||
"schisandra-cloud-album/api/permission_api"
|
"schisandra-cloud-album/api/permission_api"
|
||||||
"schisandra-cloud-album/api/role_api"
|
"schisandra-cloud-album/api/role_api"
|
||||||
@@ -19,6 +20,7 @@ type Apis struct {
|
|||||||
WebsocketApi websocket_api.WebsocketAPI
|
WebsocketApi websocket_api.WebsocketAPI
|
||||||
RoleApi role_api.RoleAPI
|
RoleApi role_api.RoleAPI
|
||||||
PermissionApi permission_api.PermissionAPI
|
PermissionApi permission_api.PermissionAPI
|
||||||
|
ClientApi client_api.ClientAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
// Api new函数实例化,实例化完成后会返回结构体地指针类型
|
// Api new函数实例化,实例化完成后会返回结构体地指针类型
|
||||||
|
7
api/client_api/client.go
Normal file
7
api/client_api/client.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package client_api
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
type ClientAPI struct{}
|
||||||
|
|
||||||
|
var mu sync.Mutex
|
42
api/client_api/client_api.go
Normal file
42
api/client_api/client_api.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package client_api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
uuid "github.com/satori/go.uuid"
|
||||||
|
"schisandra-cloud-album/common/constant"
|
||||||
|
"schisandra-cloud-album/common/redis"
|
||||||
|
"schisandra-cloud-album/common/result"
|
||||||
|
"schisandra-cloud-album/global"
|
||||||
|
"schisandra-cloud-album/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateClientId 生成客户端ID
|
||||||
|
// @Summary 生成客户端ID
|
||||||
|
// @Description 生成客户端ID
|
||||||
|
// @Tags 微信公众号
|
||||||
|
// @Produce json
|
||||||
|
// @Router /api/oauth/generate_client_id [get]
|
||||||
|
func (ClientAPI) GenerateClientId(c *gin.Context) {
|
||||||
|
// 获取客户端IP
|
||||||
|
ip := utils.GetClientIP(c)
|
||||||
|
// 加锁
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
// 从Redis获取客户端ID
|
||||||
|
clientId := redis.Get(constant.UserLoginClientRedisKey + ip).Val()
|
||||||
|
if clientId != "" {
|
||||||
|
result.OkWithData(clientId, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成新的客户端ID
|
||||||
|
v1 := uuid.NewV1()
|
||||||
|
err := redis.Set(constant.UserLoginClientRedisKey+ip, v1.String(), 0).Err()
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result.OkWithData(v1.String(), c)
|
||||||
|
return
|
||||||
|
}
|
@@ -150,7 +150,6 @@ func (OAuthAPI) GetUserLoginDevice(c *gin.Context) {
|
|||||||
os := ua.OS()
|
os := ua.OS()
|
||||||
mobile := ua.Mobile()
|
mobile := ua.Mobile()
|
||||||
mozilla := ua.Mozilla()
|
mozilla := ua.Mozilla()
|
||||||
m := ua.Model()
|
|
||||||
platform := ua.Platform()
|
platform := ua.Platform()
|
||||||
engine, engineVersion := ua.Engine()
|
engine, engineVersion := ua.Engine()
|
||||||
device := model.ScaAuthUserDevice{
|
device := model.ScaAuthUserDevice{
|
||||||
@@ -164,7 +163,6 @@ func (OAuthAPI) GetUserLoginDevice(c *gin.Context) {
|
|||||||
Mobile: &mobile,
|
Mobile: &mobile,
|
||||||
Bot: &isBot,
|
Bot: &isBot,
|
||||||
Mozilla: &mozilla,
|
Mozilla: &mozilla,
|
||||||
Model: &m,
|
|
||||||
Platform: &platform,
|
Platform: &platform,
|
||||||
EngineName: &engine,
|
EngineName: &engine,
|
||||||
EngineVersion: &engineVersion,
|
EngineVersion: &engineVersion,
|
||||||
|
@@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount/server/handlers/models"
|
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount/server/handlers/models"
|
||||||
ginI18n "github.com/gin-contrib/i18n"
|
ginI18n "github.com/gin-contrib/i18n"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
uuid "github.com/satori/go.uuid"
|
|
||||||
"github.com/yitter/idgenerator-go/idgen"
|
"github.com/yitter/idgenerator-go/idgen"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"schisandra-cloud-album/api/user_api/dto"
|
"schisandra-cloud-album/api/user_api/dto"
|
||||||
@@ -29,36 +28,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GenerateClientId 生成客户端ID
|
|
||||||
// @Summary 生成客户端ID
|
|
||||||
// @Description 生成客户端ID
|
|
||||||
// @Tags 微信公众号
|
|
||||||
// @Produce json
|
|
||||||
// @Router /api/oauth/generate_client_id [get]
|
|
||||||
func (OAuthAPI) GenerateClientId(c *gin.Context) {
|
|
||||||
// 获取客户端IP
|
|
||||||
ip := utils.GetClientIP(c)
|
|
||||||
// 加锁
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
|
|
||||||
// 从Redis获取客户端ID
|
|
||||||
clientId := redis.Get(constant.UserLoginClientRedisKey + ip).Val()
|
|
||||||
if clientId != "" {
|
|
||||||
result.OkWithData(clientId, c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成新的客户端ID
|
|
||||||
v1 := uuid.NewV1()
|
|
||||||
err := redis.Set(constant.UserLoginClientRedisKey+ip, v1.String(), 0).Err()
|
|
||||||
if err != nil {
|
|
||||||
global.LOG.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
result.OkWithData(v1.String(), c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CallbackNotify 微信回调
|
// CallbackNotify 微信回调
|
||||||
// @Summary 微信回调
|
// @Summary 微信回调
|
||||||
// @Tags 微信公众号
|
// @Tags 微信公众号
|
||||||
|
@@ -68,3 +68,18 @@ func (PermissionAPI) AssignPermissionsToRole(c *gin.Context) {
|
|||||||
result.OkWithMessage(ginI18n.MustGetMessage(c, "AssignSuccess"), c)
|
result.OkWithMessage(ginI18n.MustGetMessage(c, "AssignSuccess"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserPermissions 获取服用权限
|
||||||
|
func (PermissionAPI) GetUserPermissions(c *gin.Context) {
|
||||||
|
userId := c.Query("user_id")
|
||||||
|
if userId == "" {
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "GetUserFailed"), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := global.Casbin.GetImplicitRolesForUser(userId)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result.OkWithData(data, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@@ -154,13 +154,14 @@ func (UserAPI) AccountLogin(c *gin.Context) {
|
|||||||
// @Router /api/user/phone_login [post]
|
// @Router /api/user/phone_login [post]
|
||||||
func (UserAPI) PhoneLogin(c *gin.Context) {
|
func (UserAPI) PhoneLogin(c *gin.Context) {
|
||||||
request := dto.PhoneLoginRequest{}
|
request := dto.PhoneLoginRequest{}
|
||||||
err := c.ShouldBindJSON(&request)
|
err := c.ShouldBind(&request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
phone := request.Phone
|
phone := request.Phone
|
||||||
captcha := request.Captcha
|
captcha := request.Captcha
|
||||||
|
autoLogin := request.AutoLogin
|
||||||
if phone == "" || captcha == "" {
|
if phone == "" || captcha == "" {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneAndCaptchaNotEmpty"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneAndCaptchaNotEmpty"), c)
|
||||||
return
|
return
|
||||||
@@ -213,7 +214,7 @@ func (UserAPI) PhoneLogin(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
handelUserLogin(addUser, request.AutoLogin, c)
|
handelUserLogin(addUser, autoLogin, c)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
errChan <- err
|
errChan <- err
|
||||||
@@ -227,24 +228,24 @@ func (UserAPI) PhoneLogin(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
codeChan := make(chan *string)
|
codeChan := make(chan string)
|
||||||
go func() {
|
go func() {
|
||||||
code := redis.Get(constant.UserLoginSmsRedisKey + phone).Val()
|
code := redis.Get(constant.UserLoginSmsRedisKey + phone).Val()
|
||||||
codeChan <- &code
|
codeChan <- code
|
||||||
}()
|
}()
|
||||||
|
|
||||||
code := <-codeChan
|
code := <-codeChan
|
||||||
close(codeChan)
|
close(codeChan)
|
||||||
|
|
||||||
if code == nil {
|
if code == "" {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if &captcha != code {
|
if captcha != code {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaError"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
handelUserLogin(user, request.AutoLogin, c)
|
handelUserLogin(user, autoLogin, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,7 +317,7 @@ func handelUserLogin(user model.ScaAuthUser, autoLogin bool, c *gin.Context) {
|
|||||||
if autoLogin {
|
if autoLogin {
|
||||||
days = 7 * 24 * time.Hour
|
days = 7 * 24 * time.Hour
|
||||||
} else {
|
} else {
|
||||||
days = 24 * time.Hour
|
days = time.Minute * 30
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshToken, expiresAt := utils.GenerateRefreshToken(utils.RefreshJWTPayload{UserID: user.UID}, days)
|
refreshToken, expiresAt := utils.GenerateRefreshToken(utils.RefreshJWTPayload{UserID: user.UID}, days)
|
||||||
@@ -455,7 +456,6 @@ func getUserLoginDevice(user model.ScaAuthUser, c *gin.Context) bool {
|
|||||||
os := ua.OS()
|
os := ua.OS()
|
||||||
mobile := ua.Mobile()
|
mobile := ua.Mobile()
|
||||||
mozilla := ua.Mozilla()
|
mozilla := ua.Mozilla()
|
||||||
m := ua.Model()
|
|
||||||
platform := ua.Platform()
|
platform := ua.Platform()
|
||||||
engine, engineVersion := ua.Engine()
|
engine, engineVersion := ua.Engine()
|
||||||
|
|
||||||
@@ -470,7 +470,6 @@ func getUserLoginDevice(user model.ScaAuthUser, c *gin.Context) bool {
|
|||||||
Mobile: &mobile,
|
Mobile: &mobile,
|
||||||
Bot: &isBot,
|
Bot: &isBot,
|
||||||
Mozilla: &mozilla,
|
Mozilla: &mozilla,
|
||||||
Model: &m,
|
|
||||||
Platform: &platform,
|
Platform: &platform,
|
||||||
EngineName: &engine,
|
EngineName: &engine,
|
||||||
EngineVersion: &engineVersion,
|
EngineVersion: &engineVersion,
|
||||||
|
@@ -8,7 +8,7 @@ p = sub, obj, act
|
|||||||
g = _, _
|
g = _, _
|
||||||
|
|
||||||
[policy_effect]
|
[policy_effect]
|
||||||
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
|
e = some(where (p.eft == allow))
|
||||||
|
|
||||||
[matchers]
|
[matchers]
|
||||||
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
|
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
|
@@ -5,37 +5,31 @@ import (
|
|||||||
"github.com/casbin/casbin/v2/model"
|
"github.com/casbin/casbin/v2/model"
|
||||||
gormadapter "github.com/casbin/gorm-adapter/v3"
|
gormadapter "github.com/casbin/gorm-adapter/v3"
|
||||||
"schisandra-cloud-album/global"
|
"schisandra-cloud-album/global"
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
once sync.Once
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitCasbin() {
|
func InitCasbin() {
|
||||||
once.Do(func() {
|
adapter, err := gormadapter.NewAdapterByDBUseTableName(global.DB, global.CONFIG.Casbin.TablePrefix, global.CONFIG.Casbin.TableName)
|
||||||
adapter, err := gormadapter.NewAdapterByDBUseTableName(global.DB, global.CONFIG.Casbin.TablePrefix, global.CONFIG.Casbin.TableName)
|
if err != nil {
|
||||||
if err != nil {
|
global.LOG.Error(err.Error())
|
||||||
global.LOG.Error(err.Error())
|
panic(err)
|
||||||
panic(err)
|
}
|
||||||
}
|
m, err := model.NewModelFromFile(global.CONFIG.Casbin.ModelPath)
|
||||||
m, err := model.NewModelFromFile(global.CONFIG.Casbin.ModelPath)
|
if err != nil {
|
||||||
if err != nil {
|
global.LOG.Error(err.Error())
|
||||||
global.LOG.Error(err.Error())
|
panic(err)
|
||||||
panic(err)
|
}
|
||||||
}
|
e, err := casbin.NewCachedEnforcer(m, adapter)
|
||||||
e, err := casbin.NewCachedEnforcer(m, adapter)
|
if err != nil {
|
||||||
if err != nil {
|
global.LOG.Error(err.Error())
|
||||||
global.LOG.Error(err.Error())
|
panic(err)
|
||||||
panic(err)
|
}
|
||||||
}
|
e.EnableCache(true)
|
||||||
e.EnableCache(true)
|
e.SetExpireTime(60 * 60)
|
||||||
e.SetExpireTime(60 * 60)
|
err = e.LoadPolicy()
|
||||||
err = e.LoadPolicy()
|
if err != nil {
|
||||||
if err != nil {
|
global.LOG.Error(err.Error())
|
||||||
global.LOG.Error(err.Error())
|
panic(err)
|
||||||
panic(err)
|
}
|
||||||
}
|
global.Casbin = e
|
||||||
global.Casbin = e
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@@ -56,3 +56,5 @@ InternalError = "internal error!"
|
|||||||
RequestError = "request error!"
|
RequestError = "request error!"
|
||||||
AssignFailed = "assign failed!"
|
AssignFailed = "assign failed!"
|
||||||
AssignSuccess = "assign successfully!"
|
AssignSuccess = "assign successfully!"
|
||||||
|
DuplicateLogin = "duplicate login!"
|
||||||
|
PermissionDenied = "permission denied!"
|
||||||
|
@@ -56,4 +56,6 @@ InternalError = "内部错误!"
|
|||||||
RequestError = "请求错误!"
|
RequestError = "请求错误!"
|
||||||
AssignFailed = "分配失败!"
|
AssignFailed = "分配失败!"
|
||||||
AssignSuccess = "分配成功!"
|
AssignSuccess = "分配成功!"
|
||||||
|
DuplicateLogin = "重复登录!"
|
||||||
|
PermissionDenied = "权限不足!"
|
||||||
|
|
||||||
|
@@ -1,27 +1,40 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
ginI18n "github.com/gin-contrib/i18n"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"schisandra-cloud-album/common/result"
|
||||||
"schisandra-cloud-album/global"
|
"schisandra-cloud-album/global"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CasbinMiddleware() gin.HandlerFunc {
|
func CasbinMiddleware() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
userId, ok := c.Get("userId")
|
userIdAny, exists := c.Get("userId")
|
||||||
if !ok {
|
if !exists {
|
||||||
global.LOG.Error("casbin middleware: userId not found")
|
global.LOG.Error("casbin middleware: userId not found")
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "PermissionDenied"), c)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
userId, ok := userIdAny.(*string)
|
||||||
|
if !ok {
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "PermissionDenied"), c)
|
||||||
|
global.LOG.Error("casbin middleware: userId is not string")
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userIdStr := *userId
|
||||||
method := c.Request.Method
|
method := c.Request.Method
|
||||||
path := c.Request.URL.Path
|
path := c.Request.URL.Path
|
||||||
ok, err := global.Casbin.Enforce(userId.(string), path, method)
|
correct, err := global.Casbin.Enforce(userIdStr, path, method)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "PermissionDenied"), c)
|
||||||
global.LOG.Error("casbin middleware: ", err)
|
global.LOG.Error("casbin middleware: ", err)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !ok {
|
if !correct {
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "PermissionDenied"), c)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
31
middleware/check_client.go
Normal file
31
middleware/check_client.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
ginI18n "github.com/gin-contrib/i18n"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"schisandra-cloud-album/common/constant"
|
||||||
|
"schisandra-cloud-album/common/redis"
|
||||||
|
"schisandra-cloud-album/common/result"
|
||||||
|
"schisandra-cloud-album/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckClientMiddleware 检查客户端请求是否合法
|
||||||
|
func CheckClientMiddleware() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
id := c.GetHeader("X-Request-Id")
|
||||||
|
if id == "" {
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "AuthVerifyFailed"), c)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := utils.GetClientIP(c)
|
||||||
|
clientId := redis.Get(constant.UserLoginClientRedisKey + ip).Val()
|
||||||
|
if clientId == "" || clientId != id {
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "AuthVerifyFailed"), c)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
@@ -1,35 +1,60 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
ginI18n "github.com/gin-contrib/i18n"
|
ginI18n "github.com/gin-contrib/i18n"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"schisandra-cloud-album/common/constant"
|
||||||
|
"schisandra-cloud-album/common/redis"
|
||||||
"schisandra-cloud-album/common/result"
|
"schisandra-cloud-album/common/result"
|
||||||
"schisandra-cloud-album/global"
|
"schisandra-cloud-album/global"
|
||||||
"schisandra-cloud-album/utils"
|
"schisandra-cloud-album/utils"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type TokenData struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
ExpiresAt int64 `json:"expires_at"`
|
||||||
|
UID *string `json:"uid"`
|
||||||
|
}
|
||||||
|
|
||||||
func JWTAuthMiddleware() gin.HandlerFunc {
|
func JWTAuthMiddleware() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
// 默认双Token放在请求头Authorization的Bearer中,并以空格隔开
|
// 默认双Token放在请求头Authorization的Bearer中,并以空格隔开
|
||||||
authHeader := c.GetHeader(global.CONFIG.JWT.HeaderKey)
|
authHeader := c.GetHeader(global.CONFIG.JWT.HeaderKey)
|
||||||
if authHeader == "" {
|
if authHeader == "" {
|
||||||
c.Abort()
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "AuthVerifyFailed"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "AuthVerifyFailed"), c)
|
||||||
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
headerPrefix := global.CONFIG.JWT.HeaderPrefix
|
headerPrefix := global.CONFIG.JWT.HeaderPrefix
|
||||||
accessToken := strings.TrimPrefix(authHeader, headerPrefix+" ")
|
accessToken := strings.TrimPrefix(authHeader, headerPrefix+" ")
|
||||||
|
|
||||||
if accessToken == "" {
|
if accessToken == "" {
|
||||||
c.Abort()
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "AuthVerifyFailed"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "AuthVerifyFailed"), c)
|
||||||
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
parseToken, isUpd, err := utils.ParseAccessToken(accessToken)
|
parseToken, isUpd, err := utils.ParseAccessToken(accessToken)
|
||||||
if err != nil || !isUpd {
|
if err != nil || !isUpd {
|
||||||
c.Abort()
|
|
||||||
result.FailWithCodeAndMessage(401, ginI18n.MustGetMessage(c, "AuthVerifyExpired"), c)
|
result.FailWithCodeAndMessage(401, ginI18n.MustGetMessage(c, "AuthVerifyExpired"), c)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
token := redis.Get(constant.UserLoginTokenRedisKey + *parseToken.UserID).Val()
|
||||||
|
tokenResult := TokenData{}
|
||||||
|
err = json.Unmarshal([]byte(token), &tokenResult)
|
||||||
|
if err != nil {
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "AuthVerifyExpired"), c)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tokenResult.AccessToken != accessToken {
|
||||||
|
result.FailWithCodeAndMessage(403, ginI18n.MustGetMessage(c, "DuplicateLogin"), c)
|
||||||
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Set("userId", parseToken.UserID)
|
c.Set("userId", parseToken.UserID)
|
||||||
|
@@ -22,7 +22,6 @@ type ScaAuthUserDevice struct {
|
|||||||
Mobile *bool `gorm:"column:mobile;type:int(11);comment:是否为手机" json:"mobile"` // 是否为手机
|
Mobile *bool `gorm:"column:mobile;type:int(11);comment:是否为手机" json:"mobile"` // 是否为手机
|
||||||
Bot *bool `gorm:"column:bot;type:int(11);comment:是否为机器人" json:"bot"` // 是否为机器人
|
Bot *bool `gorm:"column:bot;type:int(11);comment:是否为机器人" json:"bot"` // 是否为机器人
|
||||||
Mozilla *string `gorm:"column:mozilla;type:varchar(10);comment:火狐版本" json:"mozilla"` // 火狐版本
|
Mozilla *string `gorm:"column:mozilla;type:varchar(10);comment:火狐版本" json:"mozilla"` // 火狐版本
|
||||||
Model *string `gorm:"column:model;type:varchar(20);comment:设备型号" json:"model"` // 设备型号
|
|
||||||
Platform *string `gorm:"column:platform;type:varchar(20);comment:平台" json:"platform"` // 平台
|
Platform *string `gorm:"column:platform;type:varchar(20);comment:平台" json:"platform"` // 平台
|
||||||
EngineName *string `gorm:"column:engine_name;type:varchar(20);comment:引擎名称" json:"engine_name"` // 引擎名称
|
EngineName *string `gorm:"column:engine_name;type:varchar(20);comment:引擎名称" json:"engine_name"` // 引擎名称
|
||||||
EngineVersion *string `gorm:"column:engine_version;type:varchar(20);comment:引擎版本" json:"engine_version"` // 引擎版本
|
EngineVersion *string `gorm:"column:engine_version;type:varchar(20);comment:引擎版本" json:"engine_version"` // 引擎版本
|
||||||
|
12
router/modules/client_router.go
Normal file
12
router/modules/client_router.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package modules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"schisandra-cloud-album/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
var clientApi = api.Api.ClientApi
|
||||||
|
|
||||||
|
func ClientRouter(router *gin.RouterGroup) {
|
||||||
|
router.GET("/client/generate_client_id", clientApi.GenerateClientId)
|
||||||
|
}
|
@@ -12,7 +12,6 @@ func OauthRouter(router *gin.RouterGroup) {
|
|||||||
{
|
{
|
||||||
wechatRouter := group.Group("/wechat")
|
wechatRouter := group.Group("/wechat")
|
||||||
{
|
{
|
||||||
wechatRouter.GET("/generate_client_id", oauth.GenerateClientId)
|
|
||||||
wechatRouter.GET("/get_temp_qrcode", oauth.GetTempQrCode)
|
wechatRouter.GET("/get_temp_qrcode", oauth.GetTempQrCode)
|
||||||
//wechatRouter.GET("/callback", oauth.CallbackVerify)
|
//wechatRouter.GET("/callback", oauth.CallbackVerify)
|
||||||
wechatRouter.POST("/callback", oauth.CallbackNotify)
|
wechatRouter.POST("/callback", oauth.CallbackNotify)
|
||||||
@@ -34,5 +33,4 @@ func OauthRouter(router *gin.RouterGroup) {
|
|||||||
}
|
}
|
||||||
group.GET("/get_device", oauth.GetUserLoginDevice)
|
group.GET("/get_device", oauth.GetUserLoginDevice)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,8 @@ var permissionApi = api.Api.PermissionApi
|
|||||||
|
|
||||||
func PermissionRouter(router *gin.RouterGroup) {
|
func PermissionRouter(router *gin.RouterGroup) {
|
||||||
group := router.Group("/auth/permission")
|
group := router.Group("/auth/permission")
|
||||||
//group.Use(middleware.JWTAuthMiddleware())
|
{
|
||||||
group.POST("/add", permissionApi.AddPermissions)
|
group.POST("/add", permissionApi.AddPermissions)
|
||||||
|
group.GET("/get_user_permissions", permissionApi.GetUserPermissions)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,14 +3,15 @@ package modules
|
|||||||
import (
|
import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"schisandra-cloud-album/api"
|
"schisandra-cloud-album/api"
|
||||||
"schisandra-cloud-album/middleware"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var roleApi = api.Api.RoleApi
|
var roleApi = api.Api.RoleApi
|
||||||
|
|
||||||
func RoleRouter(router *gin.RouterGroup) {
|
func RoleRouter(router *gin.RouterGroup) {
|
||||||
group := router.Group("/auth")
|
group := router.Group("/auth")
|
||||||
group.Use(middleware.JWTAuthMiddleware())
|
{
|
||||||
group.POST("/role/create", roleApi.CreateRole)
|
group.POST("/role/create", roleApi.CreateRole)
|
||||||
group.POST("/role/add_role_to_user", roleApi.AddRoleToUser)
|
group.POST("/role/add_role_to_user", roleApi.AddRoleToUser)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -8,10 +8,10 @@ import (
|
|||||||
"schisandra-cloud-album/global"
|
"schisandra-cloud-album/global"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SwaggerRouter(router *gin.Engine) {
|
func SwaggerRouter(router *gin.RouterGroup) {
|
||||||
docs.SwaggerInfo.BasePath = ""
|
docs.SwaggerInfo.BasePath = ""
|
||||||
docs.SwaggerInfo.Description = global.CONFIG.Swagger.Description
|
docs.SwaggerInfo.Description = global.CONFIG.Swagger.Description
|
||||||
router.GET("/api/doc/*any", gin.BasicAuth(gin.Accounts{
|
router.GET("/doc/*any", gin.BasicAuth(gin.Accounts{
|
||||||
global.CONFIG.Swagger.User: global.CONFIG.Swagger.Password,
|
global.CONFIG.Swagger.User: global.CONFIG.Swagger.Password,
|
||||||
}), ginSwagger.WrapHandler(swaggerFiles.Handler, func(config *ginSwagger.Config) {
|
}), ginSwagger.WrapHandler(swaggerFiles.Handler, func(config *ginSwagger.Config) {
|
||||||
config.Title = global.CONFIG.Swagger.Title
|
config.Title = global.CONFIG.Swagger.Title
|
||||||
|
@@ -3,7 +3,6 @@ package modules
|
|||||||
import (
|
import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"schisandra-cloud-album/api"
|
"schisandra-cloud-album/api"
|
||||||
"schisandra-cloud-album/middleware"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var userApi = api.Api.UserApi
|
var userApi = api.Api.UserApi
|
||||||
@@ -16,15 +15,18 @@ func UserRouter(router *gin.RouterGroup) {
|
|||||||
userGroup.POST("/phone_login", userApi.PhoneLogin)
|
userGroup.POST("/phone_login", userApi.PhoneLogin)
|
||||||
userGroup.POST("/reset_password", userApi.ResetPassword)
|
userGroup.POST("/reset_password", userApi.ResetPassword)
|
||||||
}
|
}
|
||||||
authGroup := router.Group("auth").Use(middleware.JWTAuthMiddleware()).Use(middleware.CasbinMiddleware())
|
|
||||||
{
|
|
||||||
authGroup.GET("/user/list", userApi.GetUserList)
|
|
||||||
authGroup.GET("/user/query_by_uuid", userApi.QueryUserByUuid)
|
|
||||||
|
|
||||||
}
|
|
||||||
tokenGroup := router.Group("token")
|
tokenGroup := router.Group("token")
|
||||||
{
|
{
|
||||||
tokenGroup.POST("/refresh", userApi.RefreshHandler)
|
tokenGroup.POST("/refresh", userApi.RefreshHandler)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserRouterAuth 用户相关路由 有auth接口组需要token验证
|
||||||
|
func UserRouterAuth(router *gin.RouterGroup) {
|
||||||
|
authGroup := router.Group("auth")
|
||||||
|
{
|
||||||
|
authGroup.GET("/user/list", userApi.GetUserList)
|
||||||
|
authGroup.GET("/user/query_by_uuid", userApi.QueryUserByUuid)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -3,12 +3,15 @@ package router
|
|||||||
import (
|
import (
|
||||||
"github.com/gin-contrib/cors"
|
"github.com/gin-contrib/cors"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"schisandra-cloud-album/api"
|
||||||
"schisandra-cloud-album/global"
|
"schisandra-cloud-album/global"
|
||||||
"schisandra-cloud-album/middleware"
|
"schisandra-cloud-album/middleware"
|
||||||
"schisandra-cloud-album/router/modules"
|
"schisandra-cloud-album/router/modules"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var oauth = api.Api.OAuthApi
|
||||||
|
|
||||||
func InitRouter() *gin.Engine {
|
func InitRouter() *gin.Engine {
|
||||||
gin.SetMode(global.CONFIG.System.Env)
|
gin.SetMode(global.CONFIG.System.Env)
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
@@ -18,25 +21,40 @@ func InitRouter() *gin.Engine {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
router.Use(middleware.RateLimitMiddleware(time.Millisecond*100, 20)) // 限流中间件
|
router.Use(middleware.RateLimitMiddleware(time.Millisecond*100, 20)) // 限流中间件
|
||||||
publicGroup := router.Group("api")
|
|
||||||
// 跨域设置
|
// 跨域设置
|
||||||
publicGroup.Use(cors.New(cors.Config{
|
router.Use(cors.New(cors.Config{
|
||||||
AllowOrigins: []string{global.CONFIG.System.Web},
|
AllowOrigins: []string{global.CONFIG.System.Web},
|
||||||
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"},
|
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"},
|
||||||
AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization", "X-CSRF-Token", "Accept-Language"},
|
AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization", "X-CSRF-Token", "Accept-Language", "X-Request-Id"},
|
||||||
AllowCredentials: true,
|
AllowCredentials: true,
|
||||||
MaxAge: 12 * time.Hour,
|
MaxAge: 12 * time.Hour,
|
||||||
}))
|
}))
|
||||||
// 国际化设置
|
// 国际化设置
|
||||||
publicGroup.Use(middleware.I18n())
|
router.Use(middleware.I18n())
|
||||||
|
|
||||||
|
publicGroup := router.Group("api") // 不需要鉴权的路由组
|
||||||
|
authGroup := router.Group("api") // 需要鉴权的路由组
|
||||||
|
authGroup.Use(
|
||||||
|
middleware.JWTAuthMiddleware(),
|
||||||
|
middleware.CasbinMiddleware(),
|
||||||
|
middleware.CheckClientMiddleware())
|
||||||
|
|
||||||
|
checkClientGroup := router.Group("api") // 需要检查客户端的路由组
|
||||||
|
|
||||||
|
checkClientGroup.Use(middleware.CheckClientMiddleware())
|
||||||
|
|
||||||
|
modules.ClientRouter(publicGroup) // 注册客户端路由
|
||||||
|
modules.SwaggerRouter(publicGroup) // 注册swagger路由
|
||||||
|
modules.WebsocketRouter(publicGroup) // 注册websocket路由
|
||||||
|
|
||||||
|
modules.CaptchaRouter(checkClientGroup) // 注册验证码路由
|
||||||
|
modules.SmsRouter(checkClientGroup) // 注册短信验证码路由
|
||||||
|
modules.OauthRouter(checkClientGroup) // 注册oauth路由
|
||||||
|
modules.UserRouter(checkClientGroup) // 注册鉴权路由
|
||||||
|
|
||||||
|
modules.UserRouterAuth(authGroup) // 注册鉴权路由
|
||||||
|
modules.RoleRouter(authGroup) // 注册角色路由
|
||||||
|
modules.PermissionRouter(authGroup) // 注册权限路由
|
||||||
|
|
||||||
modules.SwaggerRouter(router) // 注册swagger路由
|
|
||||||
modules.UserRouter(publicGroup) // 注册鉴权路由
|
|
||||||
modules.CaptchaRouter(publicGroup) // 注册验证码路由
|
|
||||||
modules.SmsRouter(publicGroup) // 注册短信验证码路由
|
|
||||||
modules.OauthRouter(publicGroup) // 注册oauth路由
|
|
||||||
modules.WebsocketRouter(publicGroup) // 注册websocket路由
|
|
||||||
modules.RoleRouter(publicGroup) // 注册角色路由
|
|
||||||
modules.PermissionRouter(publicGroup) // 注册权限路由
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,6 @@ func (UserDeviceService) UpdateUserDevice(id int64, userDevice *model.ScaAuthUse
|
|||||||
Mobile: userDevice.Mobile,
|
Mobile: userDevice.Mobile,
|
||||||
Bot: userDevice.Bot,
|
Bot: userDevice.Bot,
|
||||||
Mozilla: userDevice.Mozilla,
|
Mozilla: userDevice.Mozilla,
|
||||||
Model: userDevice.Model,
|
|
||||||
Platform: userDevice.Platform,
|
Platform: userDevice.Platform,
|
||||||
EngineName: userDevice.EngineName,
|
EngineName: userDevice.EngineName,
|
||||||
EngineVersion: userDevice.EngineVersion,
|
EngineVersion: userDevice.EngineVersion,
|
||||||
|
@@ -33,7 +33,7 @@ func GenerateAccessToken(payload AccessJWTPayload) (string, error) {
|
|||||||
claims := AccessJWTClaims{
|
claims := AccessJWTClaims{
|
||||||
AccessJWTPayload: 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.Minute * 30)),
|
||||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
NotBefore: jwt.NewNumericDate(time.Now()),
|
NotBefore: jwt.NewNumericDate(time.Now()),
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user