【发布时间】:2026-01-23 00:40:01
【问题描述】:
我想在登录和注销时登录用户,以及在 jwt 令牌过期时登录。要做到这一点,我在发生ExpiredJwtException 时调用loginHistoryService.saveUserLogout(),但问题是这个方法调用了多个,因为当用户打开表单时,这个表单包含很多ajax 请求,所以每个请求都会导致到此过滤器并多次记录用户注销。
有没有更好的方法来实现这个要求?
public class JWTAuthorizationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
if (!request.getRequestURI().contains("/signout")) {
try {
if (checkJWTToken(request)) {
Claims claims = validateToken(request);
if (claims.get(CLAIM_AUTHORITIES_KEY) != null) {
setUpSpringAuthentication(claims);
}
else {
SecurityContextHolder.clearContext();
}
}
else {
SecurityContextHolder.clearContext();
}
chain.doFilter(request, response);
} catch (ExpiredJwtException e) {
// call log method here, but this method called more than one
loginHistoryService.saveUserLogout();
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setHeader("ExpiredJwt", "true");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Your JWT token expired!");
} catch (UnsupportedJwtException | MalformedJwtException e) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "forbidden!");
}
}
else {
chain.doFilter(request, response);
}
}
}
更新 1
loginHistoryService 的目的是存储 LoginHistory 具有以下声明的对象:
@Entity
@Table(name = "tbl_login_history")
public class LoginHistory extends BaseEntity<Long> {
@Column(name = "user_name", nullable = false)
private String userName;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@Column(name = "is_success", nullable = false)
private boolean success;
@Column(name = "is_login", nullable = false)
private boolean login;
@Column(name = "browser_type")
private BrowserType browserType;
@Column(name = "browser_name")
private String browserName;
@Column(name = "operating_system_type")
private OperatingSystemType operatingSystemType;
@Column(name = "operating_system_name")
private String operatingSystemName;
@Column(name = "login_failure_reason")
private LoginFailureReason loginFailureReason;
}
审计用户登录和注销。
更新 2
我更改了LoginHistory并添加了jwtToken字段来存储jwt并检查是否存在记录,然后不要插入。但问题是它的记录插入了多次。
@Service
public class LoginHistoryService implements ILoginHistoryService {
@Autowired
private ILoginHistoryRepository loginHistoryRepository;
@Override
@Transactional
public void saveLogWhenExpiredUser(String token) {
if (!loginHistoryRepository.checkExpiredTokenExistence(token)) {
// save the log record
}
}
}
这是检查记录存在的方法:
@Override
public boolean checkExpiredTokenExistence(String token) {
String hql = " select cast((case when count(*) = 0 then 0 else 1 end) as boolean) " +
" from " + domainClass.getName() + " e " +
" where e.jwtToken = :jwtToken " +
" and e.logoutType = :logoutType " ;
Map<String, Object> params = new HashMap<>();
params.put("jwtToken", token.getBytes());
params.put("logoutType", LogOutType.Expired);
return super.find(hql, params);
}
【问题讨论】:
标签: spring spring-boot logging jwt