支持鸿蒙推送
This commit is contained in:
9
config/hm.properties
Normal file
9
config/hm.properties
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# 请参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/push-server-intro-0000001862404301-V5
|
||||||
|
#请在代码中加密私钥,仅获取 '-----BEGIN PRIVATE KEY-----\n' 和 '\n-----END PRIVATE KEY-----\n'之间的字符
|
||||||
|
hm.privateKey=a4e5e6a***
|
||||||
|
# 请使用服务帐号密钥文件中的sub_account替换
|
||||||
|
hm.iss=1002***
|
||||||
|
# 请使用服务帐号密钥文件中的key_id替换
|
||||||
|
hm.kid=184d3688732245d***
|
||||||
|
# 项目 id
|
||||||
|
hm.projectId=38842184***
|
||||||
43
pom.xml
43
pom.xml
@@ -99,33 +99,6 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<!-- <dependency>-->
|
|
||||||
<!-- <groupId>org.apache.commons.codec</groupId>-->
|
|
||||||
<!-- <artifactId>commons-codec</artifactId>-->
|
|
||||||
<!-- <version>1.9</version>-->
|
|
||||||
<!-- <scope>system</scope>-->
|
|
||||||
<!-- <systemPath>${project.basedir}/src/main/libs/commons-codec-1.9.jar-->
|
|
||||||
<!-- </systemPath>-->
|
|
||||||
<!-- </dependency>-->
|
|
||||||
|
|
||||||
<!-- <dependency>-->
|
|
||||||
<!-- <groupId>org.apache.commons.logging</groupId>-->
|
|
||||||
<!-- <artifactId>commons-logging</artifactId>-->
|
|
||||||
<!-- <version>1.2</version>-->
|
|
||||||
<!-- <scope>system</scope>-->
|
|
||||||
<!-- <systemPath>${project.basedir}/src/main/libs/commons-logging-1.2.jar-->
|
|
||||||
<!-- </systemPath>-->
|
|
||||||
<!-- </dependency>-->
|
|
||||||
|
|
||||||
<!-- <dependency>-->
|
|
||||||
<!-- <groupId>com.alibaba.fastjson</groupId>-->
|
|
||||||
<!-- <artifactId>fastjson</artifactId>-->
|
|
||||||
<!-- <version>1.2.28</version>-->
|
|
||||||
<!-- <scope>system</scope>-->
|
|
||||||
<!-- <systemPath>${project.basedir}/src/main/libs/fastjson-1.2.28.jar-->
|
|
||||||
<!-- </systemPath>-->
|
|
||||||
<!-- </dependency>-->
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.http</groupId>
|
<groupId>org.apache.http</groupId>
|
||||||
<artifactId>httpclient</artifactId>
|
<artifactId>httpclient</artifactId>
|
||||||
@@ -233,12 +206,16 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<!-- <dependency>-->
|
<dependency>
|
||||||
<!-- <groupId>io.netty</groupId>-->
|
<groupId>com.auth0</groupId>
|
||||||
<!-- <artifactId>netty-tcnative-boringssl-static</artifactId>-->
|
<artifactId>java-jwt</artifactId>
|
||||||
<!-- <version>2.0.25.Final</version>-->
|
<version>3.8.1</version>
|
||||||
<!-- <scope>runtime</scope>-->
|
</dependency>
|
||||||
<!-- </dependency>-->
|
<dependency>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
<version>1.16.0</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package cn.wildfirechat.push;
|
package cn.wildfirechat.push;
|
||||||
|
|
||||||
import cn.wildfirechat.push.android.AndroidPushService;
|
import cn.wildfirechat.push.android.AndroidPushService;
|
||||||
|
import cn.wildfirechat.push.hm.HMPushService;
|
||||||
import cn.wildfirechat.push.ios.IOSPushService;
|
import cn.wildfirechat.push.ios.IOSPushService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@@ -16,13 +17,21 @@ public class PushController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private IOSPushService mIOSPushService;
|
private IOSPushService mIOSPushService;
|
||||||
|
|
||||||
@PostMapping(value = "/android/push", produces = "application/json;charset=UTF-8" )
|
@Autowired
|
||||||
|
private HMPushService hmPushService;
|
||||||
|
|
||||||
|
@PostMapping(value = "/android/push", produces = "application/json;charset=UTF-8")
|
||||||
public Object androidPush(@RequestBody PushMessage pushMessage) {
|
public Object androidPush(@RequestBody PushMessage pushMessage) {
|
||||||
return mAndroidPushService.push(pushMessage);
|
return mAndroidPushService.push(pushMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(value = "/ios/push", produces = "application/json;charset=UTF-8" )
|
@PostMapping(value = "/ios/push", produces = "application/json;charset=UTF-8")
|
||||||
public Object iOSPush(@RequestBody PushMessage pushMessage) {
|
public Object iOSPush(@RequestBody PushMessage pushMessage) {
|
||||||
return mIOSPushService.push(pushMessage);
|
return mIOSPushService.push(pushMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/hm/push", produces = "application/json;charset=UTF-8")
|
||||||
|
public Object hmPush(@RequestBody PushMessage pushMessage) {
|
||||||
|
return hmPushService.push(pushMessage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
47
src/main/java/cn/wildfirechat/push/hm/HMConfig.java
Normal file
47
src/main/java/cn/wildfirechat/push/hm/HMConfig.java
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package cn.wildfirechat.push.hm;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.PropertySource;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "hm")
|
||||||
|
@PropertySource(value = "file:config/hm.properties")
|
||||||
|
public class HMConfig {
|
||||||
|
private String privateKey;
|
||||||
|
private String iss;
|
||||||
|
private String kid;
|
||||||
|
private String projectId;
|
||||||
|
|
||||||
|
public String getPrivateKey() {
|
||||||
|
return privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrivateKey(String privateKey) {
|
||||||
|
this.privateKey = privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIss() {
|
||||||
|
return iss;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIss(String iss) {
|
||||||
|
this.iss = iss;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKid() {
|
||||||
|
return kid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKid(String kid) {
|
||||||
|
this.kid = kid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProjectId() {
|
||||||
|
return projectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProjectId(String projectId) {
|
||||||
|
this.projectId = projectId;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/main/java/cn/wildfirechat/push/hm/HMPushService.java
Normal file
7
src/main/java/cn/wildfirechat/push/hm/HMPushService.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package cn.wildfirechat.push.hm;
|
||||||
|
|
||||||
|
import cn.wildfirechat.push.PushMessage;
|
||||||
|
|
||||||
|
public interface HMPushService {
|
||||||
|
Object push(PushMessage pushMessage);
|
||||||
|
}
|
||||||
154
src/main/java/cn/wildfirechat/push/hm/HMPushServiceImpl.java
Normal file
154
src/main/java/cn/wildfirechat/push/hm/HMPushServiceImpl.java
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
package cn.wildfirechat.push.hm;
|
||||||
|
|
||||||
|
import cn.wildfirechat.push.PushMessage;
|
||||||
|
import cn.wildfirechat.push.PushMessageType;
|
||||||
|
import cn.wildfirechat.push.Utility;
|
||||||
|
import cn.wildfirechat.push.hm.payload.AlertPayload;
|
||||||
|
import cn.wildfirechat.push.hm.payload.VoipPayload;
|
||||||
|
import com.auth0.jwt.JWT;
|
||||||
|
import com.auth0.jwt.JWTCreator;
|
||||||
|
import com.auth0.jwt.algorithms.Algorithm;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class HMPushServiceImpl implements HMPushService {
|
||||||
|
private static final String AUD = "https://oauth-login.cloud.huawei.com/oauth2/v3/token";
|
||||||
|
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(HMPushServiceImpl.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
HMConfig config;
|
||||||
|
|
||||||
|
private String pushUrl;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
void setupPushUrl() {
|
||||||
|
this.pushUrl = String.format("https://push-api.cloud.huawei.com/v3/%s/messages:send", config.getProjectId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createJwt() throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||||
|
RSAPrivateKey prk = (RSAPrivateKey) getPrivateKey(config.getPrivateKey());
|
||||||
|
Algorithm algorithm = Algorithm.RSA256(null, prk);
|
||||||
|
long iat = System.currentTimeMillis() / 1000;
|
||||||
|
long exp = iat + 3600;
|
||||||
|
JWTCreator.Builder builder =
|
||||||
|
JWT.create()
|
||||||
|
.withIssuer(config.getIss())
|
||||||
|
.withKeyId(config.getKid())
|
||||||
|
.withAudience(AUD)
|
||||||
|
.withClaim("iat", iat)
|
||||||
|
.withClaim("exp", exp);
|
||||||
|
String jwt = builder.sign(algorithm);
|
||||||
|
return jwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PrivateKey getPrivateKey(String key) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||||
|
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodeBase64(key));
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||||
|
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
|
||||||
|
return privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] decodeBase64(String key) {
|
||||||
|
return Base64.decodeBase64(key.getBytes(DEFAULT_CHARSET));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object push(PushMessage pushMessage) {
|
||||||
|
try {
|
||||||
|
String jwt = null;
|
||||||
|
jwt = createJwt();
|
||||||
|
|
||||||
|
if (pushMessage.pushMessageType == PushMessageType.PUSH_MESSAGE_TYPE_RECALLED || pushMessage.pushMessageType == PushMessageType.PUSH_MESSAGE_TYPE_DELETED) {
|
||||||
|
//Todo not implement
|
||||||
|
//撤回或者删除消息,需要更新远程通知,暂未实现
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pushMessage.pushMessageType == PushMessageType.PUSH_MESSAGE_TYPE_VOIP_INVITE || pushMessage.pushMessageType == PushMessageType.PUSH_MESSAGE_TYPE_VOIP_INVITE) {
|
||||||
|
// TODO
|
||||||
|
String data = "TODO";
|
||||||
|
VoipPayload voipPayload = VoipPayload.buildAlertPayload(pushMessage.getDeviceToken(), data);
|
||||||
|
httpPost(this.pushUrl, jwt, 10, voipPayload.toString(), 10000, 10000);
|
||||||
|
} else {
|
||||||
|
String[] titleAndContent = Utility.getPushTitleAndContent(pushMessage);
|
||||||
|
AlertPayload alertPayload = AlertPayload.buildAlertPayload(pushMessage.getDeviceToken(), titleAndContent[0], titleAndContent[1]);
|
||||||
|
String response = httpPost(this.pushUrl, jwt, 0, alertPayload.toString(), 10000, 10000);
|
||||||
|
LOG.info("Push to {} response {}", pushMessage.getDeviceToken(), response);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (NoSuchAlgorithmException | InvalidKeySpecException | IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String httpPost(String httpUrl, String jwt, int pushType, String data, int connectTimeout, int readTimeout) throws IOException {
|
||||||
|
OutputStream outPut = null;
|
||||||
|
HttpURLConnection urlConnection = null;
|
||||||
|
InputStream in = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
URL url = new URL(httpUrl);
|
||||||
|
urlConnection = (HttpURLConnection) url.openConnection();
|
||||||
|
urlConnection.setRequestMethod("POST");
|
||||||
|
urlConnection.setDoOutput(true);
|
||||||
|
urlConnection.setDoInput(true);
|
||||||
|
urlConnection.setRequestProperty("Content-Type", "application/json");
|
||||||
|
urlConnection.setRequestProperty("Authorization", "Bearer " + jwt);
|
||||||
|
urlConnection.setRequestProperty("push-type", pushType + "");
|
||||||
|
urlConnection.setConnectTimeout(connectTimeout);
|
||||||
|
urlConnection.setReadTimeout(readTimeout);
|
||||||
|
urlConnection.connect();
|
||||||
|
|
||||||
|
// POST data
|
||||||
|
outPut = urlConnection.getOutputStream();
|
||||||
|
outPut.write(data.getBytes("UTF-8"));
|
||||||
|
outPut.flush();
|
||||||
|
|
||||||
|
// read response
|
||||||
|
if (urlConnection.getResponseCode() < 400) {
|
||||||
|
in = urlConnection.getInputStream();
|
||||||
|
} else {
|
||||||
|
in = urlConnection.getErrorStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> lines = IOUtils.readLines(in, urlConnection.getContentEncoding());
|
||||||
|
StringBuffer strBuf = new StringBuffer();
|
||||||
|
for (String line : lines) {
|
||||||
|
strBuf.append(line);
|
||||||
|
}
|
||||||
|
// LOG.info(strBuf.toString());
|
||||||
|
return strBuf.toString();
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(outPut);
|
||||||
|
IOUtils.closeQuietly(in);
|
||||||
|
if (urlConnection != null) {
|
||||||
|
urlConnection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package cn.wildfirechat.push.hm.payload;
|
||||||
|
|
||||||
|
import cn.wildfirechat.push.hm.payload.internal.ClickAction;
|
||||||
|
import cn.wildfirechat.push.hm.payload.internal.Notification;
|
||||||
|
import cn.wildfirechat.push.hm.payload.internal.Payload;
|
||||||
|
import cn.wildfirechat.push.hm.payload.internal.Target;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
|
||||||
|
public class AlertPayload {
|
||||||
|
Payload payload;
|
||||||
|
Target target;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new Gson().toJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AlertPayload buildAlertPayload(String token, String title, String body) {
|
||||||
|
Notification notification = new Notification();
|
||||||
|
notification.title = title;
|
||||||
|
notification.body = body;
|
||||||
|
|
||||||
|
ClickAction clickAction = new ClickAction();
|
||||||
|
notification.clickAction = clickAction;
|
||||||
|
|
||||||
|
Target target = new Target();
|
||||||
|
target.token = new ArrayList<>();
|
||||||
|
target.token.add(token);
|
||||||
|
|
||||||
|
AlertPayload alertPayload = new AlertPayload();
|
||||||
|
alertPayload.payload = new Payload();
|
||||||
|
alertPayload.payload.notification = notification;
|
||||||
|
alertPayload.target = target;
|
||||||
|
|
||||||
|
return alertPayload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package cn.wildfirechat.push.hm.payload;
|
||||||
|
|
||||||
|
import cn.wildfirechat.push.hm.payload.internal.ClickAction;
|
||||||
|
import cn.wildfirechat.push.hm.payload.internal.Notification;
|
||||||
|
import cn.wildfirechat.push.hm.payload.internal.Payload;
|
||||||
|
import cn.wildfirechat.push.hm.payload.internal.Target;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
|
||||||
|
public class VoipPayload {
|
||||||
|
Payload payload;
|
||||||
|
Target target;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new Gson().toJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static VoipPayload buildAlertPayload(String token, String extraData) {
|
||||||
|
Target target = new Target();
|
||||||
|
target.token = new ArrayList<>();
|
||||||
|
target.token.add(token);
|
||||||
|
|
||||||
|
VoipPayload voipPayload = new VoipPayload();
|
||||||
|
voipPayload.payload = new Payload();
|
||||||
|
voipPayload.payload.extraData = extraData;
|
||||||
|
voipPayload.target = target;
|
||||||
|
|
||||||
|
return voipPayload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package cn.wildfirechat.push.hm.payload.internal;
|
||||||
|
|
||||||
|
public class ClickAction {
|
||||||
|
/**
|
||||||
|
* 0:打开应用首页
|
||||||
|
* <p>
|
||||||
|
* 1:打开应用自定义页面
|
||||||
|
*/
|
||||||
|
public int actionType;
|
||||||
|
|
||||||
|
public String action;
|
||||||
|
public String uri;
|
||||||
|
public String data;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package cn.wildfirechat.push.hm.payload.internal;
|
||||||
|
|
||||||
|
public class Notification {
|
||||||
|
public String category = "IM";
|
||||||
|
public String title;
|
||||||
|
public String body;
|
||||||
|
public ClickAction clickAction;
|
||||||
|
public int style;
|
||||||
|
public String image;
|
||||||
|
public Integer notifyId;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package cn.wildfirechat.push.hm.payload.internal;
|
||||||
|
|
||||||
|
public class Payload {
|
||||||
|
public Notification notification;
|
||||||
|
public String extraData;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package cn.wildfirechat.push.hm.payload.internal;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class Target {
|
||||||
|
public ArrayList<String> token;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user