mirror of https://github.com/bjdgyc/anylink.git
防爆增加前端手动解锁的功能
This commit is contained in:
parent
55d7300033
commit
cad74d7fdb
|
@ -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, "解锁成功!")
|
||||
|
@ -112,7 +130,7 @@ func (lm *LockManager) GetLocksInfo() []LockInfo {
|
|||
for ip, state := range lm.ipLocks {
|
||||
if state.Locked {
|
||||
info := LockInfo{
|
||||
Description: "全局 IP 锁定",
|
||||
Description: "全局IP锁定",
|
||||
Username: "",
|
||||
IP: ip,
|
||||
State: &LockState{
|
||||
|
@ -147,7 +165,7 @@ func (lm *LockManager) GetLocksInfo() []LockInfo {
|
|||
for ip, state := range ipStates {
|
||||
if state.Locked {
|
||||
info := LockInfo{
|
||||
Description: "单用户 IP 锁定",
|
||||
Description: "单用户IP锁定",
|
||||
Username: username,
|
||||
IP: ip,
|
||||
State: &LockState{
|
||||
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -1,35 +1,36 @@
|
|||
import Vue from "vue";
|
||||
import VueRouter from "vue-router";
|
||||
import {getToken} from "./token";
|
||||
import { getToken } from "./token";
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
|
||||
const routes = [
|
||||
{path: '/login', component: () => import('@/pages/Login')},
|
||||
{ path: '/login', component: () => import('@/pages/Login') },
|
||||
{
|
||||
path: '/admin',
|
||||
component: () => import('@/layout/Layout'),
|
||||
redirect: '/admin/home',
|
||||
children: [
|
||||
{path: 'home', component: () => import('@/pages/Home')},
|
||||
{ path: 'home', component: () => import('@/pages/Home') },
|
||||
|
||||
{path: 'set/system', component: () => import('@/pages/set/System')},
|
||||
{path: 'set/soft', component: () => import('@/pages/set/Soft')},
|
||||
{path: 'set/other', component: () => import('@/pages/set/Other')},
|
||||
{path: 'set/audit', component: () => import('@/pages/set/Audit')},
|
||||
{ path: 'set/system', component: () => import('@/pages/set/System') },
|
||||
{ path: 'set/soft', component: () => import('@/pages/set/Soft') },
|
||||
{ path: 'set/other', component: () => import('@/pages/set/Other') },
|
||||
{ path: 'set/audit', component: () => import('@/pages/set/Audit') },
|
||||
|
||||
{path: 'user/list', component: () => import('@/pages/user/List')},
|
||||
{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/list', component: () => import('@/pages/user/List') },
|
||||
{ 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')},
|
||||
{ path: 'group/list', component: () => import('@/pages/group/List') },
|
||||
|
||||
],
|
||||
},
|
||||
|
||||
{path: '*', redirect: '/admin/home'},
|
||||
{ path: '*', redirect: '/admin/home' },
|
||||
]
|
||||
|
||||
// 3. 创建 router 实例,然后传 `routes` 配置
|
||||
|
@ -64,7 +65,7 @@ router.beforeEach((to, from, next) => {
|
|||
}
|
||||
|
||||
if (to.path === "/login") {
|
||||
next({path: '/admin/home'});
|
||||
next({ path: '/admin/home' });
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue