update qq/gitee/github oauth2 login

This commit is contained in:
landaiqing
2024-08-24 16:31:40 +08:00
parent 014abca8f8
commit 9330935822
45 changed files with 1243 additions and 642 deletions

View File

@@ -3,6 +3,8 @@ package api
import (
"schisandra-cloud-album/api/captcha_api"
"schisandra-cloud-album/api/oauth_api"
"schisandra-cloud-album/api/permission_api"
"schisandra-cloud-album/api/role_api"
"schisandra-cloud-album/api/sms_api"
"schisandra-cloud-album/api/user_api"
"schisandra-cloud-album/api/websocket_api"
@@ -15,6 +17,8 @@ type Apis struct {
SmsApi sms_api.SmsAPI
OAuthApi oauth_api.OAuthAPI
WebsocketApi websocket_api.WebsocketAPI
RoleApi role_api.RoleAPI
PermissionApi permission_api.PermissionAPI
}
// Api new函数实例化实例化完成后会返回结构体地指针类型

View File

@@ -142,21 +142,42 @@ func (OAuthAPI) GiteeCallback(c *gin.Context) {
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
return
}
// 通过 code, 获取 token
// 异步获取 token
var tokenChan = make(chan *Token)
var errChan = make(chan error)
go func() {
var tokenAuthUrl = GetGiteeTokenAuthUrl(code)
var token *Token
if token, err = GetGiteeToken(tokenAuthUrl); err != nil {
global.LOG.Error(err)
token, err := GetGiteeToken(tokenAuthUrl)
if err != nil {
errChan <- err
return
}
tokenChan <- token
}()
// 通过token获取用户信息
var userInfo map[string]interface{}
if userInfo, err = GetGiteeUserInfo(token); err != nil {
global.LOG.Error(err)
// 异步获取用户信息
var userInfoChan = make(chan map[string]interface{})
go func() {
token := <-tokenChan
if token == nil {
errChan <- errors.New("failed to get token")
return
}
userInfo, err := GetGiteeUserInfo(token)
if err != nil {
errChan <- err
return
}
userInfoChan <- userInfo
}()
// 等待结果
select {
case err = <-errChan:
global.LOG.Error(err)
return
case userInfo := <-userInfoChan:
userInfoBytes, err := json.Marshal(userInfo)
if err != nil {
global.LOG.Error(err)
@@ -172,6 +193,17 @@ func (OAuthAPI) GiteeCallback(c *gin.Context) {
Id := strconv.Itoa(giteeUser.ID)
userSocial, err := userSocialService.QueryUserSocialByUUID(Id, enum.OAuthSourceGitee)
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
db := global.DB
tx := db.Begin() // 开始事务
if tx.Error != nil {
global.LOG.Error(tx.Error)
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// 第一次登录,创建用户
uid := idgen.NextId()
uidStr := strconv.FormatInt(uid, 10)
@@ -185,57 +217,37 @@ func (OAuthAPI) GiteeCallback(c *gin.Context) {
}
addUser, err := userService.AddUser(user)
if err != nil {
tx.Rollback()
global.LOG.Error(err)
return
}
gitee := enum.OAuthSourceGitee
userSocial = model.ScaAuthUserSocial{
UserID: &addUser.ID,
UserID: &uidStr,
UUID: &Id,
Source: &gitee,
}
err = userSocialService.AddUserSocial(userSocial)
if err != nil {
tx.Rollback()
global.LOG.Error(err)
return
}
userRole := model.ScaAuthUserRole{
UserID: uidStr,
RoleID: enum.User,
}
err = userRoleService.AddUserRole(userRole)
_, err = global.Casbin.AddRoleForUser(uidStr, enum.User)
if err != nil {
tx.Rollback()
global.LOG.Error(err)
return
}
res, data := HandelUserLogin(addUser)
if !res {
return
}
tokenData, err := json.Marshal(data)
if err != nil {
if err := tx.Commit().Error; err != nil {
tx.Rollback()
global.LOG.Error(err)
return
}
formattedScript := fmt.Sprintf(script, tokenData, global.CONFIG.System.Web)
c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(formattedScript))
HandleLoginResponse(c, *addUser.UID)
} else {
user, err := userService.QueryUserById(userSocial.UserID)
if err != nil {
global.LOG.Error(err)
return
}
res, data := HandelUserLogin(user)
if !res {
return
}
tokenData, err := json.Marshal(data)
if err != nil {
global.LOG.Error(err)
return
}
formattedScript := fmt.Sprintf(script, tokenData, global.CONFIG.System.Web)
c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(formattedScript))
HandleLoginResponse(c, *userSocial.UserID)
}
return
}
}

View File

@@ -148,22 +148,48 @@ func (OAuthAPI) Callback(c *gin.Context) {
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
return
}
// 通过 code, 获取 token
var tokenAuthUrl = GetTokenAuthUrl(code)
var token *Token
if token, err = GetToken(tokenAuthUrl); err != nil {
global.LOG.Error(err)
return
}
// 通过token获取用户信息
var userInfo map[string]interface{}
if userInfo, err = GetUserInfo(token); err != nil {
global.LOG.Error(err)
// 使用channel来接收异步操作的结果
tokenChan := make(chan *Token)
userInfoChan := make(chan map[string]interface{})
errChan := make(chan error)
// 异步获取token
go func() {
var tokenAuthUrl = GetTokenAuthUrl(code)
token, err := GetToken(tokenAuthUrl)
if err != nil {
errChan <- err
return
}
//json 转 struct
userInfoBytes, err := json.Marshal(userInfo)
tokenChan <- token
}()
// 异步获取用户信息
go func() {
token := <-tokenChan
if token == nil {
return
}
userInfo, err := GetUserInfo(token)
if err != nil {
errChan <- err
return
}
userInfoChan <- userInfo
}()
select {
case err = <-errChan:
global.LOG.Error(err)
return
case userInfo := <-userInfoChan:
if userInfo == nil {
global.LOG.Error(<-errChan)
return
}
// 继续处理用户信息
userInfoBytes, err := json.Marshal(<-userInfoChan)
if err != nil {
global.LOG.Error(err)
return
@@ -177,6 +203,17 @@ func (OAuthAPI) Callback(c *gin.Context) {
Id := strconv.Itoa(gitHubUser.ID)
userSocial, err := userSocialService.QueryUserSocialByUUID(Id, enum.OAuthSourceGithub)
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
db := global.DB
tx := db.Begin() // 开始事务
if tx.Error != nil {
global.LOG.Error(tx.Error)
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// 第一次登录,创建用户
uid := idgen.NextId()
uidStr := strconv.FormatInt(uid, 10)
@@ -190,57 +227,37 @@ func (OAuthAPI) Callback(c *gin.Context) {
}
addUser, err := userService.AddUser(user)
if err != nil {
tx.Rollback()
global.LOG.Error(err)
return
}
github := enum.OAuthSourceGithub
userSocial = model.ScaAuthUserSocial{
UserID: &addUser.ID,
UserID: &uidStr,
UUID: &Id,
Source: &github,
}
err = userSocialService.AddUserSocial(userSocial)
if err != nil {
tx.Rollback()
global.LOG.Error(err)
return
}
userRole := model.ScaAuthUserRole{
UserID: uidStr,
RoleID: enum.User,
}
err = userRoleService.AddUserRole(userRole)
_, err = global.Casbin.AddRoleForUser(uidStr, enum.User)
if err != nil {
tx.Rollback()
global.LOG.Error(err)
return
}
res, data := HandelUserLogin(addUser)
if !res {
return
}
tokenData, err := json.Marshal(data)
if err != nil {
if err := tx.Commit().Error; err != nil {
tx.Rollback()
global.LOG.Error(err)
return
}
formattedScript := fmt.Sprintf(script, tokenData, global.CONFIG.System.Web)
c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(formattedScript))
HandleLoginResponse(c, *addUser.UID)
} else {
user, err := userService.QueryUserById(userSocial.UserID)
if err != nil {
global.LOG.Error(err)
return
}
res, data := HandelUserLogin(user)
if !res {
return
}
tokenData, err := json.Marshal(data)
if err != nil {
global.LOG.Error(err)
return
}
formattedScript := fmt.Sprintf(script, tokenData, global.CONFIG.System.Web)
c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(formattedScript))
HandleLoginResponse(c, *userSocial.UserID)
}
return
}
}

View File

@@ -2,10 +2,13 @@ package oauth_api
import (
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"schisandra-cloud-album/api/user_api/dto"
"schisandra-cloud-album/common/constant"
"schisandra-cloud-album/common/redis"
"schisandra-cloud-album/model"
"schisandra-cloud-album/global"
"schisandra-cloud-album/service"
"schisandra-cloud-album/utils"
"time"
@@ -14,11 +17,7 @@ import (
type OAuthAPI struct{}
var userService = service.Service.UserService
var userRoleService = service.Service.UserRoleService
var userSocialService = service.Service.UserSocialService
var rolePermissionService = service.Service.RolePermissionService
var permissionServiceService = service.Service.PermissionService
var roleService = service.Service.RoleService
type Token struct {
AccessToken string `json:"access_token"`
@@ -31,50 +30,82 @@ var script = `
</script>
`
func HandleLoginResponse(c *gin.Context, uid string) {
res, data := HandelUserLogin(uid)
if !res {
return
}
tokenData, err := json.Marshal(data)
if err != nil {
global.LOG.Error(err)
return
}
formattedScript := fmt.Sprintf(script, tokenData, global.CONFIG.System.Web)
c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(formattedScript))
return
}
// HandelUserLogin 处理用户登录
func HandelUserLogin(user model.ScaAuthUser) (bool, map[string]interface{}) {
ids, err := userRoleService.GetUserRoleIdsByUserId(user.ID)
func HandelUserLogin(userId string) (bool, map[string]interface{}) {
// 使用goroutine生成accessToken
accessTokenChan := make(chan string)
errChan := make(chan error)
go func() {
accessToken, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: &userId})
if err != nil {
errChan <- err
return
}
accessTokenChan <- accessToken
}()
// 使用goroutine生成refreshToken
refreshTokenChan := make(chan string)
expiresAtChan := make(chan int64)
go func() {
refreshToken, expiresAt := utils.GenerateRefreshToken(utils.RefreshJWTPayload{UserID: &userId}, time.Hour*24*7)
refreshTokenChan <- refreshToken
expiresAtChan <- expiresAt
}()
// 等待accessToken和refreshToken生成完成
var accessToken string
var refreshToken string
var expiresAt int64
var err error
select {
case accessToken = <-accessTokenChan:
case err = <-errChan:
global.LOG.Error(err)
return false, nil
}
permissionIds := rolePermissionService.QueryPermissionIdsByRoleId(ids)
permissions, err := permissionServiceService.GetPermissionsByIds(permissionIds)
if err != nil {
return false, nil
select {
case refreshToken = <-refreshTokenChan:
case expiresAt = <-expiresAtChan:
}
serializedPermissions, err := json.Marshal(permissions)
if err != nil {
return false, nil
}
wrong := redis.Set(constant.UserAuthPermissionRedisKey+*user.UID, serializedPermissions, 0).Err()
if wrong != nil {
return false, nil
}
roleList, err := roleService.GetRoleListByIds(ids)
if err != nil {
return false, nil
}
serializedRoleList, err := json.Marshal(roleList)
if err != nil {
return false, nil
}
er := redis.Set(constant.UserAuthRoleRedisKey+*user.UID, serializedRoleList, 0).Err()
if er != nil {
return false, nil
}
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{
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresAt: expiresAt,
UID: user.UID,
UID: &userId,
}
fail := redis.Set(constant.UserLoginTokenRedisKey+*user.UID, data, time.Hour*24*7).Err()
// 使用goroutine将数据存入redis
redisErrChan := make(chan error)
go func() {
fail := redis.Set(constant.UserLoginTokenRedisKey+userId, data, time.Hour*24*7).Err()
if fail != nil {
redisErrChan <- fail
return
}
redisErrChan <- nil
}()
// 等待redis操作完成
redisErr := <-redisErrChan
if redisErr != nil {
global.LOG.Error(redisErr)
return false, nil
}
responseData := map[string]interface{}{

View File

@@ -173,23 +173,61 @@ func (OAuthAPI) QQCallback(c *gin.Context) {
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
return
}
// 通过 code, 获取 token
var tokenAuthUrl = GetQQTokenAuthUrl(code)
tokenChan := make(chan *QQToken)
errChan := make(chan error)
go func() {
token, err := GetQQToken(tokenAuthUrl)
if err != nil {
errChan <- err
return
}
tokenChan <- token
}()
var token *QQToken
if token, err = GetQQToken(tokenAuthUrl); err != nil {
select {
case token = <-tokenChan:
case err = <-errChan:
global.LOG.Error(err)
return
}
// 通过 token获取 openid
openIDChan := make(chan *AuthQQme)
errChan = make(chan error)
go func() {
authQQme, err := GetQQUserOpenID(token)
if err != nil {
errChan <- err
return
}
openIDChan <- authQQme
}()
var authQQme *AuthQQme
select {
case authQQme = <-openIDChan:
case err = <-errChan:
global.LOG.Error(err)
return
}
// 通过token获取用户信息
userInfoChan := make(chan map[string]interface{})
errChan = make(chan error)
go func() {
userInfo, err := GetQQUserUserInfo(token, authQQme.OpenID)
if err != nil {
errChan <- err
return
}
userInfoChan <- userInfo
}()
var userInfo map[string]interface{}
if userInfo, err = GetQQUserUserInfo(token, authQQme.OpenID); err != nil {
select {
case userInfo = <-userInfoChan:
case err = <-errChan:
global.LOG.Error(err)
return
}
@@ -205,8 +243,20 @@ func (OAuthAPI) QQCallback(c *gin.Context) {
global.LOG.Error(err)
return
}
userSocial, err := userSocialService.QueryUserSocialByOpenID(authQQme.OpenID, enum.OAuthSourceQQ)
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
db := global.DB
tx := db.Begin() // 开始事务
if tx.Error != nil {
global.LOG.Error(tx.Error)
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// 第一次登录,创建用户
uid := idgen.NextId()
uidStr := strconv.FormatInt(uid, 10)
@@ -221,59 +271,36 @@ func (OAuthAPI) QQCallback(c *gin.Context) {
}
addUser, err := userService.AddUser(user)
if err != nil {
tx.Rollback()
global.LOG.Error(err)
return
}
qq := enum.OAuthSourceQQ
userSocial = model.ScaAuthUserSocial{
UserID: &addUser.ID,
UserID: &uidStr,
OpenID: &authQQme.OpenID,
Source: &qq,
}
err = userSocialService.AddUserSocial(userSocial)
if err != nil {
tx.Rollback()
global.LOG.Error(err)
return
}
userRole := model.ScaAuthUserRole{
UserID: uidStr,
RoleID: enum.User,
}
err = userRoleService.AddUserRole(userRole)
_, err = global.Casbin.AddRoleForUser(uidStr, enum.User)
if err != nil {
tx.Rollback()
global.LOG.Error(err)
return
}
res, data := HandelUserLogin(addUser)
if !res {
return
}
tokenData, err := json.Marshal(data)
if err != nil {
if err := tx.Commit().Error; err != nil {
tx.Rollback()
global.LOG.Error(err)
return
}
formattedScript := fmt.Sprintf(script, tokenData, global.CONFIG.System.Web)
c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(formattedScript))
HandleLoginResponse(c, *addUser.UID)
return
} else {
user, err := userService.QueryUserById(userSocial.UserID)
if err != nil {
global.LOG.Error(err)
return
HandleLoginResponse(c, *userSocial.UserID)
}
res, data := HandelUserLogin(user)
if !res {
return
}
tokenData, err := json.Marshal(data)
if err != nil {
global.LOG.Error(err)
return
}
formattedScript := fmt.Sprintf(script, tokenData, global.CONFIG.System.Web)
c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(formattedScript))
return
}
}

View File

@@ -26,9 +26,12 @@ import (
"schisandra-cloud-album/utils"
"strconv"
"strings"
"sync"
"time"
)
var mu sync.Mutex
// GenerateClientId 生成客户端ID
// @Summary 生成客户端ID
// @Description 生成客户端ID
@@ -44,6 +47,9 @@ func (OAuthAPI) GenerateClientId(c *gin.Context) {
if ip == "" {
ip = c.ClientIP()
}
// 加锁
mu.Lock()
defer mu.Unlock()
// 从Redis获取客户端ID
clientId := redis.Get(constant.UserLoginClientRedisKey + ip).Val()
@@ -70,10 +76,7 @@ func (OAuthAPI) GenerateClientId(c *gin.Context) {
// @Router /api/oauth/callback_notify [POST]
func (OAuthAPI) CallbackNotify(c *gin.Context) {
rs, err := global.Wechat.Server.Notify(c.Request, func(event contract.EventInterface) interface{} {
fmt.Dump("event", event)
switch event.GetMsgType() {
case models2.CALLBACK_MSG_TYPE_EVENT:
switch event.GetEvent() {
case models.CALLBACK_EVENT_SUBSCRIBE:
@@ -122,7 +125,6 @@ func (OAuthAPI) CallbackNotify(c *gin.Context) {
println(err.Error())
return "error"
}
fmt.Dump(msg)
}
return messages.NewText("ok")
@@ -177,6 +179,7 @@ func (OAuthAPI) GetTempQrCode(c *gin.Context) {
data := response.ResponseQRCodeCreate{}
err := json.Unmarshal([]byte(qrcode), &data)
if err != nil {
global.LOG.Error(err)
return
}
result.OK(ginI18n.MustGetMessage(c, "QRCodeGetSuccess"), data.Url, c)
@@ -184,17 +187,20 @@ func (OAuthAPI) GetTempQrCode(c *gin.Context) {
}
data, err := global.Wechat.QRCode.Temporary(c.Request.Context(), clientId, 30*24*3600)
if err != nil {
global.LOG.Error(err)
result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c)
return
}
serializedData, err := json.Marshal(data)
if err != nil {
global.LOG.Error(err)
result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c)
return
}
wrong := redis.Set(constant.UserLoginQrcodeRedisKey+ip+":"+clientId, serializedData, time.Hour*24*30).Err()
if wrong != nil {
global.LOG.Error(wrong)
result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c)
return
}
@@ -208,45 +214,98 @@ func wechatLoginHandler(openId string, clientId string) bool {
}
authUserSocial, err := userSocialService.QueryUserSocialByOpenID(openId, enum.OAuthSourceWechat)
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
tx := global.DB.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
uid := idgen.NextId()
uidStr := strconv.FormatInt(uid, 10)
createUser := model.ScaAuthUser{
UID: &uidStr,
Username: &openId,
}
// 异步添加用户
addUserChan := make(chan *model.ScaAuthUser, 1)
errChan := make(chan error, 1)
go func() {
addUser, err := userService.AddUser(createUser)
if err != nil {
errChan <- err
return
}
addUserChan <- &addUser
}()
var addUser *model.ScaAuthUser
select {
case addUser = <-addUserChan:
case err := <-errChan:
tx.Rollback()
global.LOG.Error(err)
return false
}
wechat := enum.OAuthSourceWechat
userSocial := model.ScaAuthUserSocial{
UserID: &addUser.ID,
UserID: &uidStr,
OpenID: &openId,
Source: &wechat,
}
// 异步添加用户社交信息
wrongChan := make(chan error, 1)
go func() {
wrong := userSocialService.AddUserSocial(userSocial)
wrongChan <- wrong
}()
select {
case wrong := <-wrongChan:
if wrong != nil {
tx.Rollback()
global.LOG.Error(wrong)
return false
}
userRole := model.ScaAuthUserRole{
UserID: uidStr,
RoleID: enum.User,
}
e := userRoleService.AddUserRole(userRole)
if e != nil {
// 异步添加角色
roleErrChan := make(chan error, 1)
go func() {
_, err := global.Casbin.AddRoleForUser(uidStr, enum.User)
roleErrChan <- err
}()
select {
case err := <-roleErrChan:
if err != nil {
tx.Rollback()
global.LOG.Error(err)
return false
}
res := handelUserLogin(addUser, clientId)
}
// 异步处理用户登录
resChan := make(chan bool, 1)
go func() {
res := handelUserLogin(*addUser.UID, clientId)
resChan <- res
}()
select {
case res := <-resChan:
if !res {
tx.Rollback()
return false
}
}
tx.Commit()
return true
} else {
user, err := userService.QueryUserById(authUserSocial.UserID)
if err != nil {
return false
}
res := handelUserLogin(user, clientId)
res := handelUserLogin(*authUserSocial.UserID, clientId)
if !res {
return false
}
@@ -255,50 +314,26 @@ func wechatLoginHandler(openId string, clientId string) bool {
}
// handelUserLogin 处理用户登录
func handelUserLogin(user model.ScaAuthUser, clientId string) bool {
ids, err := userRoleService.GetUserRoleIdsByUserId(user.ID)
func handelUserLogin(userId string, clientId string) bool {
resultChan := make(chan bool, 1)
go func() {
accessToken, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: &userId})
if err != nil {
return false
resultChan <- false
return
}
permissionIds := rolePermissionService.QueryPermissionIdsByRoleId(ids)
permissions, err := permissionServiceService.GetPermissionsByIds(permissionIds)
if err != nil {
return false
}
serializedPermissions, err := json.Marshal(permissions)
if err != nil {
return false
}
wrong := redis.Set(constant.UserAuthPermissionRedisKey+*user.UID, serializedPermissions, 0).Err()
if wrong != nil {
return false
}
roleList, err := roleService.GetRoleListByIds(ids)
if err != nil {
return false
}
serializedRoleList, err := json.Marshal(roleList)
if err != nil {
return false
}
er := redis.Set(constant.UserAuthRoleRedisKey+*user.UID, serializedRoleList, 0).Err()
if er != nil {
return false
}
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)
refreshToken, expiresAt := utils.GenerateRefreshToken(utils.RefreshJWTPayload{UserID: &userId}, time.Hour*24*7)
data := dto.ResponseData{
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresAt: expiresAt,
UID: user.UID,
UID: &userId,
}
fail := redis.Set(constant.UserLoginTokenRedisKey+*user.UID, data, time.Hour*24*7).Err()
fail := redis.Set(constant.UserLoginTokenRedisKey+userId, data, time.Hour*24*7).Err()
if fail != nil {
return false
resultChan <- false
return
}
responseData := map[string]interface{}{
"code": 0,
@@ -308,17 +343,18 @@ func handelUserLogin(user model.ScaAuthUser, clientId string) bool {
}
tokenData, err := json.Marshal(responseData)
if err != nil {
return false
resultChan <- false
return
}
// gws方式发送消息
err = websocket_api.Handler.SendMessageToClient(clientId, tokenData)
if err != nil {
return false
global.LOG.Error(err)
resultChan <- false
return
}
// gorilla websocket方式发送消息
//res := websocket_api.SendMessageData(clientId, responseData)
//if !res {
// return false
//}
return true
resultChan <- true
}()
return <-resultChan
}

View File

@@ -0,0 +1,11 @@
package dto
import "schisandra-cloud-album/model"
type AddPermissionRequestDto struct {
Permissions []model.ScaAuthPermission `json:"permissions"`
}
type AddPermissionToRoleRequestDto struct {
RoleKey string `json:"role_key"`
Permissions []model.ScaAuthPermission `json:"permissions"`
}

View File

@@ -0,0 +1,3 @@
package permission_api
type PermissionAPI struct{}

View File

@@ -0,0 +1,36 @@
package permission_api
import (
ginI18n "github.com/gin-contrib/i18n"
"github.com/gin-gonic/gin"
"schisandra-cloud-album/api/permission_api/dto"
"schisandra-cloud-album/common/result"
"schisandra-cloud-album/global"
"schisandra-cloud-album/service"
)
var permissionService = service.Service.PermissionService
// AddPermissions 批量添加权限
// @Summary 批量添加权限
// @Description 批量添加权限
// @Tags 权限管理
// @Accept json
// @Produce json
// @Param permissions body dto.AddPermissionRequestDto true "权限列表"
// @Router /api/auth/permission/add [post]
func (PermissionAPI) AddPermissions(c *gin.Context) {
addPermissionRequestDto := dto.AddPermissionRequestDto{}
err := c.ShouldBindJSON(&addPermissionRequestDto)
if err != nil {
return
}
err = permissionService.CreatePermissions(addPermissionRequestDto.Permissions)
if err != nil {
global.LOG.Error(err)
result.FailWithMessage(ginI18n.MustGetMessage(c, "CreatedFailed"), c)
return
}
result.OkWithMessage(ginI18n.MustGetMessage(c, "CreatedSuccess"), c)
return
}

View File

@@ -0,0 +1,11 @@
package dto
type RoleRequestDto struct {
RoleName string `json:"role_name"`
RoleKey string `json:"role_key"`
}
type AddRoleToUserRequestDto struct {
Uid string `json:"uid"`
RoleKey string `json:"role_key"`
}

View File

@@ -1 +1,64 @@
package role_api
import (
ginI18n "github.com/gin-contrib/i18n"
"github.com/gin-gonic/gin"
"schisandra-cloud-album/api/role_api/dto"
"schisandra-cloud-album/common/result"
"schisandra-cloud-album/global"
"schisandra-cloud-album/model"
"schisandra-cloud-album/service"
)
var roleService = service.Service.RoleService
// CreateRole 创建角色
// @Summary 创建角色
// @Description 创建角色
// @Tags 角色
// @Accept json
// @Produce json
// @Param roleRequestDto body dto.RoleRequestDto true "角色信息"
// @Router /api/auth/role/create [post]
func (RoleAPI) CreateRole(c *gin.Context) {
roleRequestDto := dto.RoleRequestDto{}
err := c.ShouldBindJSON(&roleRequestDto)
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "CreatedFailed"), c)
return
}
role := model.ScaAuthRole{
RoleName: roleRequestDto.RoleName,
RoleKey: roleRequestDto.RoleKey,
}
err = roleService.AddRole(role)
if err != nil {
global.LOG.Error(err)
result.FailWithMessage(ginI18n.MustGetMessage(c, "CreatedFailed"), c)
return
}
result.OkWithMessage(ginI18n.MustGetMessage(c, "CreatedSuccess"), c)
}
// AddRoleToUser 给指定用户添加角色
// @Summary 给指定用户添加角色
// @Description 给指定用户添加角色
// @Tags 角色
// @Accept json
// @Produce json
// @Param addRoleToUserRequestDto body dto.AddRoleToUserRequestDto true "给指定用户添加角色"
// @Router /api/auth/role/add_role_to_user [post]
func (RoleAPI) AddRoleToUser(c *gin.Context) {
addRoleToUserRequestDto := dto.AddRoleToUserRequestDto{}
err := c.ShouldBindJSON(&addRoleToUserRequestDto)
if err != nil {
global.LOG.Error(err)
return
}
user, err := global.Casbin.AddRoleForUser(addRoleToUserRequestDto.Uid, addRoleToUserRequestDto.RoleKey)
if err != nil {
global.LOG.Error(err)
return
}
result.OkWithData(user, c)
}

View File

@@ -1,7 +1,6 @@
package user_api
import (
"encoding/json"
ginI18n "github.com/gin-contrib/i18n"
"github.com/gin-gonic/gin"
"github.com/yitter/idgenerator-go/idgen"
@@ -20,10 +19,6 @@ import (
)
var userService = service.Service.UserService
var userRoleService = service.Service.UserRoleService
var rolePermissionService = service.Service.RolePermissionService
var permissionServiceService = service.Service.PermissionService
var roleService = service.Service.RoleService
// GetUserList
// @Summary 获取所有用户列表
@@ -146,12 +141,8 @@ func (UserAPI) AddUser(c *gin.Context) {
result.FailWithMessage(ginI18n.MustGetMessage(c, "AddUserError"), c)
return
}
userRole := model.ScaAuthUserRole{
UserID: uidStr,
RoleID: enum.User,
}
e := userRoleService.AddUserRole(userRole)
if e != nil {
_, err = global.Casbin.AddRoleForUser(uidStr, enum.User)
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "AddUserRoleError"), c)
return
}
@@ -258,11 +249,30 @@ func (UserAPI) PhoneLogin(c *gin.Context) {
return
}
// 异步查询用户信息
userChan := make(chan *model.ScaAuthUser)
go func() {
user := userService.QueryUserByPhone(phone)
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
// 未注册
userChan <- &user
}()
// 异步获取验证码
codeChan := make(chan string)
go func() {
code := redis.Get(constant.UserLoginSmsRedisKey + phone)
if code == nil {
codeChan <- ""
} else {
codeChan <- code.Val()
}
}()
user := <-userChan
code := <-codeChan
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
// 未注册
if code == "" {
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
return
} else {
@@ -277,35 +287,29 @@ func (UserAPI) PhoneLogin(c *gin.Context) {
result.FailWithMessage(ginI18n.MustGetMessage(c, "RegisterUserError"), c)
return
}
userRole := model.ScaAuthUserRole{
UserID: uidStr,
RoleID: enum.User,
}
e := userRoleService.AddUserRole(userRole)
if e != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
_, err = global.Casbin.AddRoleForUser(uidStr, enum.User)
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "RegisterUserError"), c)
return
}
handelUserLogin(addUser, request.AutoLogin, c)
return
}
} else {
code := redis.Get(constant.UserLoginSmsRedisKey + phone)
if code == nil {
if code == "" {
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
return
} else {
if captcha != code.Val() {
if captcha != code {
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaError"), c)
return
} else {
handelUserLogin(user, request.AutoLogin, c)
handelUserLogin(*user, request.AutoLogin, c)
return
}
}
}
}
// RefreshHandler 刷新token
@@ -333,7 +337,7 @@ func (UserAPI) RefreshHandler(c *gin.Context) {
return
}
if isUpd {
accessTokenString, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: parseRefreshToken.UserID, RoleID: parseRefreshToken.RoleID})
accessTokenString, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: parseRefreshToken.UserID})
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
return
@@ -362,42 +366,7 @@ func (UserAPI) RefreshHandler(c *gin.Context) {
// handelUserLogin 处理用户登录
func handelUserLogin(user model.ScaAuthUser, autoLogin bool, c *gin.Context) {
ids, err := userRoleService.GetUserRoleIdsByUserId(user.ID)
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
return
}
permissionIds := rolePermissionService.QueryPermissionIdsByRoleId(ids)
permissions, err := permissionServiceService.GetPermissionsByIds(permissionIds)
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
return
}
serializedPermissions, err := json.Marshal(permissions)
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
return
}
wrong := redis.Set(constant.UserAuthPermissionRedisKey+*user.UID, serializedPermissions, 0).Err()
if wrong != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
return
}
roleList, err := roleService.GetRoleListByIds(ids)
if err != nil {
return
}
serializedRoleList, err := json.Marshal(roleList)
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
return
}
er := redis.Set(constant.UserAuthRoleRedisKey+*user.UID, serializedRoleList, 0).Err()
if er != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
return
}
accessToken, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: user.UID, RoleID: ids})
accessToken, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: user.UID})
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
return
@@ -408,7 +377,7 @@ func handelUserLogin(user model.ScaAuthUser, autoLogin bool, c *gin.Context) {
} else {
days = time.Hour * 24 * 1
}
refreshToken, expiresAt := utils.GenerateRefreshToken(utils.RefreshJWTPayload{UserID: user.UID, RoleID: ids}, days)
refreshToken, expiresAt := utils.GenerateRefreshToken(utils.RefreshJWTPayload{UserID: user.UID}, days)
data := dto.ResponseData{
AccessToken: accessToken,
RefreshToken: refreshToken,

View File

@@ -12,8 +12,6 @@ func MakeMigration() {
&model.ScaAuthUser{},
&model.ScaAuthPermission{},
&model.ScaAuthRole{},
&model.ScaAuthRolePermission{},
&model.ScaAuthUserRole{},
&model.ScaAuthUserDevice{},
&model.ScaAuthUserSocial{},
)

View File

@@ -1,7 +1,7 @@
package enum
var (
SuperAdmin int64 = 1
Admin int64 = 2
User int64 = 3
Root string = "root"
Admin string = "admin"
User string = "user"
)

10
config/conf_casbin.go Normal file
View File

@@ -0,0 +1,10 @@
package config
type Casbin struct {
// 权限模型文件路径
ModelPath string `yaml:"model-path"`
// 数据库前缀
TablePrefix string `yaml:"table-prefix"`
// 数据库表明
TableName string `yaml:"table-name"`
}

View File

@@ -6,5 +6,5 @@ type Logger struct {
Prefix string `yaml:"prefix"` // 日志前缀
Director string `yaml:"director"` // 日志文件存放目录
ShowLine bool `yaml:"show-line"` // 是否显示文件行号
LogInConsole string `yaml:"log-in-console"` // 是否在控制台打印日志
LogName string `yaml:"log-name"` // 日志文件名
}

View File

@@ -8,6 +8,7 @@ type System struct {
Port string `yaml:"port"` //端口号
Env string `yaml:"env"` //环境
Web string `yaml:"web"` //web地址
Ip string `yaml:"ip"` //ip地址
}
func (s *System) Addr() string {

View File

@@ -11,4 +11,5 @@ type Config struct {
Wechat Wechat `yaml:"wechat"`
OAuth OAuth `yaml:"oauth"`
Swagger Swagger `yaml:"swagger"`
Casbin Casbin `yaml:"casbin"`
}

View File

@@ -11,7 +11,6 @@ import (
"github.com/wenlng/go-captcha/v2/click"
"github.com/wenlng/go-captcha/v2/rotate"
"github.com/wenlng/go-captcha/v2/slide"
"log"
"schisandra-cloud-album/global"
)
@@ -47,13 +46,13 @@ func initTextCaptcha() {
// fonts
fonts, err := fzshengsksjw.GetFont()
if err != nil {
log.Fatalln(err)
global.LOG.Fatalln(err)
}
// background images
imgs, err := images.GetImages()
if err != nil {
log.Fatalln(err)
global.LOG.Fatalln(err)
}
// thumb images
@@ -119,13 +118,13 @@ func initClickShapeCaptcha() {
// click.WithUseShapeOriginalColor(false) -> Random rewriting of graphic colors
shapeMaps, err := shapes.GetShapes()
if err != nil {
log.Fatalln(err)
global.LOG.Fatalln(err)
}
// background images
imgs, err := images.GetImages()
if err != nil {
log.Fatalln(err)
global.LOG.Fatalln(err)
}
// set resources
@@ -146,12 +145,12 @@ func initsSlideCaptcha() {
// background images
imgs, err := images.GetImages()
if err != nil {
log.Fatalln(err)
global.LOG.Fatalln(err)
}
graphs, err := tiles.GetTiles()
if err != nil {
log.Fatalln(err)
global.LOG.Fatalln(err)
}
var newGraphs = make([]*slide.GraphImage, 0, len(graphs))
@@ -182,7 +181,7 @@ func initRotateCaptcha() {
// background images
imgs, err := images.GetImages()
if err != nil {
log.Fatalln(err)
global.LOG.Fatalln(err)
}
// set resources
@@ -203,12 +202,12 @@ func initSlideRegionCaptcha() {
// background image
imgs, err := images.GetImages()
if err != nil {
log.Fatalln(err)
global.LOG.Fatalln(err)
}
graphs, err := tiles.GetTiles()
if err != nil {
log.Fatalln(err)
global.LOG.Fatalln(err)
}
var newGraphs = make([]*slide.GraphImage, 0, len(graphs))
for i := 0; i < len(graphs); i++ {

View File

@@ -14,13 +14,22 @@ var (
func InitCasbin() {
once.Do(func() {
adapter, _ := gormadapter.NewAdapterByDBUseTableName(global.DB, "sca_sys_", "casbin_rule")
m, err := model.NewModelFromFile("config/rbac_model.pml")
adapter, err := gormadapter.NewAdapterByDBUseTableName(global.DB, global.CONFIG.Casbin.TablePrefix, global.CONFIG.Casbin.TableName)
if err != nil {
global.LOG.Error(err.Error())
panic(err)
}
e, _ := casbin.NewCachedEnforcer(m, adapter)
m, err := model.NewModelFromFile(global.CONFIG.Casbin.ModelPath)
if err != nil {
global.LOG.Error(err.Error())
panic(err)
}
e, err := casbin.NewCachedEnforcer(m, adapter)
if err != nil {
global.LOG.Error(err.Error())
panic(err)
}
e.EnableCache(true)
e.SetExpireTime(60 * 60)
err = e.LoadPolicy()
if err != nil {

View File

@@ -34,8 +34,9 @@ func MySQlConnect() *gorm.DB {
})
} else {
logfile, _ := os.OpenFile("/tmp/logs/mysql.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
mysqlLogger = logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
log.New(logfile, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Second, //慢sql日志
LogLevel: logger.Error, //级别
@@ -47,11 +48,15 @@ func MySQlConnect() *gorm.DB {
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: mysqlLogger,
PrepareStmt: true,
})
if err != nil {
global.LOG.Fatalf(fmt.Sprintf("[%s] MySQL 连接失败", dsn))
}
sqlDB, _ := db.DB()
sqlDB, err := db.DB()
if err != nil {
global.LOG.Fatalf(fmt.Sprintf("[%s] MySQL 获取DB失败", dsn))
}
sqlDB.SetMaxIdleConns(global.CONFIG.MySQL.MaxIdleConnes)
sqlDB.SetMaxOpenConns(global.CONFIG.MySQL.MaxOpenConnes)
sqlDB.SetConnMaxLifetime(time.Hour * 4) //连接最大复用时间

View File

@@ -1,80 +1,24 @@
package core
import (
"bytes"
"fmt"
"github.com/sirupsen/logrus"
"os"
"path"
"schisandra-cloud-album/global"
"schisandra-cloud-album/middleware"
"time"
)
const (
red = 31
yellow = 33
blue = 34
gray = 37
)
type LogFormatter struct{}
func (f *LogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
//根据不同的level显示不同的颜色
var levelColor int
switch entry.Level {
case logrus.DebugLevel, logrus.TraceLevel:
levelColor = gray
case logrus.WarnLevel:
levelColor = yellow
case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel:
levelColor = red
default:
levelColor = blue
}
var b *bytes.Buffer
if entry.Buffer != nil {
b = entry.Buffer
} else {
b = &bytes.Buffer{}
}
log := global.CONFIG.Logger
// 自定义日期格式
timestamp := entry.Time.Format("2006-01-02 15:04:05")
if entry.HasCaller() {
//自定义文件路径
funcVal := entry.Caller.Function
fileVal := fmt.Sprintf("%s:%d", path.Base(entry.Caller.File), entry.Caller.Line)
//自定义输出格式
fmt.Fprintf(b, "%s[%s] \x1b[%dm[%s]\x1b[0m %s %s : %s\n", log.Prefix, timestamp, levelColor, entry.Level, fileVal, funcVal, entry.Message)
} else {
fmt.Fprintf(b, "%s[%s] \x1b[%dm[%s]\x1b[0m : %s\n", log.Prefix, timestamp, levelColor, entry.Level, entry.Message)
}
return b.Bytes(), nil
}
func InitLogger() *logrus.Logger {
newLog := logrus.New()
newLog.SetOutput(os.Stdout) //设置输出类型
newLog.SetReportCaller(global.CONFIG.Logger.ShowLine) //设置是否显示函数名和行号
newLog.SetFormatter(&LogFormatter{}) //设置日志格式
func InitLogger() {
// 按照日期格式化输出
mylog := middleware.NewDateLog(&middleware.DateLogConfig{
Date: time.Now().Format("2006-01-02"),
Path: global.CONFIG.Logger.Director,
Name: global.CONFIG.Logger.LogName,
})
log := mylog.Init()
level, err := logrus.ParseLevel(global.CONFIG.Logger.Level)
if err != nil {
level = logrus.InfoLevel
}
newLog.SetLevel(level) //设置日志级别
global.LOG = newLog
initDefaultLogger()
return newLog
}
func initDefaultLogger() {
//全局日志
logrus.SetOutput(os.Stdout) //设置输出类型
logrus.SetReportCaller(global.CONFIG.Logger.ShowLine) //设置是否显示函数名和行号
logrus.SetFormatter(&LogFormatter{}) //设置日志格式
level, err := logrus.ParseLevel(global.CONFIG.Logger.Level)
if err != nil {
level = logrus.InfoLevel
}
logrus.SetLevel(level) //设置日志级别
log.SetLevel(level) //设置日志级别
global.LOG = log
}

View File

@@ -13,10 +13,10 @@ func InitWechat() {
Secret: global.CONFIG.Wechat.AppSecret,
Token: global.CONFIG.Wechat.Token,
AESKey: global.CONFIG.Wechat.AESKey,
//Log: officialAccount.Log{
// Level: "debug",
// File: "./wechat.log",
//},
Log: officialAccount.Log{
Level: "error",
File: "./wechat.log",
},
ResponseType: os.Getenv("response_type"),
HttpDebug: true,
Debug: true,
@@ -27,6 +27,7 @@ func InitWechat() {
}),
})
if err != nil {
global.LOG.Error(err.Error())
panic(err)
}
global.Wechat = OfficialAccountApp

View File

@@ -15,6 +15,60 @@ const docTemplate = `{
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/api/auth/role/add_role_to_user": {
"post": {
"description": "给指定用户添加角色",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"角色"
],
"summary": "给指定用户添加角色",
"parameters": [
{
"description": "给指定用户添加角色",
"name": "addRoleToUserRequestDto",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.AddRoleToUserRequestDto"
}
}
],
"responses": {}
}
},
"/api/auth/role/create": {
"post": {
"description": "创建角色",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"角色"
],
"summary": "创建角色",
"parameters": [
{
"description": "角色信息",
"name": "roleRequestDto",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.RoleRequestDto"
}
}
],
"responses": {}
}
},
"/api/auth/user/List": {
"get": {
"tags": [
@@ -735,6 +789,17 @@ const docTemplate = `{
}
}
},
"dto.AddRoleToUserRequestDto": {
"type": "object",
"properties": {
"role_key": {
"type": "string"
},
"uid": {
"type": "string"
}
}
},
"dto.AddUserRequest": {
"type": "object",
"properties": {
@@ -779,6 +844,17 @@ const docTemplate = `{
"type": "string"
}
}
},
"dto.RoleRequestDto": {
"type": "object",
"properties": {
"role_key": {
"type": "string"
},
"role_name": {
"type": "string"
}
}
}
}
}`

View File

@@ -4,6 +4,60 @@
"contact": {}
},
"paths": {
"/api/auth/role/add_role_to_user": {
"post": {
"description": "给指定用户添加角色",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"角色"
],
"summary": "给指定用户添加角色",
"parameters": [
{
"description": "给指定用户添加角色",
"name": "addRoleToUserRequestDto",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.AddRoleToUserRequestDto"
}
}
],
"responses": {}
}
},
"/api/auth/role/create": {
"post": {
"description": "创建角色",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"角色"
],
"summary": "创建角色",
"parameters": [
{
"description": "角色信息",
"name": "roleRequestDto",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.RoleRequestDto"
}
}
],
"responses": {}
}
},
"/api/auth/user/List": {
"get": {
"tags": [
@@ -724,6 +778,17 @@
}
}
},
"dto.AddRoleToUserRequestDto": {
"type": "object",
"properties": {
"role_key": {
"type": "string"
},
"uid": {
"type": "string"
}
}
},
"dto.AddUserRequest": {
"type": "object",
"properties": {
@@ -768,6 +833,17 @@
"type": "string"
}
}
},
"dto.RoleRequestDto": {
"type": "object",
"properties": {
"role_key": {
"type": "string"
},
"role_name": {
"type": "string"
}
}
}
}
}

View File

@@ -8,6 +8,13 @@ definitions:
password:
type: string
type: object
dto.AddRoleToUserRequestDto:
properties:
role_key:
type: string
uid:
type: string
type: object
dto.AddUserRequest:
properties:
password:
@@ -37,9 +44,52 @@ definitions:
repassword:
type: string
type: object
dto.RoleRequestDto:
properties:
role_key:
type: string
role_name:
type: string
type: object
info:
contact: {}
paths:
/api/auth/role/add_role_to_user:
post:
consumes:
- application/json
description: 给指定用户添加角色
parameters:
- description: 给指定用户添加角色
in: body
name: addRoleToUserRequestDto
required: true
schema:
$ref: '#/definitions/dto.AddRoleToUserRequestDto'
produces:
- application/json
responses: {}
summary: 给指定用户添加角色
tags:
- 角色
/api/auth/role/create:
post:
consumes:
- application/json
description: 创建角色
parameters:
- description: 角色信息
in: body
name: roleRequestDto
required: true
schema:
$ref: '#/definitions/dto.RoleRequestDto'
produces:
- application/json
responses: {}
summary: 创建角色
tags:
- 角色
/api/auth/user/List:
get:
responses:

8
go.mod
View File

@@ -6,12 +6,16 @@ require (
github.com/ArtisanCloud/PowerLibs/v3 v3.2.5
github.com/ArtisanCloud/PowerWeChat/v3 v3.2.38
github.com/BurntSushi/toml v1.3.2
github.com/casbin/casbin/v2 v2.98.0
github.com/casbin/gorm-adapter/v3 v3.28.0
github.com/gin-contrib/cors v1.7.2
github.com/gin-contrib/i18n v1.1.3
github.com/gin-gonic/gin v1.10.0
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/gorilla/websocket v1.5.3
github.com/juju/ratelimit v1.0.2
github.com/lxzan/gws v1.8.5
github.com/pkg6/go-sms v0.1.2
github.com/redis/go-redis/v9 v9.6.1
github.com/satori/go.uuid v1.2.0
@@ -37,8 +41,6 @@ require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/bytedance/sonic v1.12.0 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/casbin/casbin/v2 v2.98.0 // indirect
github.com/casbin/gorm-adapter/v3 v3.28.0 // indirect
github.com/casbin/govaluate v1.2.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
@@ -71,11 +73,9 @@ require (
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/juju/ratelimit v1.0.2 // indirect
github.com/klauspost/compress v1.17.5 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lxzan/gws v1.8.5 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/microsoft/go-mssqldb v1.6.0 // indirect

22
go.sum
View File

@@ -9,14 +9,20 @@ github.com/ArtisanCloud/PowerWeChat/v3 v3.2.38/go.mod h1:9CbKc6nODhoM8TVjoXqujrA
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 h1:/iHxaJhsFr0+xVFfbMr5vxz848jyiWuIEDhYq3y5odY=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0 h1:yfJe15aSwEQ6Oo6J+gdfdulPNoZ3TEhmbhLIoxZcA+U=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0/go.mod h1:Q28U+75mpCaSCDowNEmhIo/rmgdkqmkmzI7N6TGR4UY=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0 h1:T028gtTPiYt/RMUfs8nVsAL7FDQrfLlrm/NnRG/zcC4=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA=
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0 h1:HCc0+LpPfpCKs6LGGLAhwBARt9632unrVcI6i8s/8os=
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
@@ -105,10 +111,13 @@ github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
@@ -151,9 +160,12 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lxzan/gws v1.8.5 h1:6x+wW3EHtoGFNeCtZP1OVZ1IHrpZZzDaEjQGg1lUJqU=
github.com/lxzan/gws v1.8.5/go.mod h1:FcGeRMB7HwGuTvMLR24ku0Zx0p6RXqeKASeMc4VYgi4=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
@@ -163,8 +175,6 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
github.com/microsoft/go-mssqldb v1.6.0 h1:mM3gYdVwEPFrlg/Dvr2DNVEgYFG7L42l+dGc67NNNpc=
github.com/microsoft/go-mssqldb v1.6.0/go.mod h1:00mDtPbeQCRGC1HwOOR5K/gr30P1NcEG0vx6Kbv2aJU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -180,6 +190,7 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -194,9 +205,8 @@ github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJ
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230126093431-47fa9a501578 h1:VstopitMQi3hZP0fzvnsLmzXZdQGc4bEcgu24cp+d4M=
github.com/remyoudompheng/bigfft v0.0.0-20230126093431-47fa9a501578/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
@@ -337,14 +347,10 @@ gorm.io/datatypes v1.2.1/go.mod h1:hYK6OTb/1x+m96PgoZZq10UXJ6RvEBb9kRDQ2yyhzGs=
gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U=
gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A=
gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM=
gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA=
gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c=
gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I=
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
gorm.io/driver/sqlserver v1.5.3 h1:rjupPS4PVw+rjJkfvr8jn2lJ8BMhT4UW5FwuJY0P3Z0=
gorm.io/driver/sqlserver v1.5.3/go.mod h1:B+CZ0/7oFJ6tAlefsKoyxdgDCXJKSgwS2bMOQZT0I00=
gorm.io/gen v0.3.26 h1:sFf1j7vNStimPRRAtH4zz5NiHM+1dr6eA9aaRdplyhY=

View File

@@ -8,6 +8,7 @@ import (
)
func main() {
// 初始化配置
core.InitConfig() // 读取配置文件
core.InitLogger() // 初始化日志

157
middleware/logger_date.go Normal file
View File

@@ -0,0 +1,157 @@
package middleware
import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"schisandra-cloud-album/global"
"strings"
"time"
"github.com/sirupsen/logrus"
)
var (
ccRed = 1
ccYellow = 3
ccBlue = 4
ccCyan = 6
ccGray = 7
)
type LogConsoleFormatter struct{}
func (s *LogConsoleFormatter) Format(entry *logrus.Entry) ([]byte, error) {
timestamp := time.Now().Local().Format("2006-01-02 15:04:05")
var color int
switch entry.Level {
case logrus.ErrorLevel:
color = ccRed
case logrus.WarnLevel:
color = ccYellow
case logrus.InfoLevel:
color = ccBlue
case logrus.DebugLevel:
color = ccCyan
default:
color = ccGray
}
// 设置 buffer 缓冲区
var b *bytes.Buffer
if entry.Buffer == nil {
b = &bytes.Buffer{}
} else {
b = entry.Buffer
}
fileVal := fmt.Sprintf("%s:%d", filepath.Base(entry.Caller.File), entry.Caller.Line)
fmt.Fprintf(b, "[%s] \033[3%dm[%s]\033[0m [%s] %s %s\n", global.CONFIG.Logger.Prefix, color, entry.Level, timestamp, fileVal, entry.Message)
return b.Bytes(), nil
}
type LogFileFormatter struct{}
func (s *LogFileFormatter) Format(entry *logrus.Entry) ([]byte, error) {
timestamp := time.Now().Local().Format("2006-01-02 15:04:05")
var file string
var length int
if entry.Caller != nil {
file = filepath.Base(entry.Caller.File)
length = entry.Caller.Line
}
msg := fmt.Sprintf("[%s] %s [%s:%d] %s\n", strings.ToUpper(entry.Level.String()), timestamp, file, length, entry.Message)
return []byte(msg), nil
}
type ConsoleHook struct {
formatter logrus.Formatter
}
func (hook *ConsoleHook) Levels() []logrus.Level {
return logrus.AllLevels
}
func (hook *ConsoleHook) Fire(entry *logrus.Entry) error {
originalFormatter := entry.Logger.Formatter
entry.Logger.Formatter = hook.formatter
defer func() { entry.Logger.Formatter = originalFormatter }()
line, err := entry.Logger.Formatter.Format(entry)
if err != nil {
return err
}
_, err = os.Stdout.Write(line)
return err
}
type FileHook struct {
formatter logrus.Formatter
file *os.File
}
func (hook *FileHook) Levels() []logrus.Level {
return logrus.AllLevels
}
func (hook *FileHook) Fire(entry *logrus.Entry) error {
originalFormatter := entry.Logger.Formatter
entry.Logger.Formatter = hook.formatter
defer func() { entry.Logger.Formatter = originalFormatter }()
line, err := entry.Logger.Formatter.Format(entry)
if err != nil {
return err
}
_, err = hook.file.Write(line)
return err
}
type DateLogConfig struct {
Date string
Path string
Name string
}
func NewDateLog(d *DateLogConfig) *DateLogConfig {
return &DateLogConfig{
Date: d.Date,
Path: d.Path,
Name: d.Name,
}
}
func (d *DateLogConfig) Init() *logrus.Logger {
// 实例化 logrus
log := logrus.New()
// 设置是否输出文件名和行号信息
log.SetReportCaller(global.CONFIG.Logger.ShowLine)
// 将 logrus 的默认输出丢弃,确保日志只通过 hooks 输出
log.SetOutput(io.Discard)
// 控制台输出的 hook
consoleHook := &ConsoleHook{
formatter: &LogConsoleFormatter{},
}
// 添加控制台输出的 hook
log.AddHook(consoleHook)
// 文件路径
filename := fmt.Sprintf("%s/%s/%s.log", d.Path, d.Date, d.Name)
// 创建目录
if err := os.MkdirAll(fmt.Sprintf("%s/%s", d.Path, d.Date), os.ModePerm); err != nil {
log.Fatal(err)
}
// 打开文件,如果文件不存在,则创建文件
file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
if err != nil {
log.Fatal(err)
}
// 文件输出的 hook
fileHook := &FileHook{
formatter: &LogFileFormatter{},
file: file,
}
// 添加文件输出的 hook
log.AddHook(fileHook)
return log
}

116
middleware/logger_level.go Normal file
View File

@@ -0,0 +1,116 @@
package middleware
import (
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"
"github.com/sirupsen/logrus"
)
const (
allLog = "all"
errLog = "err"
warnLog = "warn"
infoLog = "info"
debugLog = "debug"
)
type LevelFormatter struct{}
func (l *LevelFormatter) Format(entry *logrus.Entry) ([]byte, error) {
timestamp := time.Now().Local().Format("2006-01-02 15:04:05")
var file string
var len int
if entry.Caller != nil {
file = filepath.Base(entry.Caller.File)
len = entry.Caller.Line
}
msg := fmt.Sprintf("[%s] %s [%s:%d] %s\n", strings.ToUpper(entry.Level.String()), timestamp, file, len, entry.Message)
return []byte(msg), nil
}
type LevelConfig struct {
Date string
Name string
Path string
}
func NewLevelLog(d *LevelConfig) *LevelConfig {
return &LevelConfig{
Date: d.Date,
Path: d.Path,
Name: d.Name,
}
}
func (l *LevelConfig) Init() *logrus.Logger {
log := logrus.New()
log.SetReportCaller(true)
log.SetOutput(io.Discard)
err := os.MkdirAll(fmt.Sprintf("%s/%s", l.Path, l.Date), os.ModePerm)
if err != nil {
log.Fatal(err)
}
allFile, _ := os.OpenFile(fmt.Sprintf("%s/%s/%s-%s.log", l.Path, l.Date, l.Name, allLog), os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
errFile, _ := os.OpenFile(fmt.Sprintf("%s/%s/%s-%s.log", l.Path, l.Date, l.Name, errLog), os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
warnFile, _ := os.OpenFile(fmt.Sprintf("%s/%s/%s-%s.log", l.Path, l.Date, l.Name, warnLog), os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
infoFile, _ := os.OpenFile(fmt.Sprintf("%s/%s/%s-%s.log", l.Path, l.Date, l.Name, infoLog), os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
debugFile, _ := os.OpenFile(fmt.Sprintf("%s/%s/%s-%s.log", l.Path, l.Date, l.Name, debugLog), os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
fileHook := &LevelHook{
formatter: &LevelFormatter{},
file: allFile,
errFile: errFile,
warnFile: warnFile,
infoFile: infoFile,
debugFile: debugFile,
}
log.AddHook(fileHook)
return log
}
type LevelHook struct {
formatter logrus.Formatter
file *os.File
errFile *os.File
warnFile *os.File
infoFile *os.File
debugFile *os.File
}
func (l *LevelHook) Levels() []logrus.Level {
return logrus.AllLevels
}
func (l *LevelHook) Fire(entry *logrus.Entry) error {
originalFormatter := entry.Logger.Formatter
entry.Logger.Formatter = l.formatter
defer func() { entry.Logger.Formatter = originalFormatter }()
line, err := entry.Logger.Formatter.Format(entry)
if err != nil {
return err
}
_, err = l.file.Write(line)
switch entry.Level {
case logrus.ErrorLevel:
_, err = l.errFile.Write(line)
case logrus.WarnLevel:
_, err = l.warnFile.Write(line)
case logrus.InfoLevel:
_, err = l.infoFile.Write(line)
case logrus.DebugLevel:
_, err = l.debugFile.Write(line)
default:
panic("unhandled default case")
}
return err
}

View File

@@ -1,24 +0,0 @@
package model
import (
"time"
)
const TableNameScaAuthRolePermission = "sca_auth_role_permission"
// ScaAuthRolePermission 角色-权限映射表
type ScaAuthRolePermission struct {
ID int64 `gorm:"column:id;type:bigint(20);primaryKey;comment:主键ID" json:"id"` // 主键ID
RoleID int64 `gorm:"column:role_id;type:bigint(20);not null;comment:角色ID" json:"role_id"` // 角色ID
PermissionID int64 `gorm:"column:permission_id;type:bigint(20);not null;comment:权限ID" json:"permission_id"` // 权限ID
CreatedBy *string `gorm:"column:created_by;type:varchar(32);comment:创建人" json:"created_by"` // 创建人
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"` // 更新人
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"` // 是否删除
}
// TableName ScaAuthRolePermission's table name
func (*ScaAuthRolePermission) TableName() string {
return TableNameScaAuthRolePermission
}

View File

@@ -1,24 +0,0 @@
package model
import (
"time"
)
const TableNameScaAuthUserRole = "sca_auth_user_role"
// ScaAuthUserRole 用户-角色映射表
type ScaAuthUserRole struct {
ID int64 `gorm:"column:id;type:bigint(20);primaryKey;comment:主键ID" json:"id"` // 主键ID
UserID string `gorm:"column:user_id;type:varchar(255);not null;comment:用户ID" json:"user_id"` // 用户ID
RoleID int64 `gorm:"column:role_id;type:bigint(20);not null;comment:角色ID" json:"role_id"` // 角色ID
CreatedBy *string `gorm:"column:created_by;type:varchar(32);comment:创建人" json:"created_by"` // 创建人
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"` // 更新人
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"` // 是否删除
}
// TableName ScaAuthUserRole's table name
func (*ScaAuthUserRole) TableName() string {
return TableNameScaAuthUserRole
}

View File

@@ -9,7 +9,7 @@ const TableNameScaAuthUserSocial = "sca_auth_user_social"
// ScaAuthUserSocial 社会用户信息表
type ScaAuthUserSocial struct {
ID int64 `gorm:"column:id;type:bigint(20);primaryKey;comment:主键ID" json:"id"` // 主键ID
UserID *int64 `gorm:"column:user_id;type:bigint(20);not null;comment:用户ID" json:"user_id"` // 用户ID
UserID *string `gorm:"column:user_id;type:varchar(255);not null;comment:用户ID" json:"user_id"` // 用户ID
UUID *string `gorm:"column:uuid;type:varchar(255);comment:第三方系统的唯一ID" json:"uuid"` // 第三方系统的唯一ID
Source *string `gorm:"column:source;type:varchar(255);comment:第三方用户来源" json:"source"` // 第三方用户来源
AccessToken *string `gorm:"column:access_token;type:varchar(255);comment:用户的授权令牌" json:"access_token"` // 用户的授权令牌

View File

@@ -14,8 +14,8 @@ func OauthRouter(router *gin.RouterGroup) {
{
wechatRouter.GET("/generate_client_id", oauth.GenerateClientId)
wechatRouter.GET("/get_temp_qrcode", oauth.GetTempQrCode)
wechatRouter.GET("/callback", oauth.CallbackVerify)
//wechatRouter.POST("/callback", oauth.CallbackNotify)
//wechatRouter.GET("/callback", oauth.CallbackVerify)
wechatRouter.POST("/callback", oauth.CallbackNotify)
}
githubRouter := group.Group("/github")
{

View File

@@ -0,0 +1,16 @@
package modules
import (
"github.com/gin-gonic/gin"
"schisandra-cloud-album/api"
"schisandra-cloud-album/middleware"
)
var roleApi = api.Api.RoleApi
func RoleRouter(router *gin.RouterGroup) {
group := router.Group("/auth")
group.Use(middleware.JWTAuthMiddleware())
group.POST("/role/create", roleApi.CreateRole)
group.POST("/role/add_role_to_user", roleApi.AddRoleToUser)
}

View File

@@ -12,14 +12,21 @@ import (
func InitRouter() *gin.Engine {
gin.SetMode(global.CONFIG.System.Env)
router := gin.Default()
err := router.SetTrustedProxies([]string{"127.0.0.1"})
err := router.SetTrustedProxies([]string{global.CONFIG.System.Ip})
if err != nil {
global.LOG.Error(err)
return nil
}
router.Use(middleware.RateLimitMiddleware(time.Millisecond*100, 20)) // 限流中间件
publicGroup := router.Group("api")
// 跨域设置
publicGroup.Use(cors.Default()).Use(middleware.RateLimitMiddleware(time.Millisecond*100, 20))
publicGroup.Use(cors.New(cors.Config{
AllowOrigins: []string{global.CONFIG.System.Web},
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"},
AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization", "X-CSRF-Token", "Accept-Language"},
AllowCredentials: true,
MaxAge: 12 * time.Hour,
}))
// 国际化设置
publicGroup.Use(middleware.I18n())
@@ -29,5 +36,6 @@ func InitRouter() *gin.Engine {
modules.SmsRouter(publicGroup) // 注册短信验证码路由
modules.OauthRouter(publicGroup) // 注册oauth路由
modules.WebsocketRouter(publicGroup) // 注册websocket路由
modules.RoleRouter(publicGroup) // 注册角色路由
return router
}

View File

@@ -13,3 +13,11 @@ func (PermissionService) GetPermissionsByIds(ids []int64) ([]model.ScaAuthPermis
}
return permissions, nil
}
// CreatePermissions 批量创建权限
func (PermissionService) CreatePermissions(permissions []model.ScaAuthPermission) error {
if err := global.DB.Model(&model.ScaAuthPermission{}).CreateInBatches(&permissions, len(permissions)).Error; err != nil {
return err
}
return nil
}

View File

@@ -1,3 +0,0 @@
package role_permission_service
type RolePermissionService struct{}

View File

@@ -1,17 +0,0 @@
package role_permission_service
import (
"schisandra-cloud-album/global"
"schisandra-cloud-album/model"
)
// QueryPermissionIdsByRoleId 通过角色ID列表查询权限ID列表
func (RolePermissionService) QueryPermissionIdsByRoleId(roleIds []*int64) []int64 {
var permissionIds []int64
rolePermission := model.ScaAuthRolePermission{}
if err := global.DB.Model(&rolePermission).Where("role_id IN ?", roleIds).Pluck("permission_id", &permissionIds).Error; err != nil {
global.LOG.Error(err)
return nil
}
return permissionIds
}

View File

@@ -13,3 +13,11 @@ func (RoleService) GetRoleListByIds(id []*int64) ([]model.ScaAuthRole, error) {
}
return roles, nil
}
// AddRole 新增角色
func (RoleService) AddRole(role model.ScaAuthRole) error {
if err := global.DB.Create(&role).Error; err != nil {
return err
}
return nil
}

View File

@@ -2,9 +2,7 @@ package service
import (
"schisandra-cloud-album/service/permission_service"
"schisandra-cloud-album/service/role_permission_service"
"schisandra-cloud-album/service/role_service"
"schisandra-cloud-album/service/user_role_service"
"schisandra-cloud-album/service/user_service"
"schisandra-cloud-album/service/user_social_service"
)
@@ -13,8 +11,6 @@ import (
type Services struct {
UserService user_service.UserService
RoleService role_service.RoleService
UserRoleService user_role_service.UserRoleService
RolePermissionService role_permission_service.RolePermissionService
PermissionService permission_service.PermissionService
UserSocialService user_social_service.UserSocialService
}

View File

@@ -1,3 +0,0 @@
package user_role_service
type UserRoleService struct{}

View File

@@ -1,32 +0,0 @@
package user_role_service
import (
"schisandra-cloud-album/global"
"schisandra-cloud-album/model"
)
// GetUserRoleIdsByUserId 通过用户ID获取用户角色ID列表
func (UserRoleService) GetUserRoleIdsByUserId(userId int64) ([]*int64, error) {
var roleIds []*int64
if err := global.DB.Table("sca_auth_user_role").Where("user_id = ? and deleted = 0 ", userId).Pluck("role_id", &roleIds).Error; err != nil {
return nil, err
}
return roleIds, nil
}
// AddUserRole 新增用户角色
func (UserRoleService) AddUserRole(userRole model.ScaAuthUserRole) error {
if err := global.DB.Create(&userRole).Error; err != nil {
return err
}
return nil
}
// GetAllUserRoles 返回所有用户的角色列表
func (UserRoleService) GetAllUserRoles() ([]*model.ScaAuthUserRole, error) {
var userRoles []*model.ScaAuthUserRole
if err := global.DB.Table("sca_auth_user_role").Where("deleted = 0").Find(&userRoles).Error; err != nil {
return nil, err
}
return userRoles, nil
}

View File

@@ -10,12 +10,10 @@ import (
type RefreshJWTPayload struct {
UserID *string `json:"user_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 AccessJWTClaims struct {