将 JWT 与基于角色的授权一起使用

Posted

技术标签:

【中文标题】将 JWT 与基于角色的授权一起使用【英文标题】:Using JWT with role based authorization 【发布时间】:2018-11-01 06:50:43 【问题描述】:

背景

我正在使用 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);
    

    //...


本教程的作者提出了另一种选择:

请注意,您还可以在 JWT 声明中对用户的用户名和角色进行编码,并通过解析来自 JWT 的声明来创建 UserDetails 对象。

但是,这样做的代价是很难改变用户的角色,因为我们无法丢弃已发行的代币,而不跟踪它们。

可能的解决方案

我研究了 JWT 的主题并提出了以下解决方案:

让我们将用户名和角色存储在 JWT 声明中,并设置一个较短的令牌过期时间(使用 exp 声明) - 在此期限之后,例如15 分钟,我们访问数据库来检查用户的详细信息。如果角色发生了变化,我们会在有效负载中使用新角色生成新令牌。如果密码已更改,我们要求用户在生成新令牌之前重新进行身份验证。

此解决方案的一个明显缺点是用户访问权限的任何更改都在到期时间之后生效。

问题

在使用 JWT 时,是否有其他方法可以解决处理用户详细信息更改的问题?

【问题讨论】:

检查这个过滤器实现:github.com/szerhusenBC/jwt-spring-security-demo/blob/master/src/… 【参考方案1】:

我们将 JWT 令牌与 Spring Security 和 Angular webapp 一起使用。

我认为您的Possible Solution 想法是有效的。我们以类似的方式处理这个问题。我们的身份验证流程如下所示:

用户通过 URL 登录,并且响应标头包含 JWT 令牌 JWT 令牌的超时时间很短(分钟) webapp 以较短的间隔 ping 一个“刷新令牌”服务,以检测令牌是否有效。如果是这样,服务器会重新发布一个新令牌,包括用户的任何更新角色,然后由 webapp 存储,以包含在未来对后端的请求中。

由于“刷新”服务,如果用户的角色发生变化,或者如果他们被系统禁止,他们将在不迟于令牌到期时间之前自动通知新角色或被“锁定”。

这对我们来说已经好几年了。我们用户的角色不会经常更改,如果希望立即更新他们的角色,他们可以随时退出/重新登录。

其他潜在解决方案

但是,如果在您的系统中立即更新用户的角色至关重要,您可以在 Spring 中使用过滤器检查每个请求的 JWT 标头,并让它进行 JWT 验证,并添加一个新的、刷新的 JWT每个响应的令牌。

然后,您的客户端可以期望在从服务器返回的每个响应中获得一个修改后的 JWT 令牌,并将该修改后的 JWT 令牌用于每个后续请求。

这可行,但如果您有大量流量,它也会相对昂贵。

这完全取决于您的用例。就像我说的,“刷新”服务对我们来说效果很好。

【讨论】:

以上是关于将 JWT 与基于角色的授权一起使用的主要内容,如果未能解决你的问题,请参考以下文章

客户端 JWT 基于角色的授权

基于角色的 jwt 授权

使用 express-jwt 进行基于角色的授权?

如何执行 Auth0 JWT 令牌用户基于角色的授权

Spring Boot JWT token 基于角色的授权问题

.NET Core 3.1 基于角色的 JWT 授权返回 403 禁止