Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
S
sts网站
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
liyang
sts网站
Commits
1268637e
Commit
1268637e
authored
Feb 21, 2023
by
RuoYi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
支持登录IP黑名单限制
parent
61caa796
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
215 additions
and
24 deletions
+215
-24
messages.properties
ruoyi-admin/src/main/resources/i18n/messages.properties
+1
-0
BlackListException.java
...a/com/ruoyi/common/exception/user/BlackListException.java
+16
-0
UserNotExistsException.java
...m/ruoyi/common/exception/user/UserNotExistsException.java
+16
-0
IpUtils.java
...mmon/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java
+119
-1
LogAspect.java
.../src/main/java/com/ruoyi/framework/aspectj/LogAspect.java
+1
-1
RateLimiterAspect.java
...n/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java
+1
-2
AsyncFactory.java
...ava/com/ruoyi/framework/manager/factory/AsyncFactory.java
+1
-1
SysLoginService.java
...java/com/ruoyi/framework/web/service/SysLoginService.java
+57
-17
TokenService.java
...in/java/com/ruoyi/framework/web/service/TokenService.java
+1
-1
index.vue
ruoyi-ui/src/views/monitor/logininfor/index.vue
+1
-1
ry_20230221.sql
sql/ry_20230221.sql
+1
-0
No files found.
ruoyi-admin/src/main/resources/i18n/messages.properties
View file @
1268637e
...
...
@@ -9,6 +9,7 @@ user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分
user.password.delete
=
对不起,您的账号已被删除
user.blocked
=
用户已封禁,请联系管理员
role.blocked
=
角色已封禁,请联系管理员
login.blocked
=
很遗憾,访问IP已被列入系统黑名单
user.logout.success
=
退出成功
length.not.valid
=
长度必须在{min}到{max}个字符之间
...
...
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/BlackListException.java
0 → 100644
View file @
1268637e
package
com
.
ruoyi
.
common
.
exception
.
user
;
/**
* 黑名单IP异常类
*
* @author ruoyi
*/
public
class
BlackListException
extends
UserException
{
private
static
final
long
serialVersionUID
=
1L
;
public
BlackListException
()
{
super
(
"login.blocked"
,
null
);
}
}
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java
0 → 100644
View file @
1268637e
package
com
.
ruoyi
.
common
.
exception
.
user
;
/**
* 用户不存在异常类
*
* @author ruoyi
*/
public
class
UserNotExistsException
extends
UserException
{
private
static
final
long
serialVersionUID
=
1L
;
public
UserNotExistsException
()
{
super
(
"user.not.exists"
,
null
);
}
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java
View file @
1268637e
...
...
@@ -3,6 +3,7 @@ package com.ruoyi.common.utils.ip;
import
java.net.InetAddress
;
import
java.net.UnknownHostException
;
import
javax.servlet.http.HttpServletRequest
;
import
com.ruoyi.common.utils.ServletUtils
;
import
com.ruoyi.common.utils.StringUtils
;
/**
...
...
@@ -12,6 +13,23 @@ import com.ruoyi.common.utils.StringUtils;
*/
public
class
IpUtils
{
public
final
static
String
REGX_0_255
=
"(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)"
;
// 匹配 ip
public
final
static
String
REGX_IP
=
"(("
+
REGX_0_255
+
"\\.){3}"
+
REGX_0_255
+
")"
;
public
final
static
String
REGX_IP_WILDCARD
=
"(((\\*\\.){3}\\*)|("
+
REGX_0_255
+
"(\\.\\*){3})|("
+
REGX_0_255
+
"\\."
+
REGX_0_255
+
")(\\.\\*){2}"
+
"|(("
+
REGX_0_255
+
"\\.){3}\\*))"
;
// 匹配网段
public
final
static
String
REGX_IP_SEG
=
"("
+
REGX_IP
+
"\\-"
+
REGX_IP
+
")"
;
/**
* 获取客户端IP
*
* @return IP地址
*/
public
static
String
getIpAddr
()
{
return
getIpAddr
(
ServletUtils
.
getRequest
());
}
/**
* 获取客户端IP
*
...
...
@@ -248,7 +266,7 @@ public class IpUtils
}
}
}
return
ip
;
return
StringUtils
.
substring
(
ip
,
0
,
255
)
;
}
/**
...
...
@@ -261,4 +279,104 @@ public class IpUtils
{
return
StringUtils
.
isBlank
(
checkString
)
||
"unknown"
.
equalsIgnoreCase
(
checkString
);
}
/**
* 是否为IP
*/
public
static
boolean
isIP
(
String
ip
)
{
return
StringUtils
.
isNotBlank
(
ip
)
&&
ip
.
matches
(
REGX_IP
);
}
/**
* 是否为IP,或 *为间隔的通配符地址
*/
public
static
boolean
isIpWildCard
(
String
ip
)
{
return
StringUtils
.
isNotBlank
(
ip
)
&&
ip
.
matches
(
REGX_IP_WILDCARD
);
}
/**
* 检测参数是否在ip通配符里
*/
public
static
boolean
ipIsInWildCardNoCheck
(
String
ipWildCard
,
String
ip
)
{
String
[]
s1
=
ipWildCard
.
split
(
"\\."
);
String
[]
s2
=
ip
.
split
(
"\\."
);
boolean
isMatchedSeg
=
true
;
for
(
int
i
=
0
;
i
<
s1
.
length
&&
!
s1
[
i
].
equals
(
"*"
);
i
++)
{
if
(!
s1
[
i
].
equals
(
s2
[
i
]))
{
isMatchedSeg
=
false
;
break
;
}
}
return
isMatchedSeg
;
}
/**
* 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串
*/
public
static
boolean
isIPSegment
(
String
ipSeg
)
{
return
StringUtils
.
isNotBlank
(
ipSeg
)
&&
ipSeg
.
matches
(
REGX_IP_SEG
);
}
/**
* 判断ip是否在指定网段中
*/
public
static
boolean
ipIsInNetNoCheck
(
String
iparea
,
String
ip
)
{
int
idx
=
iparea
.
indexOf
(
'-'
);
String
[]
sips
=
iparea
.
substring
(
0
,
idx
).
split
(
"\\."
);
String
[]
sipe
=
iparea
.
substring
(
idx
+
1
).
split
(
"\\."
);
String
[]
sipt
=
ip
.
split
(
"\\."
);
long
ips
=
0L
,
ipe
=
0L
,
ipt
=
0L
;
for
(
int
i
=
0
;
i
<
4
;
++
i
)
{
ips
=
ips
<<
8
|
Integer
.
parseInt
(
sips
[
i
]);
ipe
=
ipe
<<
8
|
Integer
.
parseInt
(
sipe
[
i
]);
ipt
=
ipt
<<
8
|
Integer
.
parseInt
(
sipt
[
i
]);
}
if
(
ips
>
ipe
)
{
long
t
=
ips
;
ips
=
ipe
;
ipe
=
t
;
}
return
ips
<=
ipt
&&
ipt
<=
ipe
;
}
/**
* 校验ip是否符合过滤串规则
*
* @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99`
* @param ip 校验IP地址
* @return boolean 结果
*/
public
static
boolean
isMatchedIp
(
String
filter
,
String
ip
)
{
if
(
StringUtils
.
isEmpty
(
filter
)
&&
StringUtils
.
isEmpty
(
ip
))
{
return
false
;
}
String
[]
ips
=
filter
.
split
(
";"
);
for
(
String
iStr
:
ips
)
{
if
(
isIP
(
iStr
)
&&
iStr
.
equals
(
ip
))
{
return
true
;
}
else
if
(
isIpWildCard
(
iStr
)
&&
ipIsInWildCardNoCheck
(
iStr
,
ip
))
{
return
true
;
}
else
if
(
isIPSegment
(
iStr
)
&&
ipIsInNetNoCheck
(
iStr
,
ip
))
{
return
true
;
}
}
return
false
;
}
}
\ No newline at end of file
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java
View file @
1268637e
...
...
@@ -90,7 +90,7 @@ public class LogAspect
SysOperLog
operLog
=
new
SysOperLog
();
operLog
.
setStatus
(
BusinessStatus
.
SUCCESS
.
ordinal
());
// 请求的地址
String
ip
=
IpUtils
.
getIpAddr
(
ServletUtils
.
getRequest
()
);
String
ip
=
IpUtils
.
getIpAddr
();
operLog
.
setOperIp
(
ip
);
operLog
.
setOperUrl
(
StringUtils
.
substring
(
ServletUtils
.
getRequest
().
getRequestURI
(),
0
,
255
));
if
(
loginUser
!=
null
)
...
...
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java
View file @
1268637e
...
...
@@ -16,7 +16,6 @@ import org.springframework.stereotype.Component;
import
com.ruoyi.common.annotation.RateLimiter
;
import
com.ruoyi.common.enums.LimitType
;
import
com.ruoyi.common.exception.ServiceException
;
import
com.ruoyi.common.utils.ServletUtils
;
import
com.ruoyi.common.utils.StringUtils
;
import
com.ruoyi.common.utils.ip.IpUtils
;
...
...
@@ -79,7 +78,7 @@ public class RateLimiterAspect
StringBuffer
stringBuffer
=
new
StringBuffer
(
rateLimiter
.
key
());
if
(
rateLimiter
.
limitType
()
==
LimitType
.
IP
)
{
stringBuffer
.
append
(
IpUtils
.
getIpAddr
(
ServletUtils
.
getRequest
()
)).
append
(
"-"
);
stringBuffer
.
append
(
IpUtils
.
getIpAddr
()).
append
(
"-"
);
}
MethodSignature
signature
=
(
MethodSignature
)
point
.
getSignature
();
Method
method
=
signature
.
getMethod
();
...
...
ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java
View file @
1268637e
...
...
@@ -38,7 +38,7 @@ public class AsyncFactory
final
Object
...
args
)
{
final
UserAgent
userAgent
=
UserAgent
.
parseUserAgentString
(
ServletUtils
.
getRequest
().
getHeader
(
"User-Agent"
));
final
String
ip
=
IpUtils
.
getIpAddr
(
ServletUtils
.
getRequest
()
);
final
String
ip
=
IpUtils
.
getIpAddr
();
return
new
TimerTask
()
{
@Override
...
...
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java
View file @
1268637e
...
...
@@ -9,16 +9,18 @@ import org.springframework.security.core.Authentication;
import
org.springframework.stereotype.Component
;
import
com.ruoyi.common.constant.CacheConstants
;
import
com.ruoyi.common.constant.Constants
;
import
com.ruoyi.common.constant.UserConstants
;
import
com.ruoyi.common.core.domain.entity.SysUser
;
import
com.ruoyi.common.core.domain.model.LoginUser
;
import
com.ruoyi.common.core.redis.RedisCache
;
import
com.ruoyi.common.exception.ServiceException
;
import
com.ruoyi.common.exception.user.BlackListException
;
import
com.ruoyi.common.exception.user.CaptchaException
;
import
com.ruoyi.common.exception.user.CaptchaExpireException
;
import
com.ruoyi.common.exception.user.UserNotExistsException
;
import
com.ruoyi.common.exception.user.UserPasswordNotMatchException
;
import
com.ruoyi.common.utils.DateUtils
;
import
com.ruoyi.common.utils.MessageUtils
;
import
com.ruoyi.common.utils.ServletUtils
;
import
com.ruoyi.common.utils.StringUtils
;
import
com.ruoyi.common.utils.ip.IpUtils
;
import
com.ruoyi.framework.manager.AsyncManager
;
...
...
@@ -61,12 +63,10 @@ public class SysLoginService
*/
public
String
login
(
String
username
,
String
password
,
String
code
,
String
uuid
)
{
boolean
captchaEnabled
=
configService
.
selectCaptchaEnabled
();
// 验证码开关
if
(
captchaEnabled
)
{
validateCaptcha
(
username
,
code
,
uuid
);
}
// 验证码校验
validateCaptcha
(
username
,
code
,
uuid
);
// 登录前置校验
loginPreCheck
(
username
,
password
);
// 用户验证
Authentication
authentication
=
null
;
try
...
...
@@ -110,18 +110,58 @@ public class SysLoginService
*/
public
void
validateCaptcha
(
String
username
,
String
code
,
String
uuid
)
{
String
verifyKey
=
CacheConstants
.
CAPTCHA_CODE_KEY
+
StringUtils
.
nvl
(
uuid
,
""
);
String
captcha
=
redisCache
.
getCacheObject
(
verifyKey
);
redisCache
.
deleteObject
(
verifyKey
);
if
(
captcha
==
null
)
boolean
captchaEnabled
=
configService
.
selectCaptchaEnabled
();
if
(
captchaEnabled
)
{
String
verifyKey
=
CacheConstants
.
CAPTCHA_CODE_KEY
+
StringUtils
.
nvl
(
uuid
,
""
);
String
captcha
=
redisCache
.
getCacheObject
(
verifyKey
);
redisCache
.
deleteObject
(
verifyKey
);
if
(
captcha
==
null
)
{
AsyncManager
.
me
().
execute
(
AsyncFactory
.
recordLogininfor
(
username
,
Constants
.
LOGIN_FAIL
,
MessageUtils
.
message
(
"user.jcaptcha.expire"
)));
throw
new
CaptchaExpireException
();
}
if
(!
code
.
equalsIgnoreCase
(
captcha
))
{
AsyncManager
.
me
().
execute
(
AsyncFactory
.
recordLogininfor
(
username
,
Constants
.
LOGIN_FAIL
,
MessageUtils
.
message
(
"user.jcaptcha.error"
)));
throw
new
CaptchaException
();
}
}
}
/**
* 登录前置校验
* @param username 用户名
* @param password 用户密码
*/
public
void
loginPreCheck
(
String
username
,
String
password
)
{
// 用户名或密码为空 错误
if
(
StringUtils
.
isEmpty
(
username
)
||
StringUtils
.
isEmpty
(
password
))
{
AsyncManager
.
me
().
execute
(
AsyncFactory
.
recordLogininfor
(
username
,
Constants
.
LOGIN_FAIL
,
MessageUtils
.
message
(
"not.null"
)));
throw
new
UserNotExistsException
();
}
// 密码如果不在指定范围内 错误
if
(
password
.
length
()
<
UserConstants
.
PASSWORD_MIN_LENGTH
||
password
.
length
()
>
UserConstants
.
PASSWORD_MAX_LENGTH
)
{
AsyncManager
.
me
().
execute
(
AsyncFactory
.
recordLogininfor
(
username
,
Constants
.
LOGIN_FAIL
,
MessageUtils
.
message
(
"user.password.not.match"
)));
throw
new
UserPasswordNotMatchException
();
}
// 用户名不在指定范围内 错误
if
(
username
.
length
()
<
UserConstants
.
USERNAME_MIN_LENGTH
||
username
.
length
()
>
UserConstants
.
USERNAME_MAX_LENGTH
)
{
AsyncManager
.
me
().
execute
(
AsyncFactory
.
recordLogininfor
(
username
,
Constants
.
LOGIN_FAIL
,
MessageUtils
.
message
(
"user.
jcaptcha.expire
"
)));
throw
new
CaptchaExpire
Exception
();
AsyncManager
.
me
().
execute
(
AsyncFactory
.
recordLogininfor
(
username
,
Constants
.
LOGIN_FAIL
,
MessageUtils
.
message
(
"user.
password.not.match
"
)));
throw
new
UserPasswordNotMatch
Exception
();
}
if
(!
code
.
equalsIgnoreCase
(
captcha
))
// IP黑名单校验
String
blackStr
=
configService
.
selectConfigByKey
(
"sys.login.blackIPList"
);
if
(
IpUtils
.
isMatchedIp
(
blackStr
,
IpUtils
.
getIpAddr
()))
{
AsyncManager
.
me
().
execute
(
AsyncFactory
.
recordLogininfor
(
username
,
Constants
.
LOGIN_FAIL
,
MessageUtils
.
message
(
"
user.jcaptcha.error
"
)));
throw
new
Captcha
Exception
();
AsyncManager
.
me
().
execute
(
AsyncFactory
.
recordLogininfor
(
username
,
Constants
.
LOGIN_FAIL
,
MessageUtils
.
message
(
"
login.blocked
"
)));
throw
new
BlackList
Exception
();
}
}
...
...
@@ -134,7 +174,7 @@ public class SysLoginService
{
SysUser
sysUser
=
new
SysUser
();
sysUser
.
setUserId
(
userId
);
sysUser
.
setLoginIp
(
IpUtils
.
getIpAddr
(
ServletUtils
.
getRequest
()
));
sysUser
.
setLoginIp
(
IpUtils
.
getIpAddr
());
sysUser
.
setLoginDate
(
DateUtils
.
getNowDate
());
userService
.
updateUserProfile
(
sysUser
);
}
...
...
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java
View file @
1268637e
...
...
@@ -156,7 +156,7 @@ public class TokenService
public
void
setUserAgent
(
LoginUser
loginUser
)
{
UserAgent
userAgent
=
UserAgent
.
parseUserAgentString
(
ServletUtils
.
getRequest
().
getHeader
(
"User-Agent"
));
String
ip
=
IpUtils
.
getIpAddr
(
ServletUtils
.
getRequest
()
);
String
ip
=
IpUtils
.
getIpAddr
();
loginUser
.
setIpaddr
(
ip
);
loginUser
.
setLoginLocation
(
AddressUtils
.
getRealAddressByIP
(
ip
));
loginUser
.
setBrowser
(
userAgent
.
getBrowser
().
getName
());
...
...
ruoyi-ui/src/views/monitor/logininfor/index.vue
View file @
1268637e
...
...
@@ -110,7 +110,7 @@
<dict-tag
:options=
"dict.type.sys_common_status"
:value=
"scope.row.status"
/>
</
template
>
</el-table-column>
<el-table-column
label=
"操作信息"
align=
"center"
prop=
"msg"
/>
<el-table-column
label=
"操作信息"
align=
"center"
prop=
"msg"
:show-overflow-tooltip=
"true"
/>
<el-table-column
label=
"登录日期"
align=
"center"
prop=
"loginTime"
sortable=
"custom"
:sort-orders=
"['descending', 'ascending']"
width=
"180"
>
<
template
slot-scope=
"scope"
>
<span>
{{
parseTime
(
scope
.
row
.
loginTime
)
}}
</span>
...
...
sql/ry_202302
16
.sql
→
sql/ry_202302
21
.sql
View file @
1268637e
...
...
@@ -545,6 +545,7 @@ insert into sys_config values(2, '用户管理-账号初始密码', 'sys
insert
into
sys_config
values
(
3
,
'主框架页-侧边栏主题'
,
'sys.index.sideTheme'
,
'theme-dark'
,
'Y'
,
'admin'
,
sysdate
(),
''
,
null
,
'深色主题theme-dark,浅色主题theme-light'
);
insert
into
sys_config
values
(
4
,
'账号自助-验证码开关'
,
'sys.account.captchaEnabled'
,
'true'
,
'Y'
,
'admin'
,
sysdate
(),
''
,
null
,
'是否开启验证码功能(true开启,false关闭)'
);
insert
into
sys_config
values
(
5
,
'账号自助-是否开启用户注册功能'
,
'sys.account.registerUser'
,
'false'
,
'Y'
,
'admin'
,
sysdate
(),
''
,
null
,
'是否开启注册用户功能(true开启,false关闭)'
);
insert
into
sys_config
values
(
6
,
'用户登录-黑名单列表'
,
'sys.login.blackIPList'
,
''
,
'Y'
,
'admin'
,
sysdate
(),
''
,
null
,
'设置登录IP黑名单限制,多个匹配项以;分隔,支持匹配(*通配、网段)'
);
-- ----------------------------
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment