Amazon Simple Notification Service
开发人员指南 (API Version 2010-03-31)
AWS 服务或AWS文档中描述的功能,可能因地区/位置而异。点 击 Getting Started with Amazon AWS to see specific differences applicable to the China (Beijing) Region.

Amazon SNS 终端节点 Java Servlet 代码示例

重要

以下代码段可帮助您了解用于处理 Amazon SNS HTTP POST 请求的 Java servlet。在生产环境中实现这些代码段之前,您应确保这些代码段的所有部分都适用于您的用途。例如,在生产环境中,为了帮助防止欺诈攻击,您应验证收到的 Amazon SNS 消息的标识是否来自 Amazon SNS。为此,可以检查 Subject Alternative Name 字段(Amazon SNS 证书中提供)中的 DNS 名称值(在 us-west-2 中,DNS 名称=sns.us-west-2.amazonaws.com;这因区域而异)是否与收到的 Amazon SNS 消息相同。有关验证服务器身份的更多信息,请参阅 RFC 2818 中的 3.1 服务器身份这一部分。另请参阅验证 Amazon SNS 消息签名

下面的方法用 Java servlet 实现一个处理程序示例,对来自 Amazon SNS 的 HTTP POST 请求进行处理。

Copy
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, SecurityException{ //Get the message type header. String messagetype = request.getHeader("x-amz-sns-message-type"); //If message doesn't have the message type header, don't process it. if (messagetype == null) return; // Parse the JSON message in the message body // and hydrate a Message object with its contents // so that we have easy access to the name/value pairs // from the JSON message. Scanner scan = new Scanner(request.getInputStream()); StringBuilder builder = new StringBuilder(); while (scan.hasNextLine()) { builder.append(scan.nextLine()); } Message msg = readMessageFromJson(builder.toString()); // The signature is based on SignatureVersion 1. // If the sig version is something other than 1, // throw an exception. if (msg.getSignatureVersion().equals("1")) { // Check the signature and throw an exception if the signature verification fails. if (isMessageSignatureValid(msg)) log.info(">>Signature verification succeeded"); else { log.info(">>Signature verification failed"); throw new SecurityException("Signature verification failed."); } } else { log.info(">>Unexpected signature version. Unable to verify signature."); throw new SecurityException("Unexpected signature version. Unable to verify signature."); } // Process the message based on type. if (messagetype.equals("Notification")) { //TODO: Do something with the Message and Subject. //Just log the subject (if it exists) and the message. String logMsgAndSubject = ">>Notification received from topic " + msg.getTopicArn(); if (msg.getSubject() != null) logMsgAndSubject += " Subject: " + msg.getSubject(); logMsgAndSubject += " Message: " + msg.getMessage(); log.info(logMsgAndSubject); } else if (messagetype.equals("SubscriptionConfirmation")) { //TODO: You should make sure that this subscription is from the topic you expect. Compare topicARN to your list of topics //that you want to enable to add this endpoint as a subscription. //Confirm the subscription by going to the subscribeURL location //and capture the return value (XML message body as a string) Scanner sc = new Scanner(new URL(msg.getSubscribeURL()).openStream()); StringBuilder sb = new StringBuilder(); while (sc.hasNextLine()) { sb.append(sc.nextLine()); } log.info(">>Subscription confirmation (" + msg.getSubscribeURL() +") Return value: " + sb.toString()); //TODO: Process the return value to ensure the endpoint is subscribed. } else if (messagetype.equals("UnsubscribeConfirmation")) { //TODO: Handle UnsubscribeConfirmation message. //For example, take action if unsubscribing should not have occurred. //You can read the SubscribeURL from this message and //re-subscribe the endpoint. log.info(">>Unsubscribe confirmation: " + msg.getMessage()); } else { //TODO: Handle unknown message type. log.info(">>Unknown message type."); } log.info(">>Done processing message: " + msg.getMessageId()); }

以下 Java 方法示例将通过 Message 数据元中的信息创建签名,上述消息数据元包含发送至请求正文的数据,同时还将签名与消息原始 base64 编码签名进行验证,上述签名亦通过 Message 数据元读取。

Copy
private static boolean isMessageSignatureValid(Message msg) { try { URL url = new URL(msg.getSigningCertURL()); InputStream inStream = url.openStream(); CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate cert = (X509Certificate)cf.generateCertificate(inStream); inStream.close(); Signature sig = Signature.getInstance("SHA1withRSA"); sig.initVerify(cert.getPublicKey()); sig.update(getMessageBytesToSign(msg)); return sig.verify(Base64.decodeBase64(msg.getSignature())); } catch (Exception e) { throw new SecurityException("Verify method failed.", e); } }

以下 Java 方法示例将共同操作,为 Amazon SNS 消息创建待签字符串。getMessageBytesToSign 方法将根据消息类型调用适当待签字符串的方法,并以字节数组的形式运行待签字符串。buildNotificationStringToSignbuildSubscriptionStringToSign 方法将按照 验证 Amazon SNS 消息签名 所述格式创建待签字符串。

Copy
private static byte [] getMessageBytesToSign (Message msg) { byte [] bytesToSign = null; if (msg.getType().equals("Notification")) bytesToSign = buildNotificationStringToSign(msg).getBytes(); else if (msg.getType().equals("SubscriptionConfirmation") || msg.getType().equals("UnsubscribeConfirmation")) bytesToSign = buildSubscriptionStringToSign(msg).getBytes(); return bytesToSign; } //Build the string to sign for Notification messages. public static String buildNotificationStringToSign( Message msg) { String stringToSign = null; //Build the string to sign from the values in the message. //Name and values separated by newline characters //The name value pairs are sorted by name //in byte sort order. stringToSign = "Message\n"; stringToSign += msg.getMessage() + "\n"; stringToSign += "MessageId\n"; stringToSign += msg.getMessageId() + "\n"; if (msg.getSubject() != null) { stringToSign += "Subject\n"; stringToSign += msg.getSubject() + "\n"; } stringToSign += "Timestamp\n"; stringToSign += msg.getTimestamp() + "\n"; stringToSign += "TopicArn\n"; stringToSign += msg.getTopicArn() + "\n"; stringToSign += "Type\n"; stringToSign += msg.getType() + "\n"; return stringToSign; } //Build the string to sign for SubscriptionConfirmation //and UnsubscribeConfirmation messages. public static String buildSubscriptionStringToSign(Message msg) { String stringToSign = null; //Build the string to sign from the values in the message. //Name and values separated by newline characters //The name value pairs are sorted by name //in byte sort order. stringToSign = "Message\n"; stringToSign += msg.getMessage() + "\n"; stringToSign += "MessageId\n"; stringToSign += msg.getMessageId() + "\n"; stringToSign += "SubscribeURL\n"; stringToSign += msg.getSubscribeURL() + "\n"; stringToSign += "Timestamp\n"; stringToSign += msg.getTimestamp() + "\n"; stringToSign += "Token\n"; stringToSign += msg.getToken() + "\n"; stringToSign += "TopicArn\n"; stringToSign += msg.getTopicArn() + "\n"; stringToSign += "Type\n"; stringToSign += msg.getType() + "\n"; return stringToSign; }