【问题标题】:Check key extracted from JWT against database对照数据库检查从 JWT 提取的密钥
【发布时间】:2021-09-23 19:52:56
【问题描述】:

我正在使用 Spring Boot 构建一个新的 API。它将暴露给外部客户(公司网络之外)。这是通过网关(apigee)完成的。

Apigee 提供了一个外部可访问的终端节点并使用 Oauth2 对其进行保护。当客户端使用有效凭据访问外部端点时,apigee 会将请求转发到我的 API 端点。 Apigee 还在标头中添加了 JWT,这将包含客户端使用的 API 密钥。在我的应用程序中 我已经定义了一个过滤器(扩展了 OncePerRequestFilter),它使用网关的公钥从 JWT 中提取 API 密钥。 我正在使用 FilterRegistrationBean 添加过滤器。

现在我在过滤器中有 api-key,我需要做两件事:

  1. 使用 api-key 访问内部 mySQL DB 并获取有关调用者的各种信息(将其存储在 ApiConsumer 对象中)。
  2. 如果在 DB 中找不到密钥,那么我想返回 401。

我在网上看到了许多使用 Spring Security 和 AuthenticationManager、UserDetailsS​​ervice 等代码进行 JWT 验证的示例。 我必须做这一切吗? 由于我的过滤器中已经有了 api-key,有没有办法可以从过滤器进行 DB 调用并验证数据库中是否存在密钥? 然后我可以将值存储在 apiConsumer 中。

过滤器代码:

public class AuthFilter extends OncePerRequestFilter {


    @Autowired
    ApiConsumer aPiConsumer;

    @Value("${jwt.pubkey}")
    private String publickey;

    @Value("${jwt.iss}")
    private String iss;
    
    @Value("${jwt.sub}")
    private String sub;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws Exception {
        
        String header = request.getHeader("Authorization");
            
        if (header == null || !header.startsWith("Bearer ")) {
            throw new Exception("No JWT found");
        }

        String jwtToken = header.substring(7);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publickey.getBytes()));
        PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(keySpec);

        @SuppressWarnings("rawtypes")
        final JwsHeader jwsHeader = Jwts.parser().setSigningKey(pubKey).parseClaimsJws(jwtToken).getHeader();
        String jwtType = jwsHeader.getType();
        String jwtAlgorithm = jwsHeader.getAlgorithm();
        if (!((jwtType.equals("JWT")) && (jwtAlgorithm.equals("RS256")))) {
            throw new Exception("Invalid JWT Type or Algorithm");
        }

        final Claims claims = Jwts.parser().setSigningKey(pubKey).parseClaimsJws(jwtToken).getBody();

        String issuer = claims.getIssuer();
        String subject = claims.getSubject();
        String apiKey = claims.get("apikey", String.class);

        if (!((issuer.equals(iss)) && (subject.equals(sub)))) {
            throw new Exception("Invalid JWT Issuer or Subject");
        } else {
            aPiConsumer.setApiKey(apikey);
            //I have the api key. Now need to hit MySQL database to access this.
        }
        filterChain.doFilter(request, response);
    }
}

bean 代码:

@Configuration
public class AuthConfig {
    
    @Bean
    public FilterRegistrationBean authorizationFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(authFilter());
        registration.setName("authFilter");
        return registration;
    }

    @Bean
    public Filter authFilter() {
        return new AuthFilter();
    }

}

【问题讨论】:

  • 你为什么不使用内置的 Spring Security 支持来为你做这一切?

标签: java spring spring-boot jwt


【解决方案1】:

您自动装配了apiConsumer,因此您也可以自动装配读取数据库(或存储库)的服务,对吧?

正如评论者指出的那样,您可以使用一些 Spring 内置支持来验证 JWT,然后您的过滤器只需读取数据库并验证 API 密钥。通常最好让库或框架处理这些事情,因为代码往往更健壮且经过测试。 (例如,如果您收到格式错误的 JWT,但在正确的 Authorization 标头中等,您的代码将崩溃)

【讨论】:

    猜你喜欢
    • 2017-09-24
    • 2020-11-28
    • 2012-07-13
    • 1970-01-01
    • 1970-01-01
    • 2014-10-21
    • 1970-01-01
    相关资源
    最近更新 更多