- 增加一定时间内登录失败次数的限制
- 增加离线会话保存时间限制 - 完成需求「使会话详情的路径栏可写」close 69 - 修复bug「Ubuntu系统RDP不能正常显示」close 66 - 修复bug「使用guacd接入ssh时,第一个按键被忽略」 close 70
This commit is contained in:
@ -46,19 +46,35 @@ func LoginEndpoint(c echo.Context) error {
|
||||
}
|
||||
|
||||
user, err := model.FindUserByUsername(loginAccount.Username)
|
||||
|
||||
// 存储登录失败次数信息
|
||||
loginFailCountKey := loginAccount.Username
|
||||
v, ok := global.Cache.Get(loginFailCountKey)
|
||||
if !ok {
|
||||
v = 1
|
||||
}
|
||||
count := v.(int)
|
||||
if count >= 5 {
|
||||
return Fail(c, -1, "登录失败次数过多,请稍后再试")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return Fail(c, -1, "您输入的账号或密码不正确")
|
||||
count++
|
||||
global.Cache.Set(loginFailCountKey, count, time.Minute*time.Duration(5))
|
||||
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
||||
}
|
||||
|
||||
if err := utils.Encoder.Match([]byte(user.Password), []byte(loginAccount.Password)); err != nil {
|
||||
return Fail(c, -1, "您输入的账号或密码不正确")
|
||||
count++
|
||||
global.Cache.Set(loginFailCountKey, count, time.Minute*time.Duration(5))
|
||||
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
||||
}
|
||||
|
||||
if user.TOTPSecret != "" && user.TOTPSecret != "-" {
|
||||
return Fail(c, 0, "")
|
||||
}
|
||||
|
||||
token, err := Login(c, loginAccount, user)
|
||||
token, err := LoginSuccess(c, loginAccount, user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -66,7 +82,7 @@ func LoginEndpoint(c echo.Context) error {
|
||||
return Success(c, token)
|
||||
}
|
||||
|
||||
func Login(c echo.Context, loginAccount LoginAccount, user model.User) (token string, err error) {
|
||||
func LoginSuccess(c echo.Context, loginAccount LoginAccount, user model.User) (token string, err error) {
|
||||
token = strings.Join([]string{utils.UUID(), utils.UUID(), utils.UUID(), utils.UUID()}, "")
|
||||
|
||||
authorization := Authorization{
|
||||
@ -75,11 +91,13 @@ func Login(c echo.Context, loginAccount LoginAccount, user model.User) (token st
|
||||
User: user,
|
||||
}
|
||||
|
||||
cacheKey := strings.Join([]string{Token, token}, ":")
|
||||
|
||||
if authorization.Remember {
|
||||
// 记住登录有效期两周
|
||||
global.Cache.Set(token, authorization, RememberEffectiveTime)
|
||||
global.Cache.Set(cacheKey, authorization, RememberEffectiveTime)
|
||||
} else {
|
||||
global.Cache.Set(token, authorization, NotRememberEffectiveTime)
|
||||
global.Cache.Set(cacheKey, authorization, NotRememberEffectiveTime)
|
||||
}
|
||||
|
||||
// 保存登录日志
|
||||
@ -107,20 +125,37 @@ func loginWithTotpEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 存储登录失败次数信息
|
||||
loginFailCountKey := loginAccount.Username
|
||||
v, ok := global.Cache.Get(loginFailCountKey)
|
||||
if !ok {
|
||||
v = 1
|
||||
}
|
||||
count := v.(int)
|
||||
if count >= 5 {
|
||||
return Fail(c, -1, "登录失败次数过多,请稍后再试")
|
||||
}
|
||||
|
||||
user, err := model.FindUserByUsername(loginAccount.Username)
|
||||
if err != nil {
|
||||
return Fail(c, -1, "您输入的账号或密码不正确")
|
||||
count++
|
||||
global.Cache.Set(loginFailCountKey, count, time.Minute*time.Duration(5))
|
||||
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
||||
}
|
||||
|
||||
if err := utils.Encoder.Match([]byte(user.Password), []byte(loginAccount.Password)); err != nil {
|
||||
return Fail(c, -1, "您输入的账号或密码不正确")
|
||||
count++
|
||||
global.Cache.Set(loginFailCountKey, count, time.Minute*time.Duration(5))
|
||||
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
||||
}
|
||||
|
||||
if !totp.Validate(loginAccount.TOTP, user.TOTPSecret) {
|
||||
return Fail(c, -2, "您的TOTP不匹配")
|
||||
count++
|
||||
global.Cache.Set(loginFailCountKey, count, time.Minute*time.Duration(5))
|
||||
return FailWithData(c, -1, "您输入双因素认证授权码不正确", count)
|
||||
}
|
||||
|
||||
token, err := Login(c, loginAccount, user)
|
||||
token, err := LoginSuccess(c, loginAccount, user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -41,7 +41,8 @@ func Auth(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
}
|
||||
|
||||
token := GetToken(c)
|
||||
authorization, found := global.Cache.Get(token)
|
||||
cacheKey := strings.Join([]string{Token, token}, ":")
|
||||
authorization, found := global.Cache.Get(cacheKey)
|
||||
if !found {
|
||||
return Fail(c, 401, "您的登录信息已失效,请重新登录后再试。")
|
||||
}
|
||||
|
@ -156,6 +156,14 @@ func Fail(c echo.Context, code int, message string) error {
|
||||
})
|
||||
}
|
||||
|
||||
func FailWithData(c echo.Context, code int, message string, data interface{}) error {
|
||||
return c.JSON(200, H{
|
||||
"code": code,
|
||||
"message": message,
|
||||
"data": data,
|
||||
})
|
||||
}
|
||||
|
||||
func Success(c echo.Context, data interface{}) error {
|
||||
return c.JSON(200, H{
|
||||
"code": 1,
|
||||
|
@ -66,15 +66,9 @@ func SessionPagingEndpoint(c echo.Context) error {
|
||||
func SessionDeleteEndpoint(c echo.Context) error {
|
||||
sessionIds := c.Param("id")
|
||||
split := strings.Split(sessionIds, ",")
|
||||
for i := range split {
|
||||
drivePath, err := model.GetRecordingPath()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if err := os.RemoveAll(path.Join(drivePath, split[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
model.DeleteSessionById(split[i])
|
||||
err := model.DeleteSessionByIds(split)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, nil)
|
||||
@ -132,7 +126,7 @@ func CloseSessionById(sessionId string, code int, reason string) {
|
||||
|
||||
if s.Status == model.Connecting {
|
||||
// 会话还未建立成功,无需保留数据
|
||||
model.DeleteSessionById(sessionId)
|
||||
_ = model.DeleteSessionById(sessionId)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -254,6 +254,9 @@ func (opt *Tunnel) Read() (p []byte, err error) {
|
||||
if s == "rate=44100,channels=2;" {
|
||||
return make([]byte, 0), nil
|
||||
}
|
||||
if s == "rate=22050,channels=2;" {
|
||||
return make([]byte, 0), nil
|
||||
}
|
||||
if s == "5.audio,1.1,31.audio/L16;" {
|
||||
s += "rate=44100,channels=2;"
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package handle
|
||||
import (
|
||||
"github.com/robfig/cron/v3"
|
||||
"github.com/sirupsen/logrus"
|
||||
"log"
|
||||
"next-terminal/pkg/guacd"
|
||||
"next-terminal/pkg/model"
|
||||
"next-terminal/pkg/utils"
|
||||
@ -22,7 +23,7 @@ func RunTicker() {
|
||||
now := time.Now()
|
||||
for i := range sessions {
|
||||
if now.Sub(sessions[i].ConnectedTime.Time) > time.Hour*1 {
|
||||
model.DeleteSessionById(sessions[i].ID)
|
||||
_ = model.DeleteSessionById(sessions[i].ID)
|
||||
s := sessions[i].Username + "@" + sessions[i].IP + ":" + strconv.Itoa(sessions[i].Port)
|
||||
logrus.Infof("会话「%v」ID「%v」超过1小时未打开,已删除。", s, sessions[i].ID)
|
||||
}
|
||||
@ -40,6 +41,40 @@ func RunTicker() {
|
||||
}
|
||||
})
|
||||
|
||||
_, err := c.AddFunc("0 0 0 * * ?", func() {
|
||||
// 定时任务 每日凌晨检查超过时长限制的会话
|
||||
property, err := model.FindPropertyByName("session-saved-limit")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if property.Value == "" || property.Value == "-" {
|
||||
return
|
||||
}
|
||||
limit, err := strconv.Atoi(property.Value)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sessions, err := model.FindOutTimeSessions(limit)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if sessions != nil && len(sessions) > 0 {
|
||||
var sessionIds []string
|
||||
for i := range sessions {
|
||||
sessionIds = append(sessionIds, sessions[i].ID)
|
||||
}
|
||||
err := model.DeleteSessionByIds(sessionIds)
|
||||
if err != nil {
|
||||
logrus.Errorf("删除离线会话失败 %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
c.Start()
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,8 @@ package model
|
||||
import (
|
||||
"next-terminal/pkg/global"
|
||||
"next-terminal/pkg/utils"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -126,6 +128,12 @@ func FindSessionByStatusIn(statuses []string) (o []Session, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func FindOutTimeSessions(dayLimit int) (o []Session, err error) {
|
||||
limitTime := time.Now().Add(time.Duration(-dayLimit*24) * time.Hour)
|
||||
err = global.DB.Where("status = ? and connected_time < ?", Disconnected, limitTime).Find(&o).Error
|
||||
return
|
||||
}
|
||||
|
||||
func CreateNewSession(o *Session) (err error) {
|
||||
err = global.DB.Create(o).Error
|
||||
return
|
||||
@ -154,8 +162,24 @@ func UpdateSessionWindowSizeById(width, height int, id string) error {
|
||||
return UpdateSessionById(&session, id)
|
||||
}
|
||||
|
||||
func DeleteSessionById(id string) {
|
||||
global.DB.Where("id = ?", id).Delete(&Session{})
|
||||
func DeleteSessionById(id string) error {
|
||||
return global.DB.Where("id = ?", id).Delete(&Session{}).Error
|
||||
}
|
||||
|
||||
func DeleteSessionByIds(sessionIds []string) error {
|
||||
drivePath, err := GetRecordingPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range sessionIds {
|
||||
if err := os.RemoveAll(path.Join(drivePath, sessionIds[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := DeleteSessionById(sessionIds[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteSessionByStatus(status string) {
|
||||
|
Reference in New Issue
Block a user