设置服务器以接收推送通知

Google Play 会生成通知,以响应发生的并可能影响企业的事件。EMM 解决方案提供商可以接收这些通知并采取相应措施,例如为其客户管理员提供提醒或其他机制。

本指南介绍如何配置仅接收和处理 EMM push通知所需的服务器基础架构。拉取通知不需要本指南中详述的设置。

对于推送通知,除了本指南中介绍的服务器设置外,您还必须在 Google API 控制台中授予适当的权限并执行其他配置任务。如需了解详情,请参阅启用 EMM 推送通知

如需详细了解 Google Cloud Pub/Sub(包括示例),请参阅 Cloud Pub/Sub 文档

您可以配置系统,将推送通知发送到指定的 HTTPS 端点,或等待发送通知的服务器。

推送端点配置简介

如需配置推送端点,您需要一台具有有效 SSL 证书的服务器。在此示例中,您将创建 SSL 证书并将其上传到证书授权机构 (CA),然后配置 NGINX 服务器。最后,编译并运行测试代码,以确认您的设置正确无误。

以下示例展示了如何在反向代理模式下配置 NGINX 服务器,以使用 Ubuntu 14.04 连接到在端口 8093 上运行的订阅者应用(在 PushSubscriber.java 中)。您的企业可能使用其他服务器,但示例设置应该在所有基于 Debian 的发行版上无需更改即可正常运行。其他发行版(例如基于 RedHat 的发行版)类似,但配置文件的位置可能有所不同。

您必须先配置符合以下条件的端点,然后才能接收通知:

  • 您必须拥有该网域,并在 Google API 控制台中验证您的所有权。

  • 您必须能够在端口 443 (SSL) 上运行服务。

  • 您必须拥有 CA 签名的 SSL 证书。自签名证书无效。

  • 您运行的 Web 服务器必须支持 webhook

您的端点不需要在 Google App Engine 上运行(但可以在 Google App Engine 上运行)。

创建并上传 SSL 证书

1. 生成安全套接字层 (SSL) 证书:

    myusername@myhost:/tmp$ sudo openssl req -x509 -nodes -days 365 \
      -newkey rsa:2048 -keyout cert.key -out cert.crt

这将生成以下响应。将以下代码中的示例值(例如 push.solarmora.commyusername@myhost)替换为您的实际服务器名称、公司、地址等。您可以使用任何子网域,前提是该子网域的 A 记录指向您的服务器。

    Generating a 2048 bit RSA private key
    ...........................................................................
    .....+++
    writing new private key to 'cert.key'
    -----
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Country Name (2 letter code) [AU]:GB
    State or Province Name (full name) [Some-State]:England
    Locality Name (eg, city) []:London
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:Solarmora, Inc.
    Organizational Unit Name (eg, section) []:Creative Publications
    Common Name (e.g. server FQDN or YOUR name) []:push.solarmora.com
    Email Address []:admin@solarmora.com

2. 验证是否已创建证书文件:

$ myusername@myhost:/tmp$ ls cert*
cert.crt  cert.key

3. 如需为此证书签名,请生成一个证书签名请求 (CSR) 以将其上传到您的签名者:

myusername@myhost:/tmp$ sudo openssl x509 -x509toreq -in cert.crt \
  -out cert.csr -signkey cert.key
Getting request Private Key
Generating certificate request
myusername@myhost:/tmp$ ls cert.*
cert.crt  cert.csr  cert.key

4. 确保 CSR 文件的内容如下所示:

    Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: C=GB, ST=England, L=London, O=Solarmora, Inc.,
        OU=Creative Publications,
        CN=push.solarmora.com/emailAddress=admin@solarmora.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:cc:0f:54:26:3d:d9:17:eb:8f:6c:f7:27:5e:77:
                    64:65:00:db:fe:2a:1f:fa:ea:de:21:7a:c5:5d:87:
                    ...
                    ...
                Exponent: 65537 (0x10001)
        Attributes:
            a0:00
    Signature Algorithm: sha256WithRSAEncryption
         1d:ea:12:b8:c2:6a:d6:f4:6e:92:2f:b9:12:5e:e3:91:15:a0:
         06:b5:81:ce:c5:cf:b7:d2:a7:dd:f2:78:ca:28:8e:21:cd:6d:
         ...
         ...
    -----BEGIN CERTIFICATE REQUEST-----
    MIIC6zCCAdMCAQAwgaUxCzAJBgNVBAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5kMQ8w
    DQYDVQQHDAZMb25kb24xGDAWBgNVBAoMD0FDTUUgQ29ycCwgSW5jLjEYMBYGA1UE
    CwwPQ3JlYXRpdmUgQW52aWxzMRswGQYDVQQDDBJwdXNoLmFjbWUtY29ycC5jb20x
    IjAgBgkqhkiG9w0BCQEWE2FkbWluQGFjbWUtY29ycC5jb20wggEiMA0GCSqGSIb3
    ...
    ...
    -----END CERTIFICATE REQUEST-----

