Skip to content

Commit

Permalink
perf: 鉴权逻辑增强,支持单用户不用设备的高并发场景
Browse files Browse the repository at this point in the history
  • Loading branch information
lichong-a committed Sep 17, 2024
1 parent 19f2bd5 commit b161d90
Show file tree
Hide file tree
Showing 3 changed files with 12 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
package org.funcode.portal.server.common.core.security.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
Expand Down Expand Up @@ -43,7 +42,7 @@ public class CustomAuthenticationSuccessHandler implements AuthenticationSuccess
private final ApplicationConfig applicationConfig;

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
// 获取返回的用户
User currentUser = (User) authentication.getPrincipal();
var accessToken = jwtService.generateToken(currentUser);
Expand All @@ -55,7 +54,7 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo
response.setStatus(HttpStatus.OK.value());
// 保存至Redis缓存起来
redisTemplate.opsForValue().set(
RedisKeyConstant.TOKEN_KEY + currentUser.getUsername(),
RedisKeyConstant.TOKEN_KEY + currentUser.getUsername() + ":" + accessToken,
accessToken,
applicationConfig.getSecurity().token().refreshExpiration(),
TimeUnit.MINUTES);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo
response.setStatus(HttpStatus.OK.value());
// 保存至Redis缓存起来
redisTemplate.opsForValue().set(
RedisKeyConstant.TOKEN_KEY + currentUser.getUsername(),
RedisKeyConstant.TOKEN_KEY + currentUser.getUsername() + ":" + accessToken,
accessToken,
applicationConfig.getSecurity().token().refreshExpiration(),
TimeUnit.MINUTES);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public void filterVerifyAccessToken(@NonNull String accessToken,
String username = e.getClaims().getSubject();
// refresh-token的过期时间
var refreshTokenExpired = applicationConfig.getSecurity().token().refreshExpiration();
String refreshToken = redisTemplate.opsForValue().getAndExpire(RedisKeyConstant.TOKEN_KEY + username, refreshTokenExpired, TimeUnit.MINUTES);
String refreshToken = redisTemplate.opsForValue().getAndExpire(RedisKeyConstant.TOKEN_KEY + username + ":" + accessToken, refreshTokenExpired, TimeUnit.MINUTES);
if (StringUtils.isBlank(refreshToken)) {
// Redis中不存在说明过期,需要重新登录
SecurityContextHolder.clearContext();
Expand All @@ -113,8 +113,8 @@ public void filterVerifyAccessToken(@NonNull String accessToken,
cookie.setHttpOnly(true);
response.addHeader(SecurityConstant.TOKEN_HEADER_KEY, newAccessToken);
response.addCookie(cookie);
redisTemplate.opsForValue().set(RedisKeyConstant.TOKEN_KEY + username, newAccessToken, refreshTokenExpired, TimeUnit.MINUTES);
redisTemplate.opsForValue().set(RedisKeyConstant.TOKEN_TRANSITION_KEY + username, accessToken, 2, TimeUnit.MINUTES);
redisTemplate.opsForValue().set(RedisKeyConstant.TOKEN_KEY + username + ":" + newAccessToken, newAccessToken, refreshTokenExpired, TimeUnit.MINUTES);
redisTemplate.opsForValue().set(RedisKeyConstant.TOKEN_TRANSITION_KEY + username + ":" + accessToken, newAccessToken, 2, TimeUnit.MINUTES);
// 保存登录状态到当前上下文
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
Expand All @@ -123,11 +123,15 @@ public void filterVerifyAccessToken(@NonNull String accessToken,
SecurityContextHolder.setContext(context);
} else {
// refresh-token 不一致时,说明 token 已经被刷新了,当前为并发场景
String refreshTransitionToken = redisTemplate.opsForValue().get(RedisKeyConstant.TOKEN_TRANSITION_KEY + username);
String refreshTransitionToken = redisTemplate.opsForValue().get(RedisKeyConstant.TOKEN_TRANSITION_KEY + username + ":" + accessToken);
// 如果在过渡时间内,允许通过认证
if (StringUtils.isNotBlank(refreshTransitionToken) && Objects.equals(accessToken, refreshTransitionToken)) {
if (StringUtils.isNotBlank(refreshTransitionToken)) {
User userDetails = (User) userDetailsService
.loadUserByUsername(username);
Cookie cookie = new Cookie(SecurityConstant.TOKEN_COOKIE_KEY, refreshTransitionToken);
cookie.setHttpOnly(true);
response.addHeader(SecurityConstant.TOKEN_HEADER_KEY, refreshTransitionToken);
response.addCookie(cookie);
// 保存登录状态到当前上下文
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
Expand Down

0 comments on commit b161d90

Please sign in to comment.