背景
我正在使用 Spring Security 开发 Web 应用程序,并且我第一次尝试使用 JSON Web Tokens 进行身份验证。应用程序应根据用户角色限制对某些 URI 的访问。它应该提供密码更改选项,并允许 Admin
用户更改其他用户的角色。
在 tutorial 中我一直在关注,在对服务器的每个 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);
}
//...
}
教程的作者提出了另一种选择:
Note that you could also encode the user's username and role inside JWT claims and create the UserDetails object by parsing those claims from the JWT.
但是,这是以难以更改用户角色为代价的,因为我们无法在不跟踪它们的情况下丢弃已发行的 token 。
可能的解决方案
我研究了 JWT 的主题并提出了以下解决方案:
让我们将用户名和角色存储在 JWT 声明中,并设置一个较短的 token 过期时间(使用 exp
声明)——在此期间之后,例如15 分钟后,我们访问数据库以检查用户的详细信息。如果角色已更改,我们会在有效负载中生成具有新角色的新 token 。如果密码已更改,我们要求用户在生成新 token 之前重新进行身份验证。
此解决方案的一个明显缺点是,用户访问权限的任何更改都会在到期时间后生效。
问题
有没有其他方法可以解决使用 JWT 时处理用户详细信息更改的问题?
最佳答案
我们将 JWT token 与 Spring Security 和 Angular webapp 结合使用。
我认为您的Possible Solution
想法是有效的。我们以类似的方式处理这个问题。我们的授权流程如下所示:
- 用户在 URL 上登录并且响应 header 包含 JWT token
- JWT token 的超时时间很短(分钟)
- Web 应用程序以较短的时间间隔对“刷新 token ”服务执行 ping 操作,以检测 token 是否有效。如果是这样,服务器会重新发布一个新 token ,其中包括用户的所有更新角色,然后由网络应用存储,以包含在未来对后端的请求中。
由于“刷新”服务,如果用户的角色发生变化,或者如果他们被系统禁止,他们将自动注意到新角色或在 token 到期时间之前被“锁定”。
多年来,这对我们来说效果很好。我们用户的角色不会经常更改,如果希望立即更新他们的角色,他们可以随时注销/重新登录。
其他潜在解决方案
但是,如果在你的系统中立即更新用户的角色是最重要的,你可以在 Spring 中使用过滤器检查每个请求的 JWT header ,并让它进行 JWT 验证,并添加一个新的、刷新的 JWT在每个响应上标记。
然后您的客户端可以期望在从服务器返回的每个响应中获得修改后的 JWT token ,并为每个后续请求使用该修改后的 JWT token 。
这会奏效,但如果您的流量很大,它的成本也会相对较高。
这完全取决于您的用例。正如我所说,“刷新”服务对我们来说效果很好。
关于java - 将 JWT 与基于角色的授权结合使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50468752/