首页 > 解决方案 > 我如何模拟 JWT.decode?

问题描述

我在我的 Spring Boot 应用程序中使用这个库。

<dependency>
  <groupId>com.auth0</groupId>
  <artifactId>java-jwt</artifactId>
  <version>3.9.0</version>
</dependency>

如何进行单元测试DecodedJWT jwt = JWT.decode(accessToken);

我可以传入一个实际的令牌,但这不是正确的方法。

我的 Spring Boot 应用程序中有这个 JwtAuthenticationFilter。

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Value("${clientid}")
    private String clientid;

    @Autowired
    private AuthenticationService authenticationService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException, InvalidRoleException {

        getJwtFromRequest(request, response, filterChain);
    }

    private void getJwtFromRequest(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

            String bearerToken = request.getHeader("Authorization");

            if (!StringUtils.hasText(bearerToken) || !bearerToken.startsWith("Bearer ")) {
                throw new AccessTokenMissingException("No access token found in request headers.");
            }

            try {
                String accessToken = bearerToken.substring(7);

                // this will also throw error when unable to reach auth server
                ResponseEntity<String> result = authenticationService.getUserInfo(accessToken);

                // Invalid access token
                if (!result.getStatusCode().is2xxSuccessful()) {
                    throw new InvalidAccessTokenException("Invalid access token.");
                }

                DecodedJWT jwt = JWT.decode(accessToken);

                String username = jwt.getClaim("preferred_username").asString();
                Map<String, Object> resources = jwt.getClaim("resource_access").asMap();

                Object roles = ((Map<String, Object>) resources.get(clientid)).get("roles");

                List<String> rolesList = (ArrayList<String>)roles;

                UserInfo user = new UserInfo();
                user.setUsername(username);
                user.setRole(rolesList);

                // Step 3: Set username to security context
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                        user.getUsername(), null, AuthUtil.getAuthRole(user.getRole()));

                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);

            } catch (HttpClientErrorException.Unauthorized | JWTDecodeException e) {
                throw new InvalidAccessTokenException("Invalid access token.");
            }

        filterChain.doFilter(request, response);
    }
}

标签: javaspring-bootjunitmockito

解决方案


您可以使用不同的策略/选项,所有这些都将起作用:

  1. 根本不要模拟 JWT.decode,将其视为应在测试中运行的实用方法。它背后的直觉是,如果您的课程使用Math.max(a,b)代码或某些日期时间操作DateTime.of(...),您会在测试中模拟它吗?可能不会......尽管在这种情况下,您可能必须在测试中使用真正可解码的令牌

  2. 使用 PowerMockito 模拟静态调用(我真的不推荐这种方式,但如果您不想更改代码,它会完成这项工作)。

  3. 进行重构,将解码功能提取到接口并将其用作过滤器中的依赖项:

public interface JWTDecoder {
   DecodedJWT decode(String token); // I assume its string for simplicity
}

@Component
public class StdJWTDecoder implements JWTDecoder {
   public DecodedJWT decode(String token) {
       return JWT.decode(tokent);
}

public class JwtAuthenticationFilter ... {
   private final JWTDecoder jwtDecoder;

   public JwtAuthenticationFilter(JWTDecoder jwtDecoder) {
       this.jwtDecoder = jwtDecoder;
   }

    ....
   private void getJwtFromRequest(HttpServletRequest request, HttpServletResponse 
     response, FilterChain filterChain)  {
      ...
      // instead of:
      DecodedJWT jwt = JWT.decode(accessToken);  
      // use this:
      DecodedJWT jwt = jwtDecoder.decode(accessToken);
      ...
   }

}

JwtDecoder使用这种方法,您可以轻松地用 mockito模拟


推荐阅读