5. 将证书的 BEGIN 行和 END 行(含)之间的部分上传到您的 CA。具体流程取决于您的 CA,但包含以下步骤:

  1. 将您的 CSR 文件上传到您的 CA 网站,或将文件内容粘贴到您的 CA 网站上。然后,您的 CA 会验证和处理这些数据。
  2. 下载 CA 生成的签名证书。

6. CA 的输出应包含多个文件:已签名的证书本身以及用于确认其有资格对证书签名的 CA 证书。将下载的软件包中的所有 *.crt 证书文件合并成一个软件包文件,例如 bundle.push.solarmora.com.crt

$ cat *.crt > bundle.push.solarmora.com.crt

配置代理服务器

在本部分中,您将配置 NGINX 开源 Web 服务器和反向代理服务器以提供端点,并将所有传入请求转发到订阅者服务器。以 NGINX 为例,您也可以改用任何其他 HTTP 代理。

1. 在您的服务器上安装 NGINX:

    $ sudo apt-get update
    $ sudo apt-get install nginx
    $ nginx -v
    $ nginx version: nginx/1.4.6 (Ubuntu)

2. 为了确保您在 sites-enabled 目录中创建的额外服务器配置文件由 NGINX 处理,请修改 /etc/nginx/nginx.conf 并包含以下内容:

    $ include /etc/nginx/conf.d/*.conf;
    $ include /etc/nginx/sites-enabled/*;

3. 将您的证书文件复制到 www-data 用户可以读取的安全位置,但最好不可供任何其他用户读取(如果您的网络服务器以其他用户身份运行,您可能需要调整用户名):

    $ sudo mkdir -p /var/openssl/push.solarmora.com
    $ sudo mv /tmp/cert.key 
/var/openssl/push.solarmora.com/push.solarmora.com.key $ sudo mv /tmp/bundle.push.solarmora.com.crt
/var/openssl/push.solarmora.com/bundle.push.solarmora.com.crt

4. 创建新的 server 配置。在 /etc/nginx/sites-enabled 中修改 push.solarmora.com,并使用实际服务器的完全限定域名作为文件名:

server {
   listen 443;
   server_name push.solarmora.com;

   ssl on;
   ssl_certificate
     /var/openssl/push.solarmora.com/bundle.push.solarmora.com.crt;
   ssl_certificate_key
     /var/openssl/push.solarmora.com/push.solarmora.com.key;

   # it is usually very convenient to have separate files for your
   # access and error log to analyze for possible problems
   access_log /var/log/nginx/nginx.push.solarmora.com.log;
   error_log /var/log/nginx/nginx.push.solarmora.com.log;

   location / {
            # assuming the subscriber will run on the same machine
            # on port 8093
            proxy_pass http://localhost:8093;
   }
}

5. 重启 NGINX 以实施更改:

    myusername@myhost:/etc/nginx$ sudo service nginx restart
    * Restarting nginx nginx
    ...done.

6. 您的服务器现已配置完毕。如需验证配置,请尝试使用 curl 查询服务器:

    [myusername@myhost:~]$ curl push.solarmora.com
    <html>
    <head><title>502 Bad Gateway</title></head>
    <body bgcolor="white">
    <center><h1>502 Bad Gateway</h1></center>
    <hr><center>nginx/1.4.6 (Ubuntu)</center>
    </body>
    </html>

由于尚未配置下游服务器(配置文件中的 localhost:8093),这属于预期响应。

编译并运行示例

如需运行本部分中的示例,您需要一个有效的 Google API 控制台项目。我们建议您专门为测试创建一个帐号,并将其与生产项目分开。创建测试项目后,您需要创建服务帐号。记下服务帐号的电子邮件地址,并将关联的 .p12 文件放在您服务器上的某个位置。

设置源代码树

1. 克隆 play-work.git 代码库:

    myusername@myhost:~/code$ git clone
    https://github.com/google/play-work.git
    Cloning into 'play-work'...
    Username for 'https://github.com': username
    Password for 'https://username@github.com':
    remote: Counting objects: 110, done.
    remote: Compressing objects: 100% (60/60), done.
    remote: Total 110 (delta 24), reused 95 (delta 9), pack-reused 0
    Receiving objects: 100% (110/110), 23.88 KiB | 0 bytes/s, done.
    Resolving deltas: 100% (24/24), done.
    Checking connectivity... done.
2. 在基于 Debian 的系统上,会安装 MavenGoogle Protocol Buffers 编译器
    $ sudo apt-get install maven protobuf-compiler

3. 验证 Maven 和 Google Protocol Buffers 编译器是否已正确安装:

    myusername@myhost:~$ mvn -v
    Apache Maven 3.0.5
    Maven home: /usr/share/maven
    Java version: 1.7.0_75, vendor: Oracle Corporation
    Java home: /usr/lib/jvm/java-7-openjdk-amd64/jre
    Default locale: en_US, platform encoding: UTF-8
    OS name: "linux", version: "3.16.0-30-generic", arch: "amd64", family: "unix"
    myusername@myhost:~$ protoc --version
    libprotoc 2.5.0

4. Maven 配置文件 pom.xml 假定 Protocol Buffers 编译器已安装到 /usr/bin/protoc 目录下:

    myusername@myhost:~$ which protoc
    /usr/bin/protoc
如果不是这种情况,您可以修改 pom.xml 或符号链接 protoc
    $ sudo ln -s which protoc /usr/bin/protoc
5. 编译示例。验证您能否通过运行 mvn clean compile assembly:single 来构建代码。这应该会生成一个名为 emm-notifications-[version-number]-jar-with-dependencies.jar 的文件,其中 [version number] 是示例的当前版本,例如 1.0-SNAPSHOT
    myusername@myhost:~/code/play-work/examples/emm-notifications$ ls target/*
    target/emm-notifications-1.0-SNAPSHOT-jar-with-dependencies.jar
6. 验证您可以运行编译后的 TestPublisher 代码。预计代码会失败:

    myusername@myhost:~/code/play-work/examples/emm-notifications$ java -cp \
      target/emm-notifications-1.0-SNAPSHOT-jar-with-dependencies.jar \
      com.google.android.work.emmnotifications.TestPublisher
    Exception in thread "main" java.lang.IllegalArgumentException:
    You must specify non-default ServiceAccountEmail in
    settings.properties
        at com.google.api.client.repackaged.com.google.common.base.Preconditions.checkArgument(Preconditions.java:119)
        at com.google.api.client.util.Preconditions.checkArgument(Preconditions.java:69)
        at com.google.android.work.emmnotifications.Settings.verifyVariable(Settings.java:129)
        at com.google.android.work.emmnotifications.Settings.getSettings(Settings.java:103)
        at com.google.android.work.emmnotifications.TestPublisher.main(TestPublisher.java:39)

7. 您必须替换 settings.properties 文件中的某些值。为此,请按如下所示创建该文件的副本并修改该副本中的属性:

    # This should be your own service account's email address
    ServiceAccountEmail=368628613713-t4hfexampledn5lhpdcu1qqfgio01626@developer.gserviceaccount.com
    ServiceAccountP12KeyFile=/opt/secret/secret.p12
    # This will be the name of the service account
    ProjectName=enterprise-cloud-pub-sub
    SubscriptionName=projects/enterprise-cloud-pub-sub/subscriptions/default
    TopicName=projects/enterprise-cloud-pub-sub/topics/default
    # The push endpoint in your API Console project
    PushEndpoint=https://push.solarmora.com

8. 再次运行 TestPublisher 代码,确保它不再崩溃。(您可能会在日志输出中看到一个错误。)

运行发布商测试代码

您需要运行用于发布通知的示例代码,以便订阅者可以阅读一些消息。

在以下示例中,代码查找 my_settings.properties 中指定的主题,但未找到该主题,因此会创建该主题。然后向该主题发布一条消息。此示例提供了一个有价值的测试工具,可让您模拟 Google Play EMM API 发送的消息。

    myusername@myhost:~/code/play-work/examples/emm-notifications$ DEVELOPER_CONSOLE_SETTINGS=./my_settings.properties java -cp \
      target/emm-notifications-1.0-SNAPSHOT-jar-with-dependencies.jar com.google.android.work.emmnotifications.TestPublisher
    Feb 27, 2015 1:39:59 PM com.google.android.work.emmnotifications.RetryHttpInitializerWrapper$1 handleResponse
    INFO: RetryHandler: {
      "error": {
        "code": 404,
        "message": "Resource not found (resource=default).",
        "errors": [
          {
            "message": "Resource not found (resource=default).",
            "domain": "global",
            "reason": "notFound"
          }
        ],
        "status": "NOT_FOUND"
      }
    }

    Feb 27, 2015 1:39:59 PM com.google.android.work.emmnotifications.TestPublisher main
    INFO: Topic projects/enterprise-cloud-pub-sub/topics/default doesn't exists, creating it
    Feb 27, 2015 1:40:02 PM com.google.android.work.emmnotifications.TestPublisher main
    INFO: Topic projects/enterprise-cloud-pub-sub/topics/default created
    Feb 27, 2015 1:40:02 PM com.google.android.work.emmnotifications.TestPublisher main
    INFO: Publishing a request: {messages=[{data=CjEKFQoIMTIzMjEzMjESCXJpZ2h0IG5vdxIWY29tLmdvb2dsZS5hbmRyb2lkLmdtcxgA}]}

运行订阅者测试代码

订阅者测试代码确认您可以接收 TestPublisher 代码发布的消息。

1. 确保您的代码是最新的且已经过编译,然后运行订阅者测试代码:

    myusername@myhost:~/code/play-work/examples/emm-notifications$ DEVELOPER_CONSOLE_SETTINGS=./my_settings.properties 
java -cp target/emm-notifications-1.0-SNAPSHOT-jar-with-dependencies.jar
com.google.android.work.emmnotifications.PushSubscriber Feb 27, 2015 1:46:37 PM com.google.android.work.emmnotifications.PushSubscriber main INFO: Will be using topic name: projects/enterprise-cloud-pub-sub/topics/default, subscription name:
projects/enterprise-cloud-pub-sub/subscriptions/default Feb 27, 2015 1:46:38 PM com.google.android.work.emmnotifications.PushSubscriber main INFO: Trying to get subscription named projects/enterprise-cloud-pub-sub/subscriptions/default Feb 27, 2015 1:46:38 PM com.google.android.work.emmnotifications.RetryHttpInitializerWrapper$1 handleResponse INFO: RetryHandler: { "error": { "code": 404, "message": "Resource not found (resource=default).", "errors": [ { "message": "Resource not found (resource=default).", "domain": "global", "reason": "notFound" } ], "status": "NOT_FOUND" } } Feb 27, 2015 1:46:38 PM com.google.android.work.emmnotifications.PushSubscriber main INFO: Subscription doesn't exist, will try to create projects/enterprise-cloud-pub-sub/subscriptions/default Feb 27, 2015 1:46:43 PM com.google.android.work.emmnotifications.PushSubscriber main INFO: Created: { "ackDeadlineSeconds" : 600, "name" : "projects/enterprise-cloud-pub-sub/subscriptions/default", "pushConfig" : { "pushEndpoint" : "https://push.solarmora.com" }, "topic" : "projects/enterprise-cloud-pub-sub/topics/default" }
订阅者现在正在运行并已准备好接受传入消息。

2. 再次运行发布者,新消息会添加到日志中:

    Feb 27, 2015 1:47:24 PM com.google.android.work.emmnotifications.PushSubscriber$1 handle
    INFO: Raw request: {"message":{"data":"CjEKFQoIMTIzMjEzMjESCXJpZ2h0IG5vdxIWY29tLmdvb2dsZS5hbmRyb2lkLmdtcxgA",
"attributes":{},"message_id":"71571141246"},"subscription":"/subscriptions/enterprise-cloud-pub-sub/default"} Feb 27, 2015 1:47:24 PM com.google.android.work.emmnotifications.PushSubscriber$1 handle INFO: Pubsub message received: { "attributes" : { }, "data" : "CjEKFQoIMTIzMjEzMjESCXJpZ2h0IG5vdxIWY29tLmdvb2dsZS5hbmRyb2lkLmdtcxgA", "message_id" : "71571141246" } Feb 27, 2015 1:47:24 PM com.google.android.work.emmnotifications.PushSubscriber$1 handle INFO: Message received: product_approval_event { common_event_information { enterprise_id: "12321321" event_notification_sent_timestamp: "right now" } product_id: "com.google.android.gms" approved: false }
消息已被正确接收和处理。