支持鸿蒙推送

This commit is contained in:
imndx
2024-04-23 18:52:26 +08:00
parent 6af09f4d6e
commit a738e2d028
12 changed files with 352 additions and 35 deletions

View File

@@ -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);
}
}

View 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;
}
}

View File

@@ -0,0 +1,7 @@
package cn.wildfirechat.push.hm;
import cn.wildfirechat.push.PushMessage;
public interface HMPushService {
Object push(PushMessage pushMessage);
}

View 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();
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -0,0 +1,6 @@
package cn.wildfirechat.push.hm.payload.internal;
public class Payload {
public Notification notification;
public String extraData;
}

View File

@@ -0,0 +1,7 @@
package cn.wildfirechat.push.hm.payload.internal;
import java.util.ArrayList;
public class Target {
public ArrayList<String> token;
}