You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot:如何从无认证Razorpay-webhook调用JWT认证邮件模块?

解决Spring Boot Razorpay Webhook调用JWT认证邮件模块的问题

核心前提:先确保Razorpay Webhook请求的合法性

不管用哪种方案,第一步必须验证请求确实来自Razorpay,防止恶意请求触发后续逻辑。Razorpay通过请求头X-Razorpay-Signature和你设置的webhook密钥生成签名,验证代码示例:

@RestController
@RequestMapping("/webhooks/razorpay")
public class RazorpayWebhookController {

    private final String WEBHOOK_SECRET = "your_razorpay_webhook_secret";

    @PostMapping
    public ResponseEntity<Void> handleWebhook(@RequestBody String payload,
                                              @RequestHeader("X-Razorpay-Signature") String signature) {
        // 验证签名
        boolean isSignatureValid = verifySignature(payload, signature);
        if (!isSignatureValid) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }

        // 后续业务逻辑:存支付数据、触发邮件
        // ...
        return ResponseEntity.ok().build();
    }

    private boolean verifySignature(String payload, String signature) {
        try {
            Mac hmac = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKey = new SecretKeySpec(WEBHOOK_SECRET.getBytes(), "HmacSHA256");
            hmac.init(secretKey);
            byte[] digest = hmac.doFinal(payload.getBytes());
            String computedSignature = Base64.getEncoder().encodeToString(digest);
            return computedSignature.equals(signature);
        } catch (Exception e) {
            return false;
        }
    }
}

方案1:Webhook内部生成JWT令牌调用邮件模块

在验证Razorpay请求合法后,直接生成邮件模块认可的JWT令牌,携带令牌调用邮件API。

实现步骤:

  1. 生成内部JWT令牌:使用邮件服务的JWT密钥、短过期时间,生成仅包含发送管理员邮件权限的令牌(避免权限过大)。
  2. 携带JWT调用邮件API:用RestTemplate或WebClient发送请求,在请求头中加入Authorization: Bearer <token>。

代码示例:

@Autowired
private RestTemplate restTemplate;

// 在webhook处理方法中添加以下逻辑
private void triggerAdminEmail(PaymentData paymentData) {
    // 生成JWT令牌
    String jwtToken = generateInternalJwt();
    // 邮件API请求头
    HttpHeaders headers = new HttpHeaders();
    headers.setBearerAuth(jwtToken);
    headers.setContentType(MediaType.APPLICATION_JSON);

    // 构造请求体
    EmailRequest emailRequest = new EmailRequest();
    emailRequest.setTo("admin@example.com");
    emailRequest.setSubject("新支付通知");
    emailRequest.setBody("收到新支付:" + paymentData.getAmount());

    HttpEntity<EmailRequest> requestEntity = new HttpEntity<>(emailRequest, headers);
    // 调用邮件模块API
    restTemplate.postForEntity("http://email-service/api/admin/send", requestEntity, Void.class);
}

private String generateInternalJwt() {
    // 使用JJWT库生成令牌
    return Jwts.builder()
            .setSubject("webhook-service")
            .claim("permission", "send_admin_email")
            .setExpiration(Date.from(Instant.now().plusMinutes(5)))
            .signWith(SignatureAlgorithm.HS256, "email_service_jwt_secret".getBytes())
            .compact();
}

方案2:消息队列解耦(异步+无API调用)

将webhook的触发逻辑与邮件发送解耦,通过消息队列传递事件,邮件服务直接监听队列处理,无需跨服务API调用,从根源避免JWT认证问题。

实现步骤:

  1. Webhook服务发送消息到队列:验证请求合法、存库后,将支付信息发送到MQ(如RabbitMQ/Kafka)。
  2. 邮件服务监听队列:收到消息后直接发送邮件,无需任何认证(队列的访问权限由内部配置保证)。

RabbitMQ示例:

// Webhook服务发送消息
@Autowired
private RabbitTemplate rabbitTemplate;

private void sendPaymentEventToQueue(PaymentData paymentData) {
    rabbitTemplate.convertAndSend("payment-exchange", "payment.success", paymentData);
}

// 邮件服务监听队列
@Component
public class PaymentEventListener {

    @Autowired
    private EmailService emailService;

    @RabbitListener(queues = "payment-queue")
    public void handlePaymentEvent(PaymentData paymentData) {
        emailService.sendAdminEmail("新支付通知", "收到新支付:" + paymentData.getAmount());
    }
}

方案3:邮件模块开放内部专属接口(IP/密钥认证)

如果不想用JWT或MQ,可让邮件模块的特定接口仅允许内部访问:

  • IP白名单:在邮件服务的Spring Security配置中,将webhook服务的IP加入白名单,允许访问发送管理员邮件的接口。
  • 内部API密钥:邮件接口要求请求头携带预共享的内部密钥(如X-Internal-Api-Key),webhook调用时带上该密钥。

Spring Security IP白名单示例:

@Configuration
public class EmailSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/api/admin/send").hasIpAddress("192.168.1.100") // webhook服务IP
                .anyRequest().authenticated();
    }
}

内容的提问来源于stack exchange,提问作者Rajeev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 08:47:27