项目场景:
项目场景:A系统(客户端)定时向B系统(服务端)推送xx数据。
注意事项:
-
http + json 传输方式
-
定时任务
-
传输安全:
- 传输前:客户端携带认证信息向服务端获取token,token90秒有效。
- 传输时:客户端携带token,数据加密,服务端验证token并解密文件。
开发文档:
一、Tonken获取 接口
接口说明:获取访问接口的认证凭证。使用认证签名调用本接口来获取token
**认证签名: **MD5( SecurityKey)
数据 格式
Content-Type:application/json;charset=UTF-8;Authorization: Basic 认证签名 返回数据
参数名 参数描述 是否必传 参数类型 备注 status 状态标识 Y String success 成功、fail 失败 message 错误信息 N String 失败必传 Token Token令牌 Y String 成功必传 返回报文
{ "status": "success", "Token": "ea9b7b8fe2f14555953a7963323b40df", "message": ""} 注意事项:token有效期为90秒,每次调用保单接口需要重新获取token
二、xx数据推送 接口
接口说明:A系统(客户端)将xx数据推送到B系统(服务端);
接口地址:http://测试地址/pushData
数据
请求数据
参数名 参数描述 是否必传 参数类型 长度 备注 sendtime 发送时间 Y String data.id xx数据id Y String data.name xx数据名称 Y String 返回数据
参数名 参数描述 是否必传 参数类型 备注 status 状态标识 Y String success 成功、fail 失败 message 错误信息 N String 失败必传 name xx数据名称 Y String 报文实例
数据 格式
Content-Type:application/json;charset=UTF-8;Authorization: Bearer token 请求报文
{
"sendTime":"2021-08-19 16:20:00",
"data": "AESUtil.encrypt(json 字符串,securityKey)"
}
**注: **json 字符串为以下内容:
{ "data":[ { "id":"", "name":"" } ] } 返回报文
{ "status": "success", "name": "xxName...", "message": ""}
服务端代码开发
开发思路:
一、token接口开发
- 提供接口认证,验证【认证信息】。
- 生成token,并保存90秒:用本地缓存保存,带失效时间。
二、数据接收接口开发
- 验证token
- 解析数据
- 保存数据、并返回返回值
开发代码
一、token接口
1、提供接口认证,验证【认证信息】
/**
* @Description : TODO 获取token接口
**/
@Controller
public class GetTokenServer {
private final static Logger LOGGER = LoggerFactory.getLogger(GetTokenServer.class);
private String SECURITY_KEY = "6666";
@RequestMapping(value = "${path}/getToken", method = RequestMethod.POST)
public @ResponseBody ResultToken getToken(HttpServletRequest request) {
//Authorization: Basic 认证签名
String signaturePost = request.getHeader("authorization");
//截取认证签名,与 SECURITY_KEY 用 MD5 加密后的内容,比对
String signature = MD5Util.encrypt32(SECURITY_KEY);
if(!signaturePost.substring(6).equals(signature)){
//认证不通过返回
return new ResultToken(StatusEnum.SIGN_ERROR.code(),StatusEnum.SIGN_ERROR.message(),"");
}
//认证通过,生成token返回,并本地缓存带超时时间保存
String token = TokenUtil.createToken();
LOGGER.info("【获取token接口】系统生成token[{}]",token);
return new ResultToken(StatusEnum.SIGN_SUCCESS.code(),StatusEnum.SIGN_SUCCESS.message(),token);
}
}
复制代码
2、生成token,并保存90秒:用本地缓存保存,带失效时间。
即 String token = TokenUtil.createToken(); 方法
/**
* @Description TODO token工具类
*/
public class TokenUtil {
private final static Logger LOGGER = LoggerFactory.getLogger(TokenUtil.class);
private static final int EXPIRE_DATE= 90;
/**
* 生成并保存token,【过期时间90s】
*/
public static String createToken (){
//此处用uuid当作token
String token = IdGen.uuid();
CacheUtil.put(token,token,EXPIRE_DATE);
return token;
}
/**
* 验证token有效性: true有效,false无效
*/
public static boolean verifyToken(String token){
LOGGER.info("缓存中是否存在该token?=[{}]",CacheUtil.isContain(token));
if(!CacheUtil.isContain(token)){
return false;
}else {
return true;
}
}
}
复制代码
3、存放token的缓存实现
/**
* @Description : TODO 缓存
**/
public class CacheUtil {
//默认大小
private static final int DEFAULT_CAPACITY = 1024;
// 最大缓存大小
private static final int MAX_CAPACITY = 10000;
//默认缓存过期时间
private static final long DEFAULT_TIMEOUT = 3600;
//1000毫秒 = 1秒
private static final long SECOND_TIME = 1000;
//存储缓存的Map
private static final ConcurrentHashMap<String, Object> map;
private static final Timer timer;
static {
map = new ConcurrentHashMap<>(DEFAULT_CAPACITY);
timer = new Timer();
}
//私有化构造方法
private CacheUtil() {
}
/**
* @Description TODO 缓存清除
* @Param
* @return
*/
static class ClearTask extends TimerTask {
private String key;
public ClearTask(String key) {
this.key = key;
}
@Override
public void run() {
CacheUtil.remove(key);
}
}
/**
* @Description TODO 添加缓存
* @Param [java.lang.String, java.lang.Object]
* @return boolean
*/
public static boolean put(String key, Object object) {
if (checkCapacity()) {
map.put(key, object);
//默认缓存时间
timer.schedule(new ClearTask(key), DEFAULT_TIMEOUT);
return true;
}
return false;
}
/**
* @Description TODO 添加缓存,带过期时间
* @Param [java.lang.String, java.lang.Object, int]
* @return boolean
*/
public static boolean put(String key, Object object, int time_out) {
if (checkCapacity()) {
map.put(key, object);
//默认缓存时间
timer.schedule(new ClearTask(key), time_out * SECOND_TIME);
}
return false;
}
/**
* 判断容量
* @param
*/
public static boolean checkCapacity() {
return map.size() < MAX_CAPACITY;
}
/**
* 删除缓存
* @param key
*/
public static void remove(String key) {
map.remove(key);
}
/**
* 清除缓存
*/
public void clearAll() {
if (map.size() > 0) {
map.clear();
}
timer.cancel();
}
/**
* 获取缓存
* @param key
* @return
*/
public static Object get(String key) {
return map.get(key);
}
/**
* 是否包含某个缓存
* @param key
* @return
*/
public static boolean isContain(String key) {
return map.contains(key);
}
}
复制代码
二、接收数据
- 验证token
- 解析数据
- 保存数据、并返回返回值
@Controller
@RequestMapping(value = "${path}/")
public class xxController{
private static final Logger logger = LoggerFactory.getLogger(xxController.class);
@Autowired
private xxService xxService;
/**
* @Description TODO 接收推送的接口
*/
@RequestMapping(value = "pushData", method = RequestMethod.POST)
@ResponseBody
public ResultPolicy pushData(HttpServletRequest request) {
String authorization = request.getHeader("authorization");
logger.info("【authorization】[{}];",authorization);
//用本地缓存校验是否包含此token
if(!TokenUtil.verifyToken(authorization.substring(7))){
logger.info("token验证失败");
return new ResultPolicy(StatusEnum.TOKEN_NUll.code(),"",StatusEnum.TOKEN_NUll.message());
}
//token存在,解析数据
Map<String, String> json = HttpUtil.getJSON(request);
logger.info("【json数据为】[{}]",json);
if(json == null || json.size()==0){
return new ResultPolicy(StatusEnum.PARAM_NULL.code(),"",StatusEnum.PARAM_NULL.message());
}
//业务代码保存数据
return xxService.parsingAndSaveData(json);
}
}
复制代码
客户端代码开发:
开发思路:
一、http post请求 携带认证信息获取token
二、推送数据开发
- 解析到token放入推送请求的请求头中
- 发送请求
- 解析返回值
开发代码
总的方法概览
/**
* 推送数据总方法
*/
public void push() {
//需要推送的数据
List<PushEntity> pushEntityList = xxDao.findPushEntityList();
// 判断有需要推送的数据
if (pushEntityList != null && pushEntityList.size() > 0) {
// 获取token
String token = "";
try {
token = HttpUtil.sendPostForToken();
} catch (IOException e) {
logger.info("【推送保单获取token失败】");
}
// token获取成功
if (StringUtils.isNotBlank(token)) {
//逐条推送数据
pushItemByItem(token,pushEntityList);
}
}
}
复制代码
一、获取token方法
/**
* 发送POST请求,获取token
* 使用apache的httpclient方式发送请求
* @param
* @return token
*/
public static String sendPostForToken() throws IOException {
//构建post请求:
//1、请求地址
HttpPost httpPost = new HttpPost(GET_TOKEN_URL);
//2、请求头按照开发文档规定填写:md5 对密钥加密作为认证信息
httpPost.setHeader("Content-type", "application/json");
httpPost.setHeader("charset", "UTF-8");
httpPost.setHeader(AUTHORIZATION, "Basic " + MD5Util.encrypt32(securityKey));
//发送post请求:
try {
responseLogin = client.execute(httpPost);
} catch (IOException ioException) {
logger.info("token请求失败!");
ioException.printStackTrace();
return null;
}
//获取请求返回结果,body:
HttpEntity entity = responseLogin.getEntity();
String stringEntity = null;
try {
stringEntity = EntityUtils.toString(entity, "UTF-8");
} catch (IOException ioException) {
ioException.printStackTrace();
return null;
}
//用fastjson将返回body转成map对象:
Map<String, String> resultParams = JSON.parseObject(stringEntity, Map.class);
String token = resultParams.get("token");
String status = resultParams.get("status");
String message = resultParams.get("message");
if (StringUtils.isNotBlank(status)) {
if (status.equals("success")) {
logger.info("【获取到token】:[{}]", token);
return token;
} else {
logger.info("【未获取到token,错误信息】:[{}]", message);
return token;
}
} else {
logger.info("【无返回值内容】");
return token;
}
}
复制代码
二、携带token发送数据
1、方法总览
/**
* 逐条推送数据
*
* @param token
* @param pushEntityList
*/
public void pushItemByItem(String token, List<PushEntity> pushEntityList) {
// 逐条数据处理
for (PushEntity entity : pushEntityList) {
//组装数据,后面方法介绍
String data = HttpUtil.entityProcessing(entity);
// 携带token、数据,http推送数据 封装接收返回值的实体类PushResult,后面方法介绍
PushResult result = HttpUtil.sendPostForData(token, data);
//解析返回参数:返回参数为[name]
String nameList = result.getName();
if(result.getStatus().equals("success")) {
//推送成功
if(StringUtils.isNotBlank(nameList)) {
String name = nameList.substring(1, nameList.length() - 1);
if(StringUtils.isNotBlank(name)) {
//更新本地状态:推送成功
xxDao.updateXX(name,"1","success");
logger.info("【推送成功】name:[{}]", name);
}
}
}else {
//推送失败
if(StringUtils.isNotBlank(nameList)) {
String name = nameList.substring(1, nameList.length() - 1);
if(StringUtils.isNotBlank(name)) {
//更新本地状态:推送失败
xxDao.updateXX(name,"2",result.getMessage());
logger.info("【推送失败】name:[{}]", name);
logger.info("【推送失败】错误信息:[{}]", result.getMessage());
}
}
}
}
}
复制代码
2、 组装数据HttpUtil.entityProcessing(entity);
/**
* 组装数据
* @param policy
* @return
*/
@SuppressWarnings("unchecked")
public static String entityProcessing(PushEntity pushEntity) {
//第一步:构建加密data数据
//单条数据构建list集合
@SuppressWarnings("rawtypes")
List<PushEntity> list = new ArrayList<PushEntity>();
list.add(pushEntity);
String jsonStrEntityList = JSON.toJSONString(list);
//集合作为map的value , map的key为“data”(依据开发文档)
Map<String,String> dataMap = new HashMap<String,String>();
dataMap.put("data", jsonStrEntityList);
//将map用fastjson转为string类型
String dataMapJsonString = JSON.toJSONString(dataMap);
//依据文档加密
String data = AESUtil.encrypt(dataMapJsonString, securityKey);
//第二步:构建请求发送数据
Map<String,String> resultMap = new HashMap<String,String>();
SimpleDateFormat sdfAll = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
resultMap.put("sendTime", sdfAll.format(System.currentTimeMillis()));
resultMap.put("data", data);
String result = JSON.toJSONString(resultMap);
return result;
}
复制代码
3、httpclient发送数据HttpUtil.sendPostForData(token, data);
/**
* 发送POST请求,推送数据,成功后获取name
* @param token
* @param data
* @return String[] name数组
*/
public static PushEntityResult sendPostForData(String token, String data) {
//构建请求地址
HttpPost httpPost = new HttpPost(PUSH_POLICY_URL);
//构建请求头:token信息
httpPost.setHeader(HTTP.CONTENT_TYPE, "application/json");
httpPost.setHeader("charset", "UTF-8");
httpPost.setHeader(AUTHORIZATION, "Bearer " + token);
//构建请求体,组装好的数据
StringEntity paramsString = new StringEntity(data, "utf-8");
paramsString.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,"application/json"));
httpPost.setEntity(paramsString);
//发送请求
try {
responseLogin = client.execute(httpPost);
} catch (IOException ioException) {
logger.info("推送请求失败!");
ioException.printStackTrace();
}
//解析返回参数body
HttpEntity entity = responseLogin.getEntity();
String stringEntity = null;
try {
stringEntity = EntityUtils.toString(entity, "UTF-8");
} catch (IOException ioException) {
ioException.printStackTrace();
}
//body转为map对象
@SuppressWarnings("unchecked")
Map<String, String> resultParams = JSON.parseObject(stringEntity, Map.class);
String name = resultParams.get("name");
logger.info("【推送请求返回值name】:[{}]", name);
String status = resultParams.get("status");
logger.info("【推送请求返回值status】:[{}]", status);
String message = resultParams.get("message");
logger.info("【推送请求返回值message】:[{}]", message);
//构建返回值对象:也可以将json返回值直接转为返回值对象
PushEntityResult result = new PushEntityResult(status,name,message);
return result;
}