防爆增加前端手动解锁的功能

This commit is contained in:
wsczx 2024-10-31 10:04:35 +08:00
parent 55d7300033
commit cad74d7fdb
5 changed files with 157 additions and 32 deletions

View File

@ -97,7 +97,25 @@ func UnlockUser(w http.ResponseWriter, r *http.Request) {
lm.mu.Lock()
defer lm.mu.Unlock()
lm.Unlock(lockinfo.State)
// 根据用户名和IP查找锁定状态
var state *LockState
switch {
case lockinfo.IP == "" && lockinfo.Username != "":
state = lm.userLocks[lockinfo.Username] // 全局用户锁定
case lockinfo.Username != "" && lockinfo.IP != "":
if userIPMap, exists := lm.ipUserLocks[lockinfo.Username]; exists {
state = userIPMap[lockinfo.IP] // 单用户 IP 锁定
}
default:
state = lm.ipLocks[lockinfo.IP] // 全局 IP 锁定
}
if state == nil || !state.Locked {
RespError(w, RespInternalErr, fmt.Errorf("锁定状态未找到或已解锁"))
return
}
lm.Unlock(state)
base.Info("解锁成功:", lockinfo.Description, lockinfo.Username, lockinfo.IP)
RespSucess(w, "解锁成功!")
@ -205,7 +223,7 @@ func (lm *LockManager) IsWhitelisted(ip string) bool {
}
func (lm *LockManager) StartCleanupTicker() {
lm.cleanupTicker = time.NewTicker(5 * time.Minute)
lm.cleanupTicker = time.NewTicker(1 * time.Minute)
go func() {
for range lm.cleanupTicker.C {
lm.CleanupExpiredLocks()
@ -220,20 +238,23 @@ func (lm *LockManager) CleanupExpiredLocks() {
defer lm.mu.Unlock()
for ip, state := range lm.ipLocks {
if now.Sub(state.LastAttempt) > time.Duration(base.Cfg.GlobalLockStateExpirationTime)*time.Second {
if !lm.CheckLockState(state, now, base.Cfg.GlobalIPLockTime) ||
now.Sub(state.LastAttempt) > time.Duration(base.Cfg.GlobalLockStateExpirationTime)*time.Second {
delete(lm.ipLocks, ip)
}
}
for user, state := range lm.userLocks {
if now.Sub(state.LastAttempt) > time.Duration(base.Cfg.GlobalLockStateExpirationTime)*time.Second {
if !lm.CheckLockState(state, now, base.Cfg.GlobalUserLockTime) ||
now.Sub(state.LastAttempt) > time.Duration(base.Cfg.GlobalLockStateExpirationTime)*time.Second {
delete(lm.userLocks, user)
}
}
for user, ipMap := range lm.ipUserLocks {
for ip, state := range ipMap {
if now.Sub(state.LastAttempt) > time.Duration(base.Cfg.GlobalLockStateExpirationTime)*time.Second {
if !lm.CheckLockState(state, now, base.Cfg.LockTime) ||
now.Sub(state.LastAttempt) > time.Duration(base.Cfg.GlobalLockStateExpirationTime)*time.Second {
delete(ipMap, ip)
if len(ipMap) == 0 {
delete(lm.ipUserLocks, user)

View File

@ -88,6 +88,7 @@ func StartAdmin() {
r.HandleFunc("/statsinfo/list", StatsInfoList)
r.HandleFunc("/locksinfo/list", GetLocksInfo)
r.HandleFunc("/locksinfo/unlok", UnlockUser)
// pprof
if base.Cfg.Pprof {

View File

@ -7,17 +7,9 @@
<!--<div class="layout-aside" :style="aside_style">-->
<el-menu :collapse="!is_active"
:default-active="route_path"
:style="is_active?'width:200px':''"
router
class="layout-menu"
:collapse-transition="false"
background-color="#545c64"
text-color="#fff"
active-text-color="#ffd04b"
>
<el-menu :collapse="!is_active" :default-active="route_path" :style="is_active ? 'width:200px' : ''" router
class="layout-menu" :collapse-transition="false" background-color="#545c64" text-color="#fff"
active-text-color="#ffd04b">
<el-menu-item index="/admin/home">
<i class="el-icon-s-home"></i>
<span slot="title">首页</span>
@ -44,6 +36,7 @@
<el-menu-item index="/admin/user/list">用户列表</el-menu-item>
<el-menu-item index="/admin/user/policy">用户策略</el-menu-item>
<el-menu-item index="/admin/user/online">在线用户</el-menu-item>
<el-menu-item index="/admin/user/lockmanager">锁定管理</el-menu-item>
<el-menu-item index="/admin/user/ip_map">IP映射</el-menu-item>
</el-submenu>

View File

@ -0,0 +1,109 @@
<template>
<div id="lock-manager">
<el-card>
<div slot="header">
<el-button type="primary" @click="getLocks">刷新信息</el-button>
</div>
<el-table :data="locksInfo" style="width: 100%" border>
<el-table-column type="index" label="序号" width="60"></el-table-column>
<el-table-column prop="description" label="描述"></el-table-column>
<el-table-column prop="username" label="用户名"></el-table-column>
<el-table-column prop="ip" label="IP地址"></el-table-column>
<el-table-column prop="state.locked" label="状态" width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.state.locked ? 'danger' : 'success'">
{{ scope.row.state.locked ? '已锁定' : '未锁定' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="state.attempts" label="失败次数"></el-table-column>
<el-table-column prop="state.lock_time" label="锁定截止时间">
<template slot-scope="scope">
{{ formatDate(scope.row.state.lock_time) }}
</template>
</el-table-column>
<el-table-column prop="state.lastAttempt" label="最后尝试时间">
<template slot-scope="scope">
{{ formatDate(scope.row.state.lastAttempt) }}
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<div class="button">
<el-button size="small" type="danger" @click="unlock(scope.row)">
解锁
</el-button>
</div>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'LockManager',
data() {
return {
locksInfo: []
};
},
methods: {
getLocks() {
axios.get('/locksinfo/list')
.then(response => {
this.locksInfo = response.data.data;
})
.catch(error => {
console.error('Failed to get locks info:', error);
this.$message.error('无法获取锁信息,请稍后再试。');
});
},
unlock(lock) {
const lockInfo = {
state: { locked: false },
username: lock.username,
ip: lock.ip,
description: lock.description
};
axios.post('/locksinfo/unlok', lockInfo)
.then(() => {
this.$message.success('解锁成功!');
this.getLocks();
})
.catch(error => {
console.error('Failed to unlock:', error);
this.$message.error('解锁失败,请稍后再试。');
});
},
formatDate(dateString) {
if (!dateString) return '';
const date = new Date(dateString);
return new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}).format(date);
}
},
created() {
this.getLocks();
}
};
</script>
<style scoped>
.button {
display: flex;
justify-content: center;
align-items: center;
}
</style>

View File

@ -23,6 +23,7 @@ const routes = [
{ path: 'user/policy', component: () => import('@/pages/user/Policy') },
{ path: 'user/online', component: () => import('@/pages/user/Online') },
{ path: 'user/ip_map', component: () => import('@/pages/user/IpMap') },
{ path: 'user/lockmanager', component: () => import('@/pages/user/LockManager') },
{ path: 'group/list', component: () => import('@/pages/group/List') },