支持鸿蒙推送
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>-->
|
||||
<!-- <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>
|
||||
<groupId>org.apache.http</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
@@ -233,12 +206,16 @@
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>io.netty</groupId>-->
|
||||
<!-- <artifactId>netty-tcnative-boringssl-static</artifactId>-->
|
||||
<!-- <version>2.0.25.Final</version>-->
|
||||
<!-- <scope>runtime</scope>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>com.auth0</groupId>
|
||||
<artifactId>java-jwt</artifactId>
|
||||
<version>3.8.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.16.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cn.wildfirechat.push;
|
||||
|
||||
import cn.wildfirechat.push.android.AndroidPushService;
|
||||
import cn.wildfirechat.push.hm.HMPushService;
|
||||
import cn.wildfirechat.push.ios.IOSPushService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -16,13 +17,21 @@ public class PushController {
|
||||
@Autowired
|
||||
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) {
|
||||
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) {
|
||||
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