背景
我正在开发一个使用Spring Security的Web应用程序,并尝试首次使用JSON Web Tokens进行认证。该应用程序应根据用户角色限制对某些URI的访问。它应提供更改密码选项并允许管理员
用户更改其他用户的角色。
在我遵循的教程中,每个HTTP请求都会通过CustomUserDetailsService
命中数据库以加载用户的当前详细信息,这似乎对性能有很大影响:
public class JwtAuthenticationFilter extends OncePerRequestFilter {
//...
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
Long userId = tokenProvider.getUserIdFromJWT(jwt);
UserDetails userDetails = customUserDetailsService.loadUserById(userId);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(request, response);
}
//...
}
教程的作者提出了另一种选择:
请注意,您还可以在JWT声明中编码用户的用户名和角色,并通过解析JWT中的这些声明来创建UserDetails对象。
然而,这样做会带来一个问题,即我们无法废弃已发行的令牌,也没有办法跟踪它们以更改用户的角色。
可能的解决方案
我对JWT的主题进行了研究,并想出了以下解决方案:
让我们将用户名和角色存储在JWT声明中,并设置较短的令牌过期时间(使用exp
声明)- 在此期间后,例如15分钟后,我们会访问数据库以检查用户详细信息。如果角色已更改,我们将在新的负载中生成具有新角色的新令牌。如果密码已更改,则在生成新令牌之前,我们要求用户重新进行身份验证。
这种解决方案的一个明显缺点是,在到期时间后才能生效用户访问权限的任何更改。
问题
在使用JWT时,是否有其他处理用户详细信息更改问题的方法?