🎨 complete SMS login function
This commit is contained in:
51
.air.toml
Normal file
51
.air.toml
Normal file
@@ -0,0 +1,51 @@
|
||||
root = "."
|
||||
testdata_dir = "testdata"
|
||||
tmp_dir = "tmp"
|
||||
|
||||
[build]
|
||||
args_bin = []
|
||||
bin = "tmp\\main.exe"
|
||||
cmd = "go build -o ./tmp/main.exe ."
|
||||
delay = 1000
|
||||
exclude_dir = ["assets", "tmp", "vendor", "testdata", ".idea", ".git"]
|
||||
exclude_file = []
|
||||
exclude_regex = ["_test.go"]
|
||||
exclude_unchanged = false
|
||||
follow_symlink = false
|
||||
full_bin = ""
|
||||
include_dir = []
|
||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||
include_file = []
|
||||
kill_delay = "0s"
|
||||
log = "build-errors.log"
|
||||
poll = false
|
||||
poll_interval = 0
|
||||
post_cmd = []
|
||||
pre_cmd = []
|
||||
rerun = false
|
||||
rerun_delay = 500
|
||||
send_interrupt = false
|
||||
stop_on_error = false
|
||||
|
||||
[color]
|
||||
app = ""
|
||||
build = "yellow"
|
||||
main = "magenta"
|
||||
runner = "green"
|
||||
watcher = "cyan"
|
||||
|
||||
[log]
|
||||
main_only = false
|
||||
time = false
|
||||
|
||||
[misc]
|
||||
clean_on_exit = false
|
||||
|
||||
[proxy]
|
||||
app_port = 0
|
||||
enabled = false
|
||||
proxy_port = 0
|
||||
|
||||
[screen]
|
||||
clear_on_rebuild = false
|
||||
keep_scroll = true
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,6 +9,7 @@
|
||||
*.so
|
||||
*.dylib
|
||||
.air.toml
|
||||
test
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
@@ -1,14 +1,14 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"schisandra-cloud-album/api/auth_api"
|
||||
"schisandra-cloud-album/api/captcha_api"
|
||||
"schisandra-cloud-album/api/sms_api"
|
||||
"schisandra-cloud-album/api/user_api"
|
||||
)
|
||||
|
||||
// Apis 统一导出的api
|
||||
type Apis struct {
|
||||
AuthApi auth_api.AuthAPI
|
||||
UserApi user_api.UserAPI
|
||||
CaptchaApi captcha_api.CaptchaAPI
|
||||
SmsApi sms_api.SmsAPI
|
||||
}
|
||||
|
@@ -1,3 +0,0 @@
|
||||
package auth_api
|
||||
|
||||
type AuthAPI struct{}
|
@@ -1,159 +0,0 @@
|
||||
package auth_api
|
||||
|
||||
import (
|
||||
ginI18n "github.com/gin-contrib/i18n"
|
||||
"github.com/gin-gonic/gin"
|
||||
"reflect"
|
||||
"schisandra-cloud-album/common/result"
|
||||
"schisandra-cloud-album/model"
|
||||
"schisandra-cloud-album/service"
|
||||
"schisandra-cloud-album/utils"
|
||||
)
|
||||
|
||||
var authService = service.Service.AuthService
|
||||
|
||||
// GetUserList
|
||||
// @Summary 获取所有用户列表
|
||||
// @Tags 鉴权模块
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/auth/user/List [get]
|
||||
func (AuthAPI) GetUserList(c *gin.Context) {
|
||||
userList := authService.GetUserList()
|
||||
result.OkWithData(userList, c)
|
||||
}
|
||||
|
||||
// QueryUserByUsername
|
||||
// @Summary 根据用户名查询用户
|
||||
// @Tags 鉴权模块
|
||||
// @Param username query string true "用户名"
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/auth/user/query_by_username [get]
|
||||
func (AuthAPI) QueryUserByUsername(c *gin.Context) {
|
||||
username := c.Query("username")
|
||||
user := authService.QueryUserByUsername(username)
|
||||
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "NotFoundUser"), c)
|
||||
return
|
||||
}
|
||||
result.OkWithData(user, c)
|
||||
}
|
||||
|
||||
// QueryUserByUuid
|
||||
// @Summary 根据uuid查询用户
|
||||
// @Tags 鉴权模块
|
||||
// @Param uuid query string true "用户uuid"
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/auth/user/query_by_uuid [get]
|
||||
func (AuthAPI) QueryUserByUuid(c *gin.Context) {
|
||||
uuid := c.Query("uuid")
|
||||
user := authService.QueryUserByUuid(uuid)
|
||||
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "NotFoundUser"), c)
|
||||
return
|
||||
}
|
||||
result.OkWithData(user, c)
|
||||
}
|
||||
|
||||
// DeleteUser 删除用户
|
||||
// @Summary 删除用户
|
||||
// @Tags 鉴权模块
|
||||
// @Param uuid query string true "用户uuid"
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/auth/user/delete [delete]
|
||||
func (AuthAPI) DeleteUser(c *gin.Context) {
|
||||
uuid := c.Query("uuid")
|
||||
err := authService.DeleteUser(uuid)
|
||||
if err != nil {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "DeletedFailed"), c)
|
||||
return
|
||||
}
|
||||
result.OkWithMessage(ginI18n.MustGetMessage(c, "DeletedSuccess"), c)
|
||||
}
|
||||
|
||||
// QueryUserByPhone 根据手机号查询用户
|
||||
// @Summary 根据手机号查询用户
|
||||
// @Tags 鉴权模块
|
||||
// @Param phone query string true "手机号"
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/auth/user/query_by_phone [get]
|
||||
func (AuthAPI) QueryUserByPhone(c *gin.Context) {
|
||||
phone := c.Query("phone")
|
||||
user := authService.QueryUserByPhone(phone)
|
||||
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "NotFoundUser"), c)
|
||||
return
|
||||
}
|
||||
result.OkWithData(user, c)
|
||||
}
|
||||
|
||||
// AccountLogin 账号登录
|
||||
// @Summary 账号登录
|
||||
// @Tags 鉴权模块
|
||||
// @Param account query string true "账号"
|
||||
// @Param password query string true "密码"
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/auth/user/login [post]
|
||||
func (AuthAPI) AccountLogin(c *gin.Context) {
|
||||
account := c.PostForm("account")
|
||||
password := c.PostForm("password")
|
||||
isPhone := utils.IsPhone(account)
|
||||
if isPhone {
|
||||
user := authService.QueryUserByPhone(account)
|
||||
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneNotRegister"), c)
|
||||
} else {
|
||||
verify := utils.Verify(password, *user.Password)
|
||||
if verify {
|
||||
result.OkWithData(user, c)
|
||||
} else {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordError"), c)
|
||||
}
|
||||
}
|
||||
}
|
||||
isEmail := utils.IsEmail(account)
|
||||
if isEmail {
|
||||
user := authService.QueryUserByEmail(account)
|
||||
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "EmailNotRegister"), c)
|
||||
} else {
|
||||
verify := utils.Verify(password, *user.Password)
|
||||
if verify {
|
||||
result.OkWithData(user, c)
|
||||
} else {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordError"), c)
|
||||
}
|
||||
}
|
||||
}
|
||||
isUsername := utils.IsUsername(account)
|
||||
if isUsername {
|
||||
user := authService.QueryUserByUsername(account)
|
||||
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "UsernameNotRegister"), c)
|
||||
} else {
|
||||
verify := utils.Verify(password, *user.Password)
|
||||
if verify {
|
||||
result.OkWithData(user, c)
|
||||
} else {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordError"), c)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Register 用户注册
|
||||
// @Summary 用户注册
|
||||
// @Tags 鉴权模块
|
||||
// @Param user body model.ScaAuthUser true "用户信息"
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/auth/user/register [post]
|
||||
func (AuthAPI) Register(c *gin.Context) {
|
||||
var user model.ScaAuthUser
|
||||
_ = c.ShouldBindJSON(&user)
|
||||
err := authService.AddUser(user)
|
||||
if err != nil {
|
||||
result.FailWithMessage("用户注册失败!", c)
|
||||
return
|
||||
}
|
||||
result.OkWithMessage("用户注册成功!", c)
|
||||
}
|
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/wenlng/go-captcha/v2/rotate"
|
||||
"github.com/wenlng/go-captcha/v2/slide"
|
||||
"log"
|
||||
"schisandra-cloud-album/api/captcha_api/model"
|
||||
"schisandra-cloud-album/api/captcha_api/dto"
|
||||
"schisandra-cloud-album/common/redis"
|
||||
"schisandra-cloud-album/common/result"
|
||||
"schisandra-cloud-album/global"
|
||||
@@ -44,7 +44,7 @@ func (CaptchaAPI) GenerateRotateCaptcha(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
key := helper.StringToMD5(string(dotsByte))
|
||||
err = redis.Set(key, dotsByte, time.Minute).Err()
|
||||
err = redis.Set("user:login:client:"+key, dotsByte, time.Minute).Err()
|
||||
if err != nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
@@ -66,7 +66,7 @@ func (CaptchaAPI) GenerateRotateCaptcha(c *gin.Context) {
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/captcha/rotate/check [post]
|
||||
func (CaptchaAPI) CheckRotateData(c *gin.Context) {
|
||||
rotateRequest := model.RotateCaptchaRequest{}
|
||||
rotateRequest := dto.RotateCaptchaRequest{}
|
||||
err := c.ShouldBindJSON(&rotateRequest)
|
||||
angle := rotateRequest.Angle
|
||||
key := rotateRequest.Key
|
||||
@@ -74,7 +74,7 @@ func (CaptchaAPI) CheckRotateData(c *gin.Context) {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
cacheDataByte, err := redis.Get(key).Bytes()
|
||||
cacheDataByte, err := redis.Get("user:login:client:" + key).Bytes()
|
||||
if len(cacheDataByte) == 0 || err != nil {
|
||||
result.FailWithCodeAndMessage(1011, ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
|
||||
return
|
||||
@@ -126,7 +126,7 @@ func (CaptchaAPI) GenerateBasicTextCaptcha(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
key := helper.StringToMD5(string(dotsByte))
|
||||
err = redis.Set(key, dotsByte, time.Minute).Err()
|
||||
err = redis.Set("user:login:client:"+key, dotsByte, time.Minute).Err()
|
||||
if err != nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
@@ -154,7 +154,7 @@ func (CaptchaAPI) CheckClickData(c *gin.Context) {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
cacheDataByte, err := redis.Get(key).Bytes()
|
||||
cacheDataByte, err := redis.Get("user:login:client:" + key).Bytes()
|
||||
if len(cacheDataByte) == 0 || err != nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package model
|
||||
package dto
|
||||
|
||||
type RotateCaptchaRequest struct {
|
||||
Angle int `json:"angle"`
|
3
api/role_api/role.go
Normal file
3
api/role_api/role.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package role_api
|
||||
|
||||
type RoleAPI struct{}
|
1
api/role_api/role_api.go
Normal file
1
api/role_api/role_api.go
Normal file
@@ -0,0 +1 @@
|
||||
package role_api
|
@@ -7,9 +7,11 @@ import (
|
||||
"github.com/pkg6/go-sms/gateways"
|
||||
"github.com/pkg6/go-sms/gateways/aliyun"
|
||||
"github.com/pkg6/go-sms/gateways/smsbao"
|
||||
"schisandra-cloud-album/common/redis"
|
||||
"schisandra-cloud-album/common/result"
|
||||
"schisandra-cloud-album/global"
|
||||
"schisandra-cloud-album/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SendMessageByAli 发送短信验证码
|
||||
@@ -46,6 +48,8 @@ func (SmsAPI) SendMessageByAli(c *gin.Context) {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaSendFailed"), c)
|
||||
return
|
||||
}
|
||||
result.OkWithMessage(ginI18n.MustGetMessage(c, "CaptchaSendSuccess"), c)
|
||||
|
||||
}
|
||||
|
||||
// SendMessageBySmsBao 短信宝发送短信验证码
|
||||
@@ -78,3 +82,32 @@ func (SmsAPI) SendMessageBySmsBao(c *gin.Context) {
|
||||
}
|
||||
result.OkWithMessage(ginI18n.MustGetMessage(c, "CaptchaSendSuccess"), c)
|
||||
}
|
||||
|
||||
// SendMessageTest 发送测试短信验证码
|
||||
// @Summary 发送测试短信验证码
|
||||
// @Description 发送测试短信验证码
|
||||
// @Tags 短信验证码
|
||||
// @Produce json
|
||||
// @Param phone query string true "手机号"
|
||||
// @Router /api/sms/test/send [get]
|
||||
func (SmsAPI) SendMessageTest(c *gin.Context) {
|
||||
phone := c.Query("phone")
|
||||
if phone == "" {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneNotEmpty"), c)
|
||||
return
|
||||
}
|
||||
isPhone := utils.IsPhone(phone)
|
||||
if !isPhone {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneError"), c)
|
||||
return
|
||||
}
|
||||
code := utils.GenValidateCode(6)
|
||||
err := redis.Set("user:login:sms:"+phone, code, time.Minute).Err()
|
||||
if err != nil {
|
||||
global.LOG.Error(err)
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaSendFailed"), c)
|
||||
return
|
||||
}
|
||||
result.OkWithMessage(ginI18n.MustGetMessage(c, "CaptchaSendSuccess"), c)
|
||||
|
||||
}
|
||||
|
30
api/user_api/dto/request_dto.go
Normal file
30
api/user_api/dto/request_dto.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package dto
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// RefreshTokenRequest 刷新token请求
|
||||
type RefreshTokenRequest struct {
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
// PhoneLoginRequest 手机号登录请求
|
||||
type PhoneLoginRequest struct {
|
||||
Phone string `json:"phone"`
|
||||
Captcha string `json:"captcha"`
|
||||
}
|
||||
|
||||
// ResponseData 返回数据
|
||||
type ResponseData struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
ExpiresAt int64 `json:"expires_at"`
|
||||
UID *string `json:"uid"`
|
||||
}
|
||||
|
||||
func (res ResponseData) MarshalBinary() ([]byte, error) {
|
||||
return json.Marshal(res)
|
||||
}
|
||||
|
||||
func (res ResponseData) UnmarshalBinary(data []byte) error {
|
||||
return json.Unmarshal(data, &res)
|
||||
}
|
3
api/user_api/user.go
Normal file
3
api/user_api/user.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package user_api
|
||||
|
||||
type UserAPI struct{}
|
307
api/user_api/user_api.go
Normal file
307
api/user_api/user_api.go
Normal file
@@ -0,0 +1,307 @@
|
||||
package user_api
|
||||
|
||||
import (
|
||||
ginI18n "github.com/gin-contrib/i18n"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yitter/idgenerator-go/idgen"
|
||||
"reflect"
|
||||
"schisandra-cloud-album/api/user_api/dto"
|
||||
"schisandra-cloud-album/common/enum"
|
||||
"schisandra-cloud-album/common/redis"
|
||||
"schisandra-cloud-album/common/result"
|
||||
"schisandra-cloud-album/global"
|
||||
"schisandra-cloud-album/model"
|
||||
"schisandra-cloud-album/service"
|
||||
"schisandra-cloud-album/utils"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var userService = service.Service.UserService
|
||||
var userRoleService = service.Service.UserRoleService
|
||||
|
||||
// GetUserList
|
||||
// @Summary 获取所有用户列表
|
||||
// @Tags 鉴权模块
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/auth/user/List [get]
|
||||
func (UserAPI) GetUserList(c *gin.Context) {
|
||||
userList := userService.GetUserList()
|
||||
result.OkWithData(userList, c)
|
||||
}
|
||||
|
||||
// QueryUserByUsername
|
||||
// @Summary 根据用户名查询用户
|
||||
// @Tags 鉴权模块
|
||||
// @Param username query string true "用户名"
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/auth/user/query_by_username [get]
|
||||
func (UserAPI) QueryUserByUsername(c *gin.Context) {
|
||||
username := c.Query("username")
|
||||
user := userService.QueryUserByUsername(username)
|
||||
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "NotFoundUser"), c)
|
||||
return
|
||||
}
|
||||
result.OkWithData(user, c)
|
||||
}
|
||||
|
||||
// QueryUserByUuid
|
||||
// @Summary 根据uuid查询用户
|
||||
// @Tags 鉴权模块
|
||||
// @Param uuid query string true "用户uuid"
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/auth/user/query_by_uuid [get]
|
||||
func (UserAPI) QueryUserByUuid(c *gin.Context) {
|
||||
uuid := c.Query("uuid")
|
||||
user := userService.QueryUserByUuid(uuid)
|
||||
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "NotFoundUser"), c)
|
||||
return
|
||||
}
|
||||
result.OkWithData(user, c)
|
||||
}
|
||||
|
||||
// DeleteUser 删除用户
|
||||
// @Summary 删除用户
|
||||
// @Tags 鉴权模块
|
||||
// @Param uuid query string true "用户uuid"
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/auth/user/delete [delete]
|
||||
func (UserAPI) DeleteUser(c *gin.Context) {
|
||||
uuid := c.Query("uuid")
|
||||
err := userService.DeleteUser(uuid)
|
||||
if err != nil {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "DeletedFailed"), c)
|
||||
return
|
||||
}
|
||||
result.OkWithMessage(ginI18n.MustGetMessage(c, "DeletedSuccess"), c)
|
||||
}
|
||||
|
||||
// QueryUserByPhone 根据手机号查询用户
|
||||
// @Summary 根据手机号查询用户
|
||||
// @Tags 鉴权模块
|
||||
// @Param phone query string true "手机号"
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/auth/user/query_by_phone [get]
|
||||
func (UserAPI) QueryUserByPhone(c *gin.Context) {
|
||||
phone := c.Query("phone")
|
||||
user := userService.QueryUserByPhone(phone)
|
||||
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "NotFoundUser"), c)
|
||||
return
|
||||
}
|
||||
result.OkWithData(user, c)
|
||||
}
|
||||
|
||||
// AccountLogin 账号登录
|
||||
// @Summary 账号登录
|
||||
// @Tags 鉴权模块
|
||||
// @Param account query string true "账号"
|
||||
// @Param password query string true "密码"
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/user/login [post]
|
||||
func (UserAPI) AccountLogin(c *gin.Context) {
|
||||
account := c.PostForm("account")
|
||||
password := c.PostForm("password")
|
||||
isPhone := utils.IsPhone(account)
|
||||
if isPhone {
|
||||
user := userService.QueryUserByPhone(account)
|
||||
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneNotRegister"), c)
|
||||
return
|
||||
} else {
|
||||
verify := utils.Verify(password, *user.Password)
|
||||
if verify {
|
||||
result.OkWithData(user, c)
|
||||
return
|
||||
} else {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordError"), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
isEmail := utils.IsEmail(account)
|
||||
if isEmail {
|
||||
user := userService.QueryUserByEmail(account)
|
||||
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "EmailNotRegister"), c)
|
||||
return
|
||||
} else {
|
||||
verify := utils.Verify(password, *user.Password)
|
||||
if verify {
|
||||
result.OkWithData(user, c)
|
||||
return
|
||||
} else {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordError"), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
isUsername := utils.IsUsername(account)
|
||||
if isUsername {
|
||||
user := userService.QueryUserByUsername(account)
|
||||
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "UsernameNotRegister"), c)
|
||||
return
|
||||
} else {
|
||||
verify := utils.Verify(password, *user.Password)
|
||||
if verify {
|
||||
result.OkWithData(user, c)
|
||||
return
|
||||
} else {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordError"), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// PhoneLogin 手机号登录/注册
|
||||
// @Summary 手机号登录/注册
|
||||
// @Tags 鉴权模块
|
||||
// @Param phone query string true "手机号"
|
||||
// @Param captcha query string true "验证码"
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/user/phone_login [post]
|
||||
func (UserAPI) PhoneLogin(c *gin.Context) {
|
||||
request := dto.PhoneLoginRequest{}
|
||||
err := c.ShouldBindJSON(&request)
|
||||
if err != nil {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
||||
return
|
||||
}
|
||||
phone := request.Phone
|
||||
captcha := request.Captcha
|
||||
if phone == "" || captcha == "" {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneAndCaptchaNotEmpty"), c)
|
||||
return
|
||||
}
|
||||
isPhone := utils.IsPhone(phone)
|
||||
if !isPhone {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneErrorFormat"), c)
|
||||
return
|
||||
}
|
||||
|
||||
user := userService.QueryUserByPhone(phone)
|
||||
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
||||
// 未注册
|
||||
code := redis.Get("user:login:sms:" + phone)
|
||||
if code == nil {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
|
||||
return
|
||||
} else {
|
||||
uid := idgen.NextId()
|
||||
uidStr := strconv.FormatInt(uid, 10)
|
||||
createUser := model.ScaAuthUser{
|
||||
UID: &uidStr,
|
||||
Phone: &phone,
|
||||
}
|
||||
addUser, err := userService.AddUser(createUser)
|
||||
if err != nil {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "RegisterUserError"), c)
|
||||
return
|
||||
}
|
||||
userRole := model.ScaAuthUserRole{
|
||||
UserID: addUser.ID,
|
||||
RoleID: enum.User,
|
||||
}
|
||||
e := userRoleService.AddUserRole(userRole)
|
||||
if e != nil {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
||||
return
|
||||
}
|
||||
ids, err := userRoleService.GetUserRoleIdsByUserId(addUser.ID)
|
||||
if err != nil {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
||||
return
|
||||
}
|
||||
accessToken, refreshToken, expiresAt := utils.GenerateAccessTokenAndRefreshToken(utils.JWTPayload{UserID: addUser.UID, RoleID: ids})
|
||||
|
||||
data := dto.ResponseData{
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
ExpiresAt: expiresAt,
|
||||
UID: addUser.UID,
|
||||
}
|
||||
fail := redis.Set("user:login:token:"+*addUser.UID, data, time.Hour*24*30).Err()
|
||||
if fail != nil {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
||||
return
|
||||
}
|
||||
result.OkWithData(data, c)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
code := redis.Get("user:login:sms:" + phone)
|
||||
if code == nil {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
|
||||
return
|
||||
} else {
|
||||
if captcha != code.Val() {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaError"), c)
|
||||
return
|
||||
} else {
|
||||
ids, err := userRoleService.GetUserRoleIdsByUserId(user.ID)
|
||||
if err != nil {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
||||
return
|
||||
}
|
||||
accessToken, refreshToken, expiresAt := utils.GenerateAccessTokenAndRefreshToken(utils.JWTPayload{UserID: user.UID, RoleID: ids})
|
||||
|
||||
data := dto.ResponseData{
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
ExpiresAt: expiresAt,
|
||||
UID: user.UID,
|
||||
}
|
||||
fail := redis.Set("user:login:token:"+*user.UID, data, time.Hour*24*30).Err()
|
||||
if fail != nil {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
||||
return
|
||||
}
|
||||
result.OkWithData(data, c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// RefreshHandler 刷新token
|
||||
// @Summary 刷新token
|
||||
// @Tags 鉴权模块
|
||||
// @Param refresh_token query string true "刷新token"
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/auth/token/refresh [post]
|
||||
func (UserAPI) RefreshHandler(c *gin.Context) {
|
||||
refreshToken := c.Query("refresh_token")
|
||||
if refreshToken == "" {
|
||||
result.FailWithMessage("refresh_token不能为空!", c)
|
||||
return
|
||||
}
|
||||
parseRefreshToken, isUpd, err := utils.ParseToken(refreshToken)
|
||||
if err != nil {
|
||||
global.LOG.Errorln(err)
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
||||
return
|
||||
}
|
||||
if isUpd {
|
||||
accessTokenString, refreshTokenString, expiresAt := utils.GenerateAccessTokenAndRefreshToken(utils.JWTPayload{UserID: parseRefreshToken.UserID, RoleID: parseRefreshToken.RoleID})
|
||||
data := dto.ResponseData{
|
||||
AccessToken: accessTokenString,
|
||||
RefreshToken: refreshTokenString,
|
||||
ExpiresAt: expiresAt,
|
||||
UID: parseRefreshToken.UserID,
|
||||
}
|
||||
fail := redis.Set("user:login:token:"+*parseRefreshToken.UserID, data, time.Hour*24*30).Err()
|
||||
if fail != nil {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
||||
return
|
||||
}
|
||||
result.OkWithData(data, c)
|
||||
return
|
||||
}
|
||||
}
|
7
common/enum/role.go
Normal file
7
common/enum/role.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package enum
|
||||
|
||||
var (
|
||||
SuperAdmin int64 = 1
|
||||
Admin int64 = 2
|
||||
User int64 = 3
|
||||
)
|
@@ -1,10 +1,8 @@
|
||||
package config
|
||||
|
||||
type JWT struct {
|
||||
Secret string `yaml:"secret"`
|
||||
Expiration string `yaml:"expiration"`
|
||||
RefreshExpiration string `yaml:"refresh-expiration"`
|
||||
RefreshTokenKey string `yaml:"refresh-token-key"`
|
||||
HeaderKey string `yaml:"header-key"`
|
||||
HeaderPrefix string `yaml:"header-prefix"`
|
||||
Secret string `yaml:"secret"`
|
||||
HeaderKey string `yaml:"header-key"`
|
||||
HeaderPrefix string `yaml:"header-prefix"`
|
||||
Issuer string `yaml:"issuer"`
|
||||
}
|
||||
|
10
core/idgenerator.go
Normal file
10
core/idgenerator.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package core
|
||||
|
||||
import "github.com/yitter/idgenerator-go/idgen"
|
||||
|
||||
func InitIDGenerator() {
|
||||
var options = idgen.NewIdGeneratorOptions(1)
|
||||
options.WorkerIdBitLength = 6 // 默认值6,限定 WorkerId 最大值为2^6-1,即默认最多支持64个节点。
|
||||
options.SeqBitLength = 6 // 默认值6,限制每毫秒生成的ID个数。若生成速度超过5万个/秒,建议加大 SeqBitLength 到 10。
|
||||
idgen.SetIdGenerator(options)
|
||||
}
|
@@ -176,7 +176,7 @@ const docTemplate = `{
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/model.ScaAuthUser"
|
||||
"$ref": "#/definitions/dto.ScaAuthUser"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -429,7 +429,7 @@ const docTemplate = `{
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"model.ScaAuthUser": {
|
||||
"dto.ScaAuthUser": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"avatar": {
|
||||
|
4
go.mod
4
go.mod
@@ -28,7 +28,7 @@ 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/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
@@ -41,6 +41,7 @@ require (
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.22.0 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
@@ -60,6 +61,7 @@ require (
|
||||
github.com/pkg6/go-sms v0.1.2 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/yitter/idgenerator-go v1.3.3 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/image v0.18.0 // indirect
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
|
6
go.sum
6
go.sum
@@ -15,6 +15,8 @@ github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP
|
||||
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
@@ -53,6 +55,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
||||
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
@@ -151,6 +155,8 @@ github.com/wenlng/go-captcha-assets v1.0.1 h1:AdjRFMKmadPRWRTv0XEYfjDvcaayZ2yExI
|
||||
github.com/wenlng/go-captcha-assets v1.0.1/go.mod h1:yQqc7rRbxgLCg+tWtVp+7Y317D1wIZDan/yIwt8wSac=
|
||||
github.com/wenlng/go-captcha/v2 v2.0.0 h1:7Z4Zy09SIHgvj9e8ZxP4VhYOwg7IHt8kGlVrE5jP5Z8=
|
||||
github.com/wenlng/go-captcha/v2 v2.0.0/go.mod h1:5hac1em3uXoyC5ipZ0xFv9umNM/waQvYAQdr0cx/h34=
|
||||
github.com/yitter/idgenerator-go v1.3.3 h1:i6rzmpbCL0vlmr/tuW5+lSQzNuDG9vYBjIYRvnRcHE8=
|
||||
github.com/yitter/idgenerator-go v1.3.3/go.mod h1:VVjbqFjGUsIkaXVkXEdmx1LiXUL3K1NvyxWPJBPbBpE=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
|
@@ -33,3 +33,11 @@ CaptchaSendSuccess = "captcha send successfully!"
|
||||
CaptchaTooOften = "captcha too often!"
|
||||
PhoneNotEmpty = "phone number can not be empty!"
|
||||
EmailNotEmpty = "email can not be empty!"
|
||||
AuthVerifyFailed = "auth verify failed!, please try again!"
|
||||
AuthVerifySuccess = "auth verify successfully!"
|
||||
AuthVerifyExpired = "auth verify expired!"
|
||||
ParamsError = "params error!"
|
||||
PhoneAndCaptchaNotEmpty = "phone number and captcha can not be empty!"
|
||||
PhoneErrorFormat = "phone number error format!"
|
||||
RegisterUserError = "register user error!"
|
||||
LoginExpired = "login expired!, please try again!"
|
||||
|
@@ -33,3 +33,11 @@ CaptchaSendSuccess = "验证码发送成功!"
|
||||
CaptchaTooOften = "验证码发送过于频繁!"
|
||||
PhoneNotEmpty = "手机号不能为空!"
|
||||
EmailNotEmpty = "邮箱不能为空!"
|
||||
AuthVerifyFailed = "认证失败!,请重新登录!"
|
||||
AuthVerifySuccess = "认证成功!"
|
||||
AuthVerifyExpired = "认证已过期!,请重新登录!"
|
||||
ParamsError = "参数错误!"
|
||||
PhoneAndCaptchaNotEmpty = "手机号和验证码不能为空!"
|
||||
PhoneErrorFormat = "手机号格式错误!"
|
||||
RegisterUserError = "注册用户错误!"
|
||||
LoginExpired = "登录已过期!,请重新登录!"
|
||||
|
11
main.go
11
main.go
@@ -9,11 +9,12 @@ import (
|
||||
|
||||
func main() {
|
||||
// 初始化配置
|
||||
core.InitConfig() // 读取配置文件
|
||||
core.InitLogger() // 初始化日志
|
||||
core.InitGorm() // 初始化数据库
|
||||
core.InitRedis() // 初始化redis
|
||||
core.InitCaptcha() // 初始化验证码
|
||||
core.InitConfig() // 读取配置文件
|
||||
core.InitLogger() // 初始化日志
|
||||
core.InitGorm() // 初始化数据库
|
||||
core.InitRedis() // 初始化redis
|
||||
core.InitCaptcha() // 初始化验证码
|
||||
core.InitIDGenerator() // 初始化ID生成器
|
||||
// 命令行参数绑定
|
||||
option := cmd.Parse()
|
||||
if cmd.IsStopWeb(&option) {
|
||||
|
38
middleware/jwt.go
Normal file
38
middleware/jwt.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
ginI18n "github.com/gin-contrib/i18n"
|
||||
"github.com/gin-gonic/gin"
|
||||
"schisandra-cloud-album/common/result"
|
||||
"schisandra-cloud-album/global"
|
||||
"schisandra-cloud-album/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func JWTAuthMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 默认双Token放在请求头Authorization的Bearer中,并以空格隔开
|
||||
authHeader := c.GetHeader(global.CONFIG.JWT.HeaderKey)
|
||||
if authHeader == "" {
|
||||
c.Abort()
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "AuthVerifyFailed"), c)
|
||||
return
|
||||
}
|
||||
headerPrefix := global.CONFIG.JWT.HeaderPrefix
|
||||
accessToken := strings.TrimPrefix(authHeader, headerPrefix+" ")
|
||||
|
||||
if accessToken == "undefined" || accessToken == "" {
|
||||
c.Abort()
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "AuthVerifyFailed"), c)
|
||||
return
|
||||
}
|
||||
parseToken, isUpd, err := utils.ParseToken(accessToken)
|
||||
if err != nil || !isUpd {
|
||||
c.Abort()
|
||||
result.FailWithCodeAndMessage(401, ginI18n.MustGetMessage(c, "AuthVerifyExpired"), c)
|
||||
return
|
||||
}
|
||||
c.Set("userId", parseToken.UserID)
|
||||
c.Next()
|
||||
}
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -9,7 +10,7 @@ const TableNameScaAuthUser = "sca_auth_user"
|
||||
// ScaAuthUser 用户表
|
||||
type ScaAuthUser struct {
|
||||
ID int64 `gorm:"column:id;type:bigint(255);primaryKey;autoIncrement:true;comment:自增ID" json:"-"` // 自增ID
|
||||
UUID *string `gorm:"column:uuid;type:varchar(255);comment:唯一ID" json:"uuid"` // 唯一ID
|
||||
UID *string `gorm:"column:uid;type:varchar(255);comment:唯一ID" json:"uid"` // 唯一ID
|
||||
Username *string `gorm:"column:username;type:varchar(32);comment:用户名" json:"username"` // 用户名
|
||||
Nickname *string `gorm:"column:nickname;type:varchar(32);comment:昵称" json:"nickname"` // 昵称
|
||||
Email *string `gorm:"column:email;type:varchar(32);comment:邮箱" json:"email"` // 邮箱
|
||||
@@ -17,14 +18,14 @@ type ScaAuthUser struct {
|
||||
Password *string `gorm:"column:password;type:varchar(64);comment:密码" json:"-"` // 密码
|
||||
Gender *string `gorm:"column:gender;type:varchar(32);comment:性别" json:"gender"` // 性别
|
||||
Avatar *string `gorm:"column:avatar;type:varchar(255);comment:头像" json:"avatar"` // 头像
|
||||
Status *int64 `gorm:"column:status;type:tinyint(4);comment:状态 0 正常 1 封禁" json:"status"` // 状态 0 正常 1 封禁
|
||||
Status *int64 `gorm:"column:status;type:tinyint(4);default:0;comment:状态 0 正常 1 封禁" json:"status"` // 状态 0 正常 1 封禁
|
||||
Introduce *string `gorm:"column:introduce;type:varchar(255);comment:介绍" json:"introduce"` // 介绍
|
||||
ExtJSON *string `gorm:"column:ext_json;type:varchar(255);comment:额外字段" json:"-"` // 额外字段
|
||||
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:是否删除 0 未删除 1 已删除" json:"-"` // 是否删除 0 未删除 1 已删除
|
||||
Deleted *int64 `gorm:"column:deleted;type:int(11);default:0;comment:是否删除 0 未删除 1 已删除" json:"-"` // 是否删除 0 未删除 1 已删除
|
||||
Blog *string `gorm:"column:blog;type:varchar(255);comment:博客" json:"blog"` // 博客
|
||||
Location *string `gorm:"column:location;type:varchar(255);comment:地址" json:"location"` // 地址
|
||||
Company *string `gorm:"column:company;type:varchar(255);comment:公司" json:"company"` // 公司
|
||||
@@ -34,3 +35,11 @@ type ScaAuthUser struct {
|
||||
func (*ScaAuthUser) TableName() string {
|
||||
return TableNameScaAuthUser
|
||||
}
|
||||
|
||||
func (user *ScaAuthUser) MarshalBinary() ([]byte, error) {
|
||||
return json.Marshal(user)
|
||||
}
|
||||
|
||||
func (user *ScaAuthUser) UnmarshalBinary(data []byte) error {
|
||||
return json.Unmarshal(data, user)
|
||||
}
|
||||
|
@@ -1,19 +0,0 @@
|
||||
package modules
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"schisandra-cloud-album/api"
|
||||
)
|
||||
|
||||
var authApi = api.Api.AuthApi
|
||||
|
||||
func AuthRouter(router *gin.RouterGroup) {
|
||||
group := router.Group("auth")
|
||||
group.GET("/user/List", authApi.GetUserList)
|
||||
group.GET("/user/query_by_username", authApi.QueryUserByUsername)
|
||||
group.GET("/user/query_by_uuid", authApi.QueryUserByUuid)
|
||||
group.DELETE("/user/delete", authApi.DeleteUser)
|
||||
group.GET("/user/query_by_phone", authApi.QueryUserByPhone)
|
||||
group.POST("/user/login", authApi.AccountLogin)
|
||||
group.POST("/user/register", authApi.Register)
|
||||
}
|
@@ -11,4 +11,5 @@ func SmsRouter(router *gin.RouterGroup) {
|
||||
group := router.Group("/sms")
|
||||
group.GET("/ali/send", smsApi.SendMessageByAli)
|
||||
group.GET("/smsbao/send", smsApi.SendMessageBySmsBao)
|
||||
group.GET("/test/send", smsApi.SendMessageTest)
|
||||
}
|
||||
|
25
router/modules/user_router.go
Normal file
25
router/modules/user_router.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package modules
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"schisandra-cloud-album/api"
|
||||
"schisandra-cloud-album/middleware"
|
||||
)
|
||||
|
||||
var userApi = api.Api.UserApi
|
||||
|
||||
// UserRouter 用户相关路由 有auth接口组需要token验证,没有auth接口组不需要token验证
|
||||
func UserRouter(router *gin.RouterGroup) {
|
||||
userGroup := router.Group("user")
|
||||
{
|
||||
userGroup.POST("/login", userApi.AccountLogin)
|
||||
userGroup.POST("/phone_login", userApi.PhoneLogin)
|
||||
}
|
||||
authGroup := router.Group("auth").Use(middleware.JWTAuthMiddleware())
|
||||
{
|
||||
authGroup.GET("/user/List", userApi.GetUserList)
|
||||
authGroup.GET("/user/query_by_uuid", userApi.QueryUserByUuid)
|
||||
authGroup.POST("/token/refresh", userApi.RefreshHandler)
|
||||
}
|
||||
|
||||
}
|
@@ -23,7 +23,7 @@ func InitRouter() *gin.Engine {
|
||||
publicGroup.Use(middleware.I18n())
|
||||
|
||||
modules.SwaggerRouter(router) // 注册swagger路由
|
||||
modules.AuthRouter(publicGroup) // 注册鉴权路由
|
||||
modules.UserRouter(publicGroup) // 注册鉴权路由
|
||||
modules.CaptchaRouter(publicGroup) // 注册验证码路由
|
||||
modules.SmsRouter(publicGroup) // 注册短信验证码路由
|
||||
return router
|
||||
|
@@ -1,3 +0,0 @@
|
||||
package auth_service
|
||||
|
||||
type AuthService struct{}
|
3
service/role_service/role.go
Normal file
3
service/role_service/role.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package role_service
|
||||
|
||||
type RoleService struct{}
|
15
service/role_service/role_service.go
Normal file
15
service/role_service/role_service.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package role_service
|
||||
|
||||
import (
|
||||
"schisandra-cloud-album/global"
|
||||
"schisandra-cloud-album/model"
|
||||
)
|
||||
|
||||
// GetRoleById : 通过Id获取角色信息
|
||||
func (RoleService) GetRoleById(id int64) (model.ScaAuthRole, error) {
|
||||
var role model.ScaAuthRole
|
||||
if err := global.DB.Where("id = ? and deleted = 0", id).First(&role).Error; err != nil {
|
||||
return model.ScaAuthRole{}, err
|
||||
}
|
||||
return role, nil
|
||||
}
|
@@ -1,12 +1,16 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"schisandra-cloud-album/service/auth_service"
|
||||
"schisandra-cloud-album/service/role_service"
|
||||
"schisandra-cloud-album/service/user_role_service"
|
||||
"schisandra-cloud-album/service/user_service"
|
||||
)
|
||||
|
||||
// Services 统一导出的service
|
||||
type Services struct {
|
||||
AuthService auth_service.AuthService
|
||||
UserService user_service.UserService
|
||||
RoleService role_service.RoleService
|
||||
UserRoleService user_role_service.UserRoleService
|
||||
}
|
||||
|
||||
// Service new函数实例化,实例化完成后会返回结构体地指针类型
|
||||
|
3
service/user_role_service/user_role.go
Normal file
3
service/user_role_service/user_role.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package user_role_service
|
||||
|
||||
type UserRoleService struct{}
|
23
service/user_role_service/user_role_service.go
Normal file
23
service/user_role_service/user_role_service.go
Normal file
@@ -0,0 +1,23 @@
|
||||
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 = ?", 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
|
||||
}
|
3
service/user_service/user.go
Normal file
3
service/user_service/user.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package user_service
|
||||
|
||||
type UserService struct{}
|
@@ -1,4 +1,4 @@
|
||||
package auth_service
|
||||
package user_service
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
@@ -8,52 +8,60 @@ import (
|
||||
)
|
||||
|
||||
// GetUserList 获取所有用户列表
|
||||
func (AuthService) GetUserList() []*model.ScaAuthUser {
|
||||
func (UserService) GetUserList() []*model.ScaAuthUser {
|
||||
data := make([]*model.ScaAuthUser, 0)
|
||||
global.DB.Where("deleted = 0 ").Find(&data)
|
||||
return data
|
||||
}
|
||||
|
||||
// QueryUserByUsername 根据用户名查询用户
|
||||
func (AuthService) QueryUserByUsername(username string) model.ScaAuthUser {
|
||||
func (UserService) QueryUserByUsername(username string) model.ScaAuthUser {
|
||||
authUser := model.ScaAuthUser{}
|
||||
global.DB.Where("username = ? and deleted = 0", username).First(&authUser)
|
||||
return authUser
|
||||
}
|
||||
|
||||
// QueryUserByUuid 根据用户uuid查询用户
|
||||
func (AuthService) QueryUserByUuid(uuid string) model.ScaAuthUser {
|
||||
func (UserService) QueryUserByUuid(uuid string) model.ScaAuthUser {
|
||||
authUser := model.ScaAuthUser{}
|
||||
global.DB.Where("uuid = ? and deleted = 0", uuid).First(&authUser)
|
||||
return authUser
|
||||
}
|
||||
|
||||
// AddUser 添加用户
|
||||
func (AuthService) AddUser(user model.ScaAuthUser) error {
|
||||
return global.DB.Create(&user).Error
|
||||
func (UserService) AddUser(user model.ScaAuthUser) (model.ScaAuthUser, error) {
|
||||
if err := global.DB.Create(&user).Error; err != nil {
|
||||
return model.ScaAuthUser{}, err
|
||||
}
|
||||
// 查询创建后的用户信息
|
||||
var createdUser model.ScaAuthUser
|
||||
if err := global.DB.First(&createdUser, user.ID).Error; err != nil {
|
||||
return model.ScaAuthUser{}, err
|
||||
}
|
||||
return createdUser, nil
|
||||
}
|
||||
|
||||
// UpdateUser 更新用户
|
||||
func (AuthService) UpdateUser(user model.ScaAuthUser) *gorm.DB {
|
||||
func (UserService) UpdateUser(user model.ScaAuthUser) *gorm.DB {
|
||||
authUser := model.ScaAuthUser{}
|
||||
return global.DB.Model(&authUser).Where("uuid = ?", user.UUID).Updates(user)
|
||||
return global.DB.Model(&authUser).Where("uuid = ?", user.UID).Updates(user)
|
||||
}
|
||||
|
||||
// DeleteUser 删除用户
|
||||
func (AuthService) DeleteUser(uuid string) error {
|
||||
func (UserService) DeleteUser(uuid string) error {
|
||||
authUser := model.ScaAuthUser{}
|
||||
return global.DB.Model(&authUser).Where("uuid = ?", uuid).Updates(&model.ScaAuthUser{Deleted: &enum.DELETED}).Error
|
||||
}
|
||||
|
||||
// QueryUserByPhone 根据手机号查询用户
|
||||
func (AuthService) QueryUserByPhone(phone string) model.ScaAuthUser {
|
||||
func (UserService) QueryUserByPhone(phone string) model.ScaAuthUser {
|
||||
authUser := model.ScaAuthUser{}
|
||||
global.DB.Where("phone = ? and deleted = 0", phone).First(&authUser)
|
||||
return authUser
|
||||
}
|
||||
|
||||
// QueryUserByEmail 根据邮箱查询用户
|
||||
func (AuthService) QueryUserByEmail(email string) model.ScaAuthUser {
|
||||
func (UserService) QueryUserByEmail(email string) model.ScaAuthUser {
|
||||
authUser := model.ScaAuthUser{}
|
||||
global.DB.Where("email = ? and deleted = 0", email).First(&authUser)
|
||||
return authUser
|
54
utils/jwt.go
54
utils/jwt.go
@@ -7,9 +7,8 @@ import (
|
||||
)
|
||||
|
||||
type JWTPayload struct {
|
||||
UserID int `json:"user_id"`
|
||||
Role string `json:"role"`
|
||||
Username string `json:"username"`
|
||||
UserID *string `json:"user_id"`
|
||||
RoleID []*int64 `json:"role_id"`
|
||||
}
|
||||
|
||||
type JWTClaims struct {
|
||||
@@ -17,10 +16,11 @@ type JWTClaims struct {
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
var MySecret = []byte(global.CONFIG.JWT.Secret)
|
||||
var MySecret []byte
|
||||
|
||||
// GenerateToken generates a JWT token with the given payload
|
||||
func GenerateToken(payload JWTPayload) (string, error) {
|
||||
MySecret = []byte(global.CONFIG.JWT.Secret)
|
||||
claims := JWTClaims{
|
||||
JWTPayload: payload,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
@@ -33,17 +33,55 @@ func GenerateToken(payload JWTPayload) (string, error) {
|
||||
return token.SignedString(MySecret)
|
||||
}
|
||||
|
||||
// GenerateAccessTokenAndRefreshToken generates a JWT token with the given payload, and returns the accessToken and refreshToken
|
||||
func GenerateAccessTokenAndRefreshToken(payload JWTPayload) (string, string, int64) {
|
||||
MySecret = []byte(global.CONFIG.JWT.Secret)
|
||||
// accessToken 的数据
|
||||
accessClaims := JWTClaims{
|
||||
JWTPayload: payload,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 2)),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
NotBefore: jwt.NewNumericDate(time.Now()),
|
||||
Issuer: global.CONFIG.JWT.Issuer,
|
||||
},
|
||||
}
|
||||
refreshClaims := JWTClaims{
|
||||
JWTPayload: payload,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24 * 7)), // 7天
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
NotBefore: jwt.NewNumericDate(time.Now()),
|
||||
Issuer: global.CONFIG.JWT.Issuer,
|
||||
},
|
||||
}
|
||||
accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, accessClaims)
|
||||
refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, refreshClaims)
|
||||
accessTokenString, err := accessToken.SignedString(MySecret)
|
||||
if err != nil {
|
||||
global.LOG.Error(err)
|
||||
return "", "", 0
|
||||
}
|
||||
refreshTokenString, err := refreshToken.SignedString(MySecret)
|
||||
if err != nil {
|
||||
global.LOG.Error(err)
|
||||
return "", "", 0
|
||||
}
|
||||
return accessTokenString, refreshTokenString, refreshClaims.ExpiresAt.Time.Unix()
|
||||
}
|
||||
|
||||
// ParseToken parses a JWT token and returns the payload
|
||||
func ParseToken(tokenString string) (*JWTPayload, error) {
|
||||
func ParseToken(tokenString string) (*JWTPayload, bool, error) {
|
||||
MySecret = []byte(global.CONFIG.JWT.Secret)
|
||||
token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return MySecret, nil
|
||||
})
|
||||
if err != nil {
|
||||
global.LOG.Error(err)
|
||||
return nil, err
|
||||
return nil, false, err
|
||||
}
|
||||
if claims, ok := token.Claims.(*JWTClaims); ok && token.Valid {
|
||||
return &claims.JWTPayload, nil
|
||||
return &claims.JWTPayload, true, nil
|
||||
}
|
||||
return nil, err
|
||||
return nil, false, err
|
||||
}
|
||||
|
Reference in New Issue
Block a user