login performance optimization

This commit is contained in:
landaiqing
2024-08-25 00:06:00 +08:00
parent 974a96b6e0
commit 6e47e72514
8 changed files with 149 additions and 145 deletions

View File

@@ -30,32 +30,37 @@ func (CaptchaAPI) GenerateRotateCaptcha(c *gin.Context) {
captchaData, err := global.RotateCaptcha.Generate() captchaData, err := global.RotateCaptcha.Generate()
if err != nil { if err != nil {
global.LOG.Fatalln(err) global.LOG.Fatalln(err)
result.FailWithNull(c)
return
} }
blockData := captchaData.GetData() blockData := captchaData.GetData()
if blockData == nil { if blockData == nil {
result.FailWithNull(c) result.FailWithNull(c)
return return
} }
var masterImageBase64, thumbImageBase64 string
masterImageBase64 = captchaData.GetMasterImage().ToBase64() masterImageBase64 := captchaData.GetMasterImage().ToBase64()
thumbImageBase64 = captchaData.GetThumbImage().ToBase64() thumbImageBase64 := captchaData.GetThumbImage().ToBase64()
dotsByte, err := json.Marshal(blockData) dotsByte, err := json.Marshal(blockData)
if err != nil { if err != nil {
global.LOG.Fatalln(err)
result.FailWithNull(c) result.FailWithNull(c)
return return
} }
key := helper.StringToMD5(string(dotsByte)) key := helper.StringToMD5(string(dotsByte))
err = redis.Set(constant.UserLoginCaptchaRedisKey+key, dotsByte, time.Minute).Err() err = redis.Set(constant.UserLoginCaptchaRedisKey+key, dotsByte, time.Minute).Err()
if err != nil { if err != nil {
global.LOG.Fatalln(err)
result.FailWithNull(c) result.FailWithNull(c)
return return
} }
bt := map[string]interface{}{
result.OkWithData(map[string]interface{}{
"key": key, "key": key,
"image": masterImageBase64, "image": masterImageBase64,
"thumb": thumbImageBase64, "thumb": thumbImageBase64,
} }, c)
result.OkWithData(bt, c)
} }
// CheckRotateData 验证旋转验证码 // CheckRotateData 验证旋转验证码
@@ -67,30 +72,36 @@ func (CaptchaAPI) GenerateRotateCaptcha(c *gin.Context) {
// @Success 200 {string} json // @Success 200 {string} json
// @Router /api/captcha/rotate/check [post] // @Router /api/captcha/rotate/check [post]
func (CaptchaAPI) CheckRotateData(c *gin.Context) { func (CaptchaAPI) CheckRotateData(c *gin.Context) {
rotateRequest := dto.RotateCaptchaRequest{} var rotateRequest dto.RotateCaptchaRequest
err := c.ShouldBindJSON(&rotateRequest) if err := c.ShouldBindJSON(&rotateRequest); err != nil {
angle := rotateRequest.Angle
key := rotateRequest.Key
if err != nil {
result.FailWithNull(c) result.FailWithNull(c)
return return
} }
cacheDataByte, err := redis.Get(constant.UserLoginCaptchaRedisKey + key).Bytes()
if len(cacheDataByte) == 0 || err != nil { cacheDataByte, err := redis.Get(constant.UserLoginCaptchaRedisKey + rotateRequest.Key).Bytes()
if err != nil || len(cacheDataByte) == 0 {
result.FailWithCodeAndMessage(1011, ginI18n.MustGetMessage(c, "CaptchaExpired"), c) result.FailWithCodeAndMessage(1011, ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
return return
} }
var dct *rotate.Block var dct *rotate.Block
if err := json.Unmarshal(cacheDataByte, &dct); err != nil { if err := json.Unmarshal(cacheDataByte, &dct); err != nil {
result.FailWithNull(c) result.FailWithNull(c)
return return
} }
sAngle, _ := strconv.ParseFloat(fmt.Sprintf("%v", angle), 64)
sAngle, err := strconv.ParseFloat(fmt.Sprintf("%v", rotateRequest.Angle), 64)
if err != nil {
result.FailWithNull(c)
return
}
chkRet := rotate.CheckAngle(int64(sAngle), int64(dct.Angle), 2) chkRet := rotate.CheckAngle(int64(sAngle), int64(dct.Angle), 2)
if chkRet { if chkRet {
result.OkWithMessage("success", c) result.OkWithMessage("success", c)
return return
} }
result.FailWithMessage("fail", c) result.FailWithMessage("fail", c)
} }

View File

@@ -40,13 +40,7 @@ var mu sync.Mutex
// @Router /api/oauth/generate_client_id [get] // @Router /api/oauth/generate_client_id [get]
func (OAuthAPI) GenerateClientId(c *gin.Context) { func (OAuthAPI) GenerateClientId(c *gin.Context) {
// 获取客户端IP // 获取客户端IP
ip := c.GetHeader("X-Real-IP") ip := utils.GetClientIP(c)
if ip == "" {
ip = c.GetHeader("X-Forwarded-For")
}
if ip == "" {
ip = c.ClientIP()
}
// 加锁 // 加锁
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
@@ -161,49 +155,48 @@ func (OAuthAPI) CallbackVerify(c *gin.Context) {
// @Router /api/oauth/get_temp_qrcode [get] // @Router /api/oauth/get_temp_qrcode [get]
func (OAuthAPI) GetTempQrCode(c *gin.Context) { func (OAuthAPI) GetTempQrCode(c *gin.Context) {
clientId := c.Query("client_id") clientId := c.Query("client_id")
// 获取客户端IP
ip := c.GetHeader("X-Real-IP")
if ip == "" {
ip = c.GetHeader("X-Forwarded-For")
}
if ip == "" {
ip = c.ClientIP()
}
if clientId == "" { if clientId == "" {
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
return return
} }
qrcode := redis.Get(constant.UserLoginQrcodeRedisKey + ip + ":" + clientId).Val()
ip := utils.GetClientIP(c) // 使用工具函数获取客户端IP
key := constant.UserLoginQrcodeRedisKey + ip + ":" + clientId
// 从Redis获取二维码数据
qrcode := redis.Get(key).Val()
if qrcode != "" { if qrcode != "" {
data := response.ResponseQRCodeCreate{} data := new(response.ResponseQRCodeCreate)
err := json.Unmarshal([]byte(qrcode), &data) if err := json.Unmarshal([]byte(qrcode), data); err != nil {
if err != nil {
global.LOG.Error(err) global.LOG.Error(err)
result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c)
return return
} }
result.OK(ginI18n.MustGetMessage(c, "QRCodeGetSuccess"), data.Url, c) result.OK(ginI18n.MustGetMessage(c, "QRCodeGetSuccess"), data.Url, c)
return return
} }
// 生成临时二维码
data, err := global.Wechat.QRCode.Temporary(c.Request.Context(), clientId, 30*24*3600) data, err := global.Wechat.QRCode.Temporary(c.Request.Context(), clientId, 30*24*3600)
if err != nil { if err != nil {
global.LOG.Error(err) global.LOG.Error(err)
result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c)
return return
} }
// 序列化数据并存储到Redis
serializedData, err := json.Marshal(data) serializedData, err := json.Marshal(data)
if err != nil { if err != nil {
global.LOG.Error(err) global.LOG.Error(err)
result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c)
return return
} }
wrong := redis.Set(constant.UserLoginQrcodeRedisKey+ip+":"+clientId, serializedData, time.Hour*24*30).Err() if err := redis.Set(key, serializedData, time.Hour*24*30).Err(); err != nil {
global.LOG.Error(err)
if wrong != nil {
global.LOG.Error(wrong)
result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c)
return return
} }
result.OK(ginI18n.MustGetMessage(c, "QRCodeGetSuccess"), data.Url, c) result.OK(ginI18n.MustGetMessage(c, "QRCodeGetSuccess"), data.Url, c)
} }

View File

@@ -98,58 +98,6 @@ func (UserAPI) QueryUserByPhone(c *gin.Context) {
result.OkWithData(user, c) result.OkWithData(user, c)
} }
// AddUser 添加用户
// @Summary 添加用户
// @Tags 用户模块
// @Param user body dto.AddUserRequest true "用户信息"
// @Success 200 {string} json
// @Router /api/user/add [post]
func (UserAPI) AddUser(c *gin.Context) {
addUserRequest := dto.AddUserRequest{}
err := c.ShouldBindJSON(&addUserRequest)
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
return
}
username := userService.QueryUserByUsername(addUserRequest.Username)
if !reflect.DeepEqual(username, model.ScaAuthUser{}) {
result.FailWithMessage(ginI18n.MustGetMessage(c, "UsernameExists"), c)
return
}
phone := userService.QueryUserByPhone(addUserRequest.Phone)
if !reflect.DeepEqual(phone, model.ScaAuthUser{}) {
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneExists"), c)
return
}
encrypt, err := utils.Encrypt(addUserRequest.Password)
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "AddUserError"), c)
return
}
uid := idgen.NextId()
uidStr := strconv.FormatInt(uid, 10)
user := model.ScaAuthUser{
UID: &uidStr,
Username: &addUserRequest.Username,
Password: &encrypt,
Phone: &addUserRequest.Phone,
}
_, err = userService.AddUser(user)
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "AddUserError"), c)
return
}
_, err = global.Casbin.AddRoleForUser(uidStr, enum.User)
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "AddUserRoleError"), c)
return
}
result.OkWithMessage(ginI18n.MustGetMessage(c, "AddUserSuccess"), c)
return
}
// AccountLogin 账号登录 // AccountLogin 账号登录
// @Summary 账号登录 // @Summary 账号登录
// @Tags 用户模块 // @Tags 用户模块
@@ -219,30 +167,11 @@ func (UserAPI) PhoneLogin(c *gin.Context) {
return return
} }
// 异步查询用户信息
userChan := make(chan *model.ScaAuthUser)
go func() {
user := userService.QueryUserByPhone(phone) user := userService.QueryUserByPhone(phone)
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 reflect.DeepEqual(user, model.ScaAuthUser{}) {
// 未注册 // 未注册
if code == "" { code := redis.Get(constant.UserLoginSmsRedisKey + phone)
if code == nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
return return
} else { } else {
@@ -263,23 +192,31 @@ func (UserAPI) PhoneLogin(c *gin.Context) {
result.FailWithMessage(ginI18n.MustGetMessage(c, "RegisterUserError"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "RegisterUserError"), c)
return return
} }
err = global.Casbin.SavePolicy()
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "RegisterUserError"), c)
return
}
handelUserLogin(addUser, request.AutoLogin, c) handelUserLogin(addUser, request.AutoLogin, c)
return return
} }
} else { } else {
if code == "" { code := redis.Get(constant.UserLoginSmsRedisKey + phone)
if code == nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
return return
} else { } else {
if captcha != code { if captcha != code.Val() {
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaError"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaError"), c)
return return
} else { } else {
handelUserLogin(*user, request.AutoLogin, c) handelUserLogin(user, request.AutoLogin, c)
return return
} }
} }
} }
} }
// RefreshHandler 刷新token // RefreshHandler 刷新token
@@ -300,15 +237,11 @@ func (UserAPI) RefreshHandler(c *gin.Context) {
return return
} }
parseRefreshToken, isUpd, err := utils.ParseRefreshToken(refreshToken) parseRefreshToken, isUpd, err := utils.ParseRefreshToken(refreshToken)
if err != nil { if err != nil || !isUpd {
global.LOG.Errorln(err) global.LOG.Errorln(err)
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
return return
} }
if !isUpd {
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
return
}
accessTokenString, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: parseRefreshToken.UserID}) accessTokenString, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: parseRefreshToken.UserID})
if err != nil { if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
@@ -316,7 +249,7 @@ func (UserAPI) RefreshHandler(c *gin.Context) {
} }
tokenKey := constant.UserLoginTokenRedisKey + *parseRefreshToken.UserID tokenKey := constant.UserLoginTokenRedisKey + *parseRefreshToken.UserID
token, err := redis.Get(tokenKey).Result() token, err := redis.Get(tokenKey).Result()
if token == "" || err != nil { if err != nil || token == "" {
global.LOG.Errorln(err) global.LOG.Errorln(err)
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
return return
@@ -335,17 +268,24 @@ func (UserAPI) RefreshHandler(c *gin.Context) {
// handelUserLogin 处理用户登录 // handelUserLogin 处理用户登录
func handelUserLogin(user model.ScaAuthUser, autoLogin bool, c *gin.Context) { func handelUserLogin(user model.ScaAuthUser, autoLogin bool, c *gin.Context) {
// 检查 user.UID 是否为 nil
if user.UID == nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
return
}
accessToken, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: user.UID}) accessToken, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: user.UID})
if err != nil { if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
return return
} }
var days time.Duration var days time.Duration
if autoLogin { if autoLogin {
days = time.Hour * 24 * 7 days = 7 * 24 * time.Hour
} else { } else {
days = time.Hour * 24 * 1 days = 24 * time.Hour
} }
refreshToken, expiresAt := utils.GenerateRefreshToken(utils.RefreshJWTPayload{UserID: user.UID}, days) refreshToken, expiresAt := utils.GenerateRefreshToken(utils.RefreshJWTPayload{UserID: user.UID}, days)
data := dto.ResponseData{ data := dto.ResponseData{
AccessToken: accessToken, AccessToken: accessToken,
@@ -353,13 +293,14 @@ func handelUserLogin(user model.ScaAuthUser, autoLogin bool, c *gin.Context) {
ExpiresAt: expiresAt, ExpiresAt: expiresAt,
UID: user.UID, UID: user.UID,
} }
fail := redis.Set(constant.UserLoginTokenRedisKey+*user.UID, data, time.Hour*24*1).Err()
if fail != nil { err = redis.Set(constant.UserLoginTokenRedisKey+*user.UID, data, days).Err()
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
return return
} }
result.OkWithData(data, c) result.OkWithData(data, c)
return
} }
// ResetPassword 重置密码 // ResetPassword 重置密码
@@ -369,50 +310,86 @@ func handelUserLogin(user model.ScaAuthUser, autoLogin bool, c *gin.Context) {
// @Success 200 {string} json // @Success 200 {string} json
// @Router /api/user/reset_password [post] // @Router /api/user/reset_password [post]
func (UserAPI) ResetPassword(c *gin.Context) { func (UserAPI) ResetPassword(c *gin.Context) {
resetPasswordRequest := dto.ResetPasswordRequest{} var resetPasswordRequest dto.ResetPasswordRequest
err := c.ShouldBindJSON(&resetPasswordRequest) if err := c.ShouldBindJSON(&resetPasswordRequest); err != nil {
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
return return
} }
phone := resetPasswordRequest.Phone phone := resetPasswordRequest.Phone
captcha := resetPasswordRequest.Captcha captcha := resetPasswordRequest.Captcha
password := resetPasswordRequest.Password password := resetPasswordRequest.Password
repassword := resetPasswordRequest.Repassword repassword := resetPasswordRequest.Repassword
if phone == "" || captcha == "" || password == "" || repassword == "" { if phone == "" || captcha == "" || password == "" || repassword == "" {
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
return return
} }
isPhone := utils.IsPhone(phone)
if !isPhone { if !utils.IsPhone(phone) {
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneErrorFormat"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneError"), c)
return return
} }
code := redis.Get(constant.UserLoginSmsRedisKey + phone)
if code == nil { if password != repassword {
result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordNotSame"), c)
return
}
if !utils.IsPassword(password) {
result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordError"), c)
return
}
// 使用事务确保验证码检查和密码更新的原子性
tx := global.DB.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.Error; err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "DatabaseError"), c)
return
}
code := redis.Get(constant.UserLoginSmsRedisKey + phone).Val()
if code == "" {
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
return return
} else { }
if captcha != code.Val() {
if captcha != code {
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaError"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaError"), c)
return return
} }
// 验证码检查通过后立即删除或标记为已使用
if err := redis.Del(constant.UserLoginSmsRedisKey + phone).Err(); err != nil {
tx.Rollback()
result.FailWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordError"), c)
return
} }
user := userService.QueryUserByPhone(phone) user := userService.QueryUserByPhone(phone)
if reflect.DeepEqual(user, model.ScaAuthUser{}) { if reflect.DeepEqual(user, model.ScaAuthUser{}) {
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneNotRegister"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneNotRegister"), c)
return return
} }
encrypt, err := utils.Encrypt(password) encrypt, err := utils.Encrypt(password)
if err != nil { if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordError")+": "+err.Error(), c)
return
}
if err := userService.UpdateUser(phone, encrypt); err != nil {
tx.Rollback()
result.FailWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordError"), c) result.FailWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordError"), c)
return return
} }
wrong := userService.UpdateUser(phone, encrypt)
if wrong != nil { tx.Commit()
result.FailWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordError"), c)
return
}
result.OkWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordSuccess"), c) result.OkWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordSuccess"), c)
return
} }

View File

@@ -52,3 +52,4 @@ ResetPasswordSuccess = "reset password success!"
QRCodeGetFailed = "qr code get failed!" QRCodeGetFailed = "qr code get failed!"
QRCodeGetSuccess = "qr code get successfully!" QRCodeGetSuccess = "qr code get successfully!"
QRCodeExpired = "qr code expired!" QRCodeExpired = "qr code expired!"
InternalError = "internal error!"

View File

@@ -52,4 +52,5 @@ ResetPasswordSuccess = "重置密码成功!"
QRCodeGetFailed = "获取二维码失败!" QRCodeGetFailed = "获取二维码失败!"
QRCodeGetSuccess = "获取二维码成功!" QRCodeGetSuccess = "获取二维码成功!"
QRCodeExpired = "二维码已过期!" QRCodeExpired = "二维码已过期!"
InternalError = "内部错误!"

View File

@@ -14,7 +14,6 @@ func UserRouter(router *gin.RouterGroup) {
{ {
userGroup.POST("/login", userApi.AccountLogin) userGroup.POST("/login", userApi.AccountLogin)
userGroup.POST("/phone_login", userApi.PhoneLogin) userGroup.POST("/phone_login", userApi.PhoneLogin)
userGroup.POST("/add", userApi.AddUser)
userGroup.POST("/reset_password", userApi.ResetPassword) userGroup.POST("/reset_password", userApi.ResetPassword)
} }
authGroup := router.Group("auth").Use(middleware.JWTAuthMiddleware()).Use(middleware.CasbinMiddleware()) authGroup := router.Group("auth").Use(middleware.JWTAuthMiddleware()).Use(middleware.CasbinMiddleware())

15
utils/get_ip.go Normal file
View File

@@ -0,0 +1,15 @@
package utils
import "github.com/gin-gonic/gin"
// GetClientIP 工具函数获取客户端IP
func GetClientIP(c *gin.Context) string {
ip := c.GetHeader("X-Real-IP")
if ip == "" {
ip = c.GetHeader("X-Forwarded-For")
}
if ip == "" {
ip = c.ClientIP()
}
return ip
}

View File

@@ -30,3 +30,10 @@ func IsUsername(username string) bool {
match, _ := regexp.MatchString(phoneRegex, username) match, _ := regexp.MatchString(phoneRegex, username)
return match return match
} }
// IsPassword 密码的正则表达式
func IsPassword(password string) bool {
phoneRegex := `^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{6,18}$`
match, _ := regexp.MatchString(phoneRegex, password)
return match
}