Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
jinli gu
Eladmin
Commits
788aaf33
Commit
788aaf33
authored
Feb 28, 2022
by
Zheng Jie
Browse files
Merge branch 'master' into deploy
parents
e39ae1cb
0705b123
Changes
5
Hide whitespace changes
Inline
Side-by-side
eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserCacheClean.java
View file @
788aaf33
...
...
@@ -16,6 +16,7 @@
package
me.zhengjie.modules.security.service
;
import
lombok.AllArgsConstructor
;
import
me.zhengjie.utils.StringUtils
;
import
org.springframework.stereotype.Component
;
...
...
@@ -25,8 +26,11 @@ import org.springframework.stereotype.Component;
* @apiNote: 用于清理 用户登录信息缓存,为防止Spring循环依赖与安全考虑 ,单独构成工具类
*/
@Component
@AllArgsConstructor
public
class
UserCacheClean
{
private
final
UserCacheManager
userCacheManager
;
/**
* 清理特定用户缓存信息<br>
* 用户信息变更时
...
...
@@ -35,7 +39,7 @@ public class UserCacheClean {
*/
public
void
cleanUserCache
(
String
userName
)
{
if
(
StringUtils
.
isNotEmpty
(
userName
))
{
U
ser
DetailsServiceImpl
.
USER_DTO_CACHE
.
remove
(
userName
);
u
ser
CacheManager
.
remove
(
userName
);
}
}
...
...
@@ -44,6 +48,6 @@ public class UserCacheClean {
* ,如发生角色授权信息变化,可以简便的全部失效缓存
*/
public
void
cleanAll
()
{
U
ser
DetailsServiceImpl
.
USER_DTO_CACHE
.
clear
();
u
ser
CacheManager
.
clear
();
}
}
eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserCacheManager.java
0 → 100644
View file @
788aaf33
package
me.zhengjie.modules.security.service
;
import
lombok.extern.slf4j.Slf4j
;
import
me.zhengjie.modules.security.service.dto.JwtUserDto
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.stereotype.Component
;
import
java.util.Iterator
;
import
java.util.Map
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.Future
;
import
java.util.concurrent.atomic.AtomicBoolean
;
/**
* 用户缓存
*
* @author TikiWong
* @date 2022/1/27 8:23
**/
@Slf4j
@Component
public
class
UserCacheManager
{
@Value
(
"${user-cache.min-evictable-size}"
)
private
int
minEvictableSize
;
@Value
(
"${user-cache.min-evictable-interval}"
)
private
long
minEvictableInterval
;
@Value
(
"${user-cache.min-idle-time}"
)
private
long
minIdleTime
;
private
final
Map
<
String
,
Node
>
cache
=
new
ConcurrentHashMap
<>();
private
final
AtomicBoolean
expelLock
=
new
AtomicBoolean
(
true
);
private
long
nextMinEvictableTime
=
0
;
public
Future
<
JwtUserDto
>
putIfAbsent
(
String
username
,
Future
<
JwtUserDto
>
ft
)
{
Node
tryNode
=
new
Node
(
ft
);
Node
node
=
cache
.
putIfAbsent
(
username
,
tryNode
);
expel
();
return
nodeToDate
(
node
);
}
/**
* 缓存回收
* 为避免超过边界后回收热点数据设置了最小生存时间
* 回收时会保留在最小生存时间内的数据
**/
public
void
expel
()
{
long
now
=
System
.
currentTimeMillis
();
if
(
cache
.
size
()
<
minEvictableSize
||
now
<
nextMinEvictableTime
||
!
expelLock
.
compareAndSet
(
true
,
false
))
{
return
;
}
long
oldestTime
=
now
;
int
evictedCount
=
0
;
try
{
Iterator
<
Map
.
Entry
<
String
,
Node
>>
iterator
=
cache
.
entrySet
().
iterator
();
while
(
iterator
.
hasNext
())
{
Map
.
Entry
<
String
,
Node
>
entry
=
iterator
.
next
();
long
nodeTime
=
entry
.
getValue
().
getTime
();
if
(
nodeTime
+
minIdleTime
<
now
)
{
iterator
.
remove
();
evictedCount
++;
}
oldestTime
=
Math
.
min
(
oldestTime
,
nodeTime
);
}
}
finally
{
this
.
nextMinEvictableTime
=
Math
.
max
(
now
+
minEvictableInterval
,
oldestTime
);
expelLock
.
set
(
true
);
log
.
info
(
"回收掉【{}】条用户缓存, 剩余缓存数为【{}】,下次可回收时间为【{}】秒后"
,
evictedCount
,
cache
.
size
(),
(
this
.
nextMinEvictableTime
-
now
)
/
1000
);
}
}
public
Future
<
JwtUserDto
>
get
(
String
username
)
{
return
nodeToDate
(
cache
.
get
(
username
));
}
public
void
clear
()
{
cache
.
clear
();
}
public
void
remove
(
String
username
)
{
cache
.
remove
(
username
);
}
private
Future
<
JwtUserDto
>
nodeToDate
(
Node
node
)
{
return
node
==
null
?
null
:
node
.
getData
();
}
private
static
class
Node
{
private
final
Future
<
JwtUserDto
>
data
;
private
final
long
time
;
public
Node
(
Future
<
JwtUserDto
>
data
)
{
this
.
data
=
data
;
this
.
time
=
System
.
currentTimeMillis
();
}
public
Future
<
JwtUserDto
>
getData
()
{
return
data
;
}
public
long
getTime
()
{
return
time
;
}
}
}
eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserDetailsServiceImpl.java
View file @
788aaf33
...
...
@@ -29,7 +29,6 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
import
org.springframework.stereotype.Service
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.concurrent.*
;
import
java.util.concurrent.atomic.AtomicInteger
;
...
...
@@ -45,17 +44,12 @@ public class UserDetailsServiceImpl implements UserDetailsService {
private
final
DataService
dataService
;
private
final
LoginProperties
loginProperties
;
private
final
UserCacheManager
USER_DTO_CACHE
;
public
void
setEnableCache
(
boolean
enableCache
)
{
this
.
loginProperties
.
setCacheEnable
(
enableCache
);
}
/**
* 用户信息缓存
*
* @see {@link UserCacheClean}
*/
final
static
Map
<
String
,
Future
<
JwtUserDto
>>
USER_DTO_CACHE
=
new
ConcurrentHashMap
<>();
public
static
ExecutorService
executor
=
newThreadPool
();
@Override
...
...
@@ -68,7 +62,7 @@ public class UserDetailsServiceImpl implements UserDetailsService {
user
=
userService
.
findByName
(
username
);
}
catch
(
EntityNotFoundException
e
)
{
// SpringSecurity会自动转换UsernameNotFoundException为BadCredentialsException
throw
new
UsernameNotFoundException
(
""
,
e
);
throw
new
UsernameNotFoundException
(
username
,
e
);
}
if
(
user
==
null
)
{
throw
new
UsernameNotFoundException
(
""
);
...
...
@@ -85,25 +79,26 @@ public class UserDetailsServiceImpl implements UserDetailsService {
return
jwtUserDto
;
}
if
(
future
==
null
)
{
Callable
<
JwtUserDto
>
call
=
()->
getJwtBySearchDb
(
username
);
FutureTask
<
JwtUserDto
>
ft
=
new
FutureTask
<>(
call
);
future
=
USER_DTO_CACHE
.
putIfAbsent
(
username
,
ft
);
if
(
future
==
null
){
future
=
ft
;
if
(
future
==
null
)
{
Callable
<
JwtUserDto
>
call
=
()
->
getJwtBySearchDb
(
username
);
FutureTask
<
JwtUserDto
>
ft
=
new
FutureTask
<>(
call
);
future
=
USER_DTO_CACHE
.
putIfAbsent
(
username
,
ft
);
if
(
future
==
null
)
{
future
=
ft
;
executor
.
submit
(
ft
);
}
try
{
try
{
return
future
.
get
();
}
catch
(
CancellationException
e
){
}
catch
(
CancellationException
e
)
{
USER_DTO_CACHE
.
remove
(
username
);
}
catch
(
InterruptedException
|
ExecutionException
e
)
{
System
.
out
.
println
(
"error"
+
Thread
.
currentThread
().
getName
());
}
catch
(
InterruptedException
|
ExecutionException
e
)
{
throw
new
RuntimeException
(
e
.
getMessage
());
}
}
else
{
}
else
{
try
{
jwtUserDto
=
future
.
get
();
}
catch
(
InterruptedException
|
ExecutionException
e
)
{
jwtUserDto
=
future
.
get
();
}
catch
(
InterruptedException
|
ExecutionException
e
)
{
throw
new
RuntimeException
(
e
.
getMessage
());
}
// 检查dataScope是否修改
...
...
eladmin-system/src/main/resources/config/application.yml
View file @
788aaf33
...
...
@@ -54,3 +54,12 @@ code:
#密码加密传输,前端公钥加密,后端私钥解密
rsa
:
private_key
:
MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A==
# 内存用户缓存配置
user-cache
:
# 最小回收数(当缓存数量达到此值时进行回收)
min-evictable-size
:
512
# 最小回收间隔
min-evictable-interval
:
1800000
# 最小存活时间 (ms)
min-idle-time
:
3600000
\ No newline at end of file
eladmin-system/src/test/java/me/zhengjie/LoginCacheTest.java
View file @
788aaf33
...
...
@@ -41,4 +41,24 @@ public class LoginCacheTest {
System
.
out
.
print
(
"使用缓存:"
+
(
end1
-
start1
)
+
"毫秒\n 不使用缓存:"
+
(
end2
-
start2
)
+
"毫秒"
);
}
@Test
public
void
testCacheManager
()
throws
InterruptedException
{
int
size
=
1000
;
CountDownLatch
latch
=
new
CountDownLatch
(
size
);
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
int
mod
=
i
%
10
;
executor
.
submit
(()
->
{
try
{
Thread
.
sleep
(
mod
*
2
+
(
int
)
(
Math
.
random
()
*
10000
));
}
catch
(
InterruptedException
e
)
{
e
.
printStackTrace
();
}
userDetailsService
.
loadUserByUsername
(
"admin"
+
mod
);
latch
.
countDown
();
System
.
out
.
println
(
"剩余未完成数量"
+
latch
.
getCount
());
});
}
latch
.
await
();
}
}
Write
Preview
Supports
Markdown
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