优化平板信息获取,优化推送复用及增加用户推送

This commit is contained in:
2025-08-22 18:04:35 +08:00
parent 6b5eeee6ee
commit 1038c1b0d4
6 changed files with 207 additions and 49 deletions

View File

@@ -1,31 +1,21 @@
package com.onekeycall.videotablet.controller; package com.onekeycall.videotablet.controller;
import com.google.gson.JsonObject;
import com.onekeycall.videotablet.entity.DeviceInfo; import com.onekeycall.videotablet.entity.DeviceInfo;
import com.onekeycall.videotablet.entity.User; import com.onekeycall.videotablet.entity.User;
import com.onekeycall.videotablet.result.Result; import com.onekeycall.videotablet.result.Result;
import com.onekeycall.videotablet.service.DeviceSnService; import com.onekeycall.videotablet.service.DeviceSnService;
import com.onekeycall.videotablet.service.UserService; import com.onekeycall.videotablet.service.UserService;
import com.onekeycall.videotablet.utils.JwtUtil; import com.onekeycall.videotablet.utils.JwtUtil;
import com.onekeycall.videotablet.utils.PushUtils; import com.onekeycall.videotablet.utils.DevicePushUtils;
import com.onekeycall.videotablet.utils.TextUtils; import com.onekeycall.videotablet.utils.TextUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.crypto.SecretKey;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -82,11 +72,15 @@ public class BindSnController {
return Result.error().message("sn already bind"); return Result.error().message("sn already bind");
} }
try { try {
String verifyKey = RandomStringUtils.randomAlphanumeric(32); String verifyKey = RandomStringUtils.randomAlphanumeric(32);
// PushUtils.aliyunAsyncPush(verifyKey, userPhone, sn); JsonObject params = new JsonObject();
PushUtils.tpnsPush(verifyKey, userPhone, sn); params.addProperty("verify_key", verifyKey);
params.addProperty("phone", userPhone);
params.addProperty("expire_time", System.currentTimeMillis() + 60 * 1000);
// PushUtils.aliyunAsyncPush("1", params.toString(), sn);
DevicePushUtils.tpnsPush("1", params.toString(), sn);
redisTemplate.opsForValue().set(sn, verifyKey, 1, TimeUnit.MINUTES); redisTemplate.opsForValue().set(sn, verifyKey, 1, TimeUnit.MINUTES);
return Result.ok().message("send message success"); return Result.ok().message("send message success");
} catch (Exception e) { } catch (Exception e) {
@@ -147,7 +141,7 @@ public class BindSnController {
oldDeviceInfo.setToken(deviceToken); oldDeviceInfo.setToken(deviceToken);
oldDeviceInfo.setSn(sn); oldDeviceInfo.setSn(sn);
deviceSnService.save(oldDeviceInfo); deviceSnService.save(oldDeviceInfo);
Map<String, Object> map = new HashMap<>(); Map<String, Object> map = new LinkedHashMap<>();
map.put("phone", phone); map.put("phone", phone);
map.put("device_token", deviceToken); map.put("device_token", deviceToken);
map.put("device_sig", deviceSig); map.put("device_sig", deviceSig);
@@ -170,16 +164,32 @@ public class BindSnController {
@RequestHeader("Device-Sig") String deviceSig, @RequestHeader("Device-Sig") String deviceSig,
@RequestParam(value = "sn") String sn) { @RequestParam(value = "sn") String sn) {
if (!jwtUtil.validateDeviceToken(deviceToken, deviceId, sn)) {
return Result.error().message("Invalid token");
}
DeviceInfo deviceInfo = deviceSnService.findBySn(sn); DeviceInfo deviceInfo = deviceSnService.findBySn(sn);
if (deviceInfo == null) { if (deviceInfo == null) {
return Result.notFound().message("sn not found"); return Result.notFound().message("sn not found");
} }
if (!deviceInfo.getBindSig().equals(deviceSig)) {
return Result.error().message("device sig not match");
}
if (TextUtils.isEmpty(deviceInfo.getBindPhone())) { if (TextUtils.isEmpty(deviceInfo.getBindPhone())) {
return Result.error().message("sn not bind"); return Result.error().message("sn not bind");
} }
return Result.ok().message("sn bind"); Map<String, Object> map = new LinkedHashMap<>();
map.put("bind_status", 1);
map.put("device_alias", deviceInfo.getDeviceAlias());
map.put("bind_phone", deviceInfo.getBindPhone());
map.put("user_id", deviceInfo.getUserId());
map.put("add_time", deviceInfo.getAddTime());
map.put("bind_time", deviceInfo.getBindTime());
return Result.ok().data(map).message("sn bind");
} }
} }

View File

@@ -1,14 +1,7 @@
package com.onekeycall.videotablet.controller; package com.onekeycall.videotablet.controller;
import com.onekeycall.videotablet.entity.DeviceInfo;
import com.onekeycall.videotablet.entity.User;
import com.onekeycall.videotablet.result.Result;
import com.onekeycall.videotablet.service.DeviceSnService; import com.onekeycall.videotablet.service.DeviceSnService;
import com.onekeycall.videotablet.service.UserService;
import com.onekeycall.videotablet.utils.JwtUtil; import com.onekeycall.videotablet.utils.JwtUtil;
import com.onekeycall.videotablet.utils.PushUtils;
import com.onekeycall.videotablet.utils.TextUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;

View File

@@ -1,15 +1,12 @@
package com.onekeycall.videotablet.controller; package com.onekeycall.videotablet.controller;
import com.onekeycall.videotablet.entity.DeviceInfo; import com.onekeycall.videotablet.entity.DeviceInfo;
import com.onekeycall.videotablet.entity.User;
import com.onekeycall.videotablet.result.Result; import com.onekeycall.videotablet.result.Result;
import com.onekeycall.videotablet.service.DeviceSnService; import com.onekeycall.videotablet.service.DeviceSnService;
import com.onekeycall.videotablet.service.UserService; import com.onekeycall.videotablet.service.UserService;
import com.onekeycall.videotablet.utils.CXAESUtil; import com.onekeycall.videotablet.utils.CXAESUtil;
import com.onekeycall.videotablet.utils.JwtUtil; import com.onekeycall.videotablet.utils.JwtUtil;
import com.onekeycall.videotablet.utils.PushUtils;
import com.onekeycall.videotablet.utils.TextUtils; import com.onekeycall.videotablet.utils.TextUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -14,7 +14,6 @@ import com.tencent.xinge.bean.MessageAndroid;
import com.tencent.xinge.bean.MessageType; import com.tencent.xinge.bean.MessageType;
import com.tencent.xinge.push.app.PushAppRequest; import com.tencent.xinge.push.app.PushAppRequest;
import darabonba.core.client.ClientOverrideConfiguration; import darabonba.core.client.ClientOverrideConfiguration;
import org.glassfish.jaxb.core.v2.TODO;
import org.json.JSONObject; import org.json.JSONObject;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -23,10 +22,17 @@ import java.util.ArrayList;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
public class PushUtils { public class DevicePushUtils {
static Logger logger = LoggerFactory.getLogger(PushUtils.class); static Logger logger = LoggerFactory.getLogger(DevicePushUtils.class);
public static void aliyunAsyncPush(String verifyKey, String phone, String sn) throws ExecutionException, InterruptedException { /**
* @param title
* @param jsonString
* @param targetValue
* @throws ExecutionException
* @throws InterruptedException
*/
public static void aliyunAsyncPush(String title, String jsonString, String targetValue) throws ExecutionException, InterruptedException {
// HttpClient Configuration // HttpClient Configuration
/*HttpClient httpClient = new ApacheAsyncHttpClientBuilder() /*HttpClient httpClient = new ApacheAsyncHttpClientBuilder()
.connectionTimeout(Duration.ofSeconds(10)) // Set the connection timeout time, the default is 10 seconds .connectionTimeout(Duration.ofSeconds(10)) // Set the connection timeout time, the default is 10 seconds
@@ -64,10 +70,6 @@ public class PushUtils {
//.setConnectTimeout(Duration.ofSeconds(30)) //.setConnectTimeout(Duration.ofSeconds(30))
) )
.build(); .build();
JsonObject params = new JsonObject();
params.addProperty("verify_key", verifyKey);
params.addProperty("phone", phone);
params.addProperty("time", System.currentTimeMillis());
// Parameter settings for API request // Parameter settings for API request
PushRequest pushRequest = PushRequest.builder() PushRequest pushRequest = PushRequest.builder()
@@ -75,10 +77,10 @@ public class PushUtils {
.pushType("MESSAGE") .pushType("MESSAGE")
.deviceType("ANDROID") .deviceType("ANDROID")
.target("ALIAS") .target("ALIAS")
.targetValue(sn) .targetValue(targetValue)
.title("1") .title(title)
.storeOffline(true) .storeOffline(true)
.body(params.toString()) .body(jsonString)
// Request-level configuration rewrite, can set Http request parameters, etc. // Request-level configuration rewrite, can set Http request parameters, etc.
// .requestConfiguration(RequestConfiguration.create().setHttpHeaders(new HttpHeaders())) // .requestConfiguration(RequestConfiguration.create().setHttpHeaders(new HttpHeaders()))
.build(); .build();
@@ -100,7 +102,7 @@ public class PushUtils {
client.close(); client.close();
} }
public static void tpnsPush(String verifyKey, String phone, String sn) { public static void tpnsPush(String title, String jsonString, String targetValue) {
XingeApp xingeApp = new XingeApp.Builder() XingeApp xingeApp = new XingeApp.Builder()
.appId("1500043720") .appId("1500043720")
.secretKey("1712af9e3079087447f4c35d2f622b89") .secretKey("1712af9e3079087447f4c35d2f622b89")
@@ -116,22 +118,17 @@ public class PushUtils {
//todo 为1时不能接收到不知什么原因 https://console.cloud.tencent.com/tpns/user-tools/1500043720/ap-guangzhou-1 //todo 为1时不能接收到不知什么原因 https://console.cloud.tencent.com/tpns/user-tools/1500043720/ap-guangzhou-1
pushAppRequest.setAccount_push_type(0); pushAppRequest.setAccount_push_type(0);
JsonObject params = new JsonObject();
params.addProperty("verify_key", verifyKey);
params.addProperty("phone", phone);
params.addProperty("expire_time", System.currentTimeMillis() + 60 * 1000);
Message message = new Message(); Message message = new Message();
// 推送标题 // 推送标题
message.setTitle("1"); message.setTitle(title);
// 推送内容 // 推送内容
message.setContent(params.toString()); message.setContent(jsonString);
MessageAndroid messageAndroid = new MessageAndroid(); MessageAndroid messageAndroid = new MessageAndroid();
message.setAndroid(messageAndroid); message.setAndroid(messageAndroid);
pushAppRequest.setMessage(message); pushAppRequest.setMessage(message);
ArrayList<String> accountList = new ArrayList<>(); ArrayList<String> accountList = new ArrayList<>();
// 添加绑定账号 // 添加绑定账号
accountList.add(sn); accountList.add(targetValue);
// 设置账号列表 // 设置账号列表
pushAppRequest.setAccount_list(accountList); pushAppRequest.setAccount_list(accountList);

View File

@@ -188,6 +188,7 @@ public class JwtUtil {
/** /**
* 生成设备签名(首次绑定) * 生成设备签名(首次绑定)
*
* @param sn 设备序列号 * @param sn 设备序列号
* @return 设备唯一签名 * @return 设备唯一签名
*/ */
@@ -202,6 +203,7 @@ public class JwtUtil {
/** /**
* 生成设备令牌联合SN+deviceId * 生成设备令牌联合SN+deviceId
*
* @param sn 设备序列号 * @param sn 设备序列号
* @param deviceId 设备ID * @param deviceId 设备ID
* @return JWT格式令牌 * @return JWT格式令牌
@@ -217,11 +219,29 @@ public class JwtUtil {
/** /**
* 验证设备令牌 * 验证设备令牌
*
* @param deviceToken 设备令牌 * @param deviceToken 设备令牌
* @return 验证结果 * @return 验证结果
*/ */
public Claims validateDeviceToken(String deviceToken) { public boolean validateDeviceToken(String deviceToken, String deviceId, String sn) {
Claims claims = parseDeviceToken(deviceToken);
//验证设备ID一致性
String tokenDeviceId = claims.getSubject();
if (!deviceId.equals(tokenDeviceId)) {
throw new SecurityException("设备ID不匹配");
}
//验证SN一致性
String tokenSn = claims.get("sn", String.class);
if (!sn.equals(tokenSn)) {
throw new SecurityException("SN不匹配");
}
return true; // 通过所有校验
}
public Claims parseDeviceToken(String deviceToken) {
try { try {
return Jwts.parser() return Jwts.parser()
.verifyWith(Keys.hmacShaKeyFor(TABLET_SECRET.getBytes())) .verifyWith(Keys.hmacShaKeyFor(TABLET_SECRET.getBytes()))

View File

@@ -0,0 +1,141 @@
package com.onekeycall.videotablet.utils;
import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.sdk.service.push20160801.AsyncClient;
import com.aliyun.sdk.service.push20160801.models.PushRequest;
import com.aliyun.sdk.service.push20160801.models.PushResponse;
import com.google.gson.Gson;
import com.tencent.xinge.XingeApp;
import com.tencent.xinge.bean.AudienceType;
import com.tencent.xinge.bean.Message;
import com.tencent.xinge.bean.MessageAndroid;
import com.tencent.xinge.bean.MessageType;
import com.tencent.xinge.push.app.PushAppRequest;
import darabonba.core.client.ClientOverrideConfiguration;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class UserPushUtils {
static Logger logger = LoggerFactory.getLogger(UserPushUtils.class);
/**
* @param title
* @param jsonString
* @param targetValue
* @throws ExecutionException
* @throws InterruptedException
*/
public static void aliyunAsyncPush(String title, String jsonString, String targetValue) throws ExecutionException, InterruptedException {
// HttpClient Configuration
/*HttpClient httpClient = new ApacheAsyncHttpClientBuilder()
.connectionTimeout(Duration.ofSeconds(10)) // Set the connection timeout time, the default is 10 seconds
.responseTimeout(Duration.ofSeconds(10)) // Set the response timeout time, the default is 20 seconds
.maxConnections(128) // Set the connection pool size
.maxIdleTimeOut(Duration.ofSeconds(50)) // Set the connection pool timeout, the default is 30 seconds
// Configure the proxy
.proxy(new ProxyOptions(ProxyOptions.Type.HTTP, new InetSocketAddress("<your-proxy-hostname>", 9001))
.setCredentials("<your-proxy-username>", "<your-proxy-password>"))
// If it is an https connection, you need to configure the certificate, or ignore the certificate(.ignoreSSL(true))
.x509TrustManagers(new X509TrustManager[]{})
.keyManagers(new KeyManager[]{})
.ignoreSSL(false)
.build();*/
// Configure Credentials authentication information, including ak, secret, token
StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
// Please ensure that the environment variables ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET are set.
.accessKeyId("LTAI5tAffPd7YNsEbNfQSiwU")
.accessKeySecret("6RzsuGceNOUD0WFpRfQRZ8eLXdHtFA")
//.securityToken(System.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")) // use STS token
.build());
// Configure the Client
AsyncClient client = AsyncClient.builder()
.region("cn-chengdu") // Region ID
//.httpClient(httpClient) // Use the configured HttpClient, otherwise use the default HttpClient (Apache HttpClient)
.credentialsProvider(provider)
//.serviceConfiguration(Configuration.create()) // Service-level configuration
// Client-level configuration rewrite, can set Endpoint, Http request parameters, etc.
.overrideConfiguration(
ClientOverrideConfiguration.create()
// Endpoint 请参考 https://api.aliyun.com/product/Push
.setEndpointOverride("cloudpush.aliyuncs.com")
//.setConnectTimeout(Duration.ofSeconds(30))
)
.build();
// Parameter settings for API request
PushRequest pushRequest = PushRequest.builder()
.appKey(335580260L)
.pushType("MESSAGE")
.deviceType("ANDROID")
.target("ALIAS")
.targetValue(targetValue)
.title(title)
.storeOffline(true)
.body(jsonString)
// Request-level configuration rewrite, can set Http request parameters, etc.
// .requestConfiguration(RequestConfiguration.create().setHttpHeaders(new HttpHeaders()))
.build();
// Asynchronously get the return value of the API request
CompletableFuture<PushResponse> response = client.push(pushRequest);
// Synchronously get the return value of the API request
PushResponse resp = response.get();
logger.info(new Gson().toJson(resp));
// Asynchronous processing of return values
/*response.thenAccept(resp -> {
logger.info(new Gson().toJson(resp));
}).exceptionally(throwable -> { // Handling exceptions
logger.info(throwable.getMessage());
return null;
});*/
// Finally, close the client
client.close();
}
public static void tpnsPush(String title, String jsonString, String targetValue) {
XingeApp xingeApp = new XingeApp.Builder()
.appId("1500043720")
.secretKey("1712af9e3079087447f4c35d2f622b89")
.domainUrl("https://api.tpns.tencent.com/")
.build();
PushAppRequest pushAppRequest = new PushAppRequest();
// 选择推送目标类型
pushAppRequest.setAudience_type(AudienceType.account);
// 消息类型:通知栏或透传消息
pushAppRequest.setMessage_type(MessageType.message);
// 选择推送的账号类型
//todo 为1时不能接收到不知什么原因 https://console.cloud.tencent.com/tpns/user-tools/1500043720/ap-guangzhou-1
pushAppRequest.setAccount_push_type(0);
Message message = new Message();
// 推送标题
message.setTitle(title);
// 推送内容
message.setContent(jsonString);
MessageAndroid messageAndroid = new MessageAndroid();
message.setAndroid(messageAndroid);
pushAppRequest.setMessage(message);
ArrayList<String> accountList = new ArrayList<>();
// 添加绑定账号
accountList.add(targetValue);
// 设置账号列表
pushAppRequest.setAccount_list(accountList);
JSONObject ret = xingeApp.pushApp(pushAppRequest);
logger.info(ret.toString());
}
}