parent
d764e17d1e
commit
d15741ce8f
14 changed files with 820 additions and 0 deletions
@ -0,0 +1,112 @@ |
||||
package org.springblade.weixin.controller; |
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils; |
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
||||
import io.swagger.annotations.Api; |
||||
import lombok.AllArgsConstructor; |
||||
import org.springblade.common.cache.DictCache; |
||||
import org.springblade.common.enums.DictEnum; |
||||
import org.springblade.core.log.exception.ServiceException; |
||||
import org.springblade.core.tool.api.R; |
||||
import org.springblade.core.tool.support.Kv; |
||||
import org.springblade.core.tool.utils.SpringUtil; |
||||
import org.springblade.modules.auth.endpoint.BladeTokenEndPoint; |
||||
import org.springblade.modules.system.entity.User; |
||||
import org.springblade.modules.system.service.IUserService; |
||||
import org.springblade.weixin.entity.WeChatPhone; |
||||
import org.springblade.weixin.entity.WeChatPhoneInfo; |
||||
import org.springblade.weixin.entity.WeChatUser; |
||||
import org.springblade.weixin.service.IWeChatUserService; |
||||
import org.springblade.weixin.utils.WeChatUtil; |
||||
import org.springframework.web.bind.annotation.*; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
@RestController |
||||
@Api(tags = "登录-小程序") |
||||
@AllArgsConstructor |
||||
@RequestMapping("/app") |
||||
public class AppEbLoginController { |
||||
|
||||
private final IWeChatUserService weChatUserService; |
||||
|
||||
/** |
||||
* 获取openid |
||||
* |
||||
* @param weChatPhone |
||||
* @return |
||||
*/ |
||||
@PostMapping("/login") |
||||
public R<Map> login(@RequestBody WeChatPhone weChatPhone) { |
||||
//小程序appId appSecret
|
||||
// String appId = "wx432c2efe6df3b97a";
|
||||
// String appSecret = "859df8b167e74223e9237dee1b344524";
|
||||
String appId = DictCache.getValue(DictEnum.WECHAT_APP, "appId"); |
||||
String appSecret = DictCache.getValue(DictEnum.WECHAT_APP, "appSecret"); |
||||
|
||||
//小程序需要传来一个code
|
||||
cn.hutool.json.JSONObject accessTokenJson = WeChatUtil.getCode2Session(weChatPhone.getCode(), appId, appSecret); |
||||
String openid = accessTokenJson.get("openid", String.class); |
||||
System.out.println("accessTokenJson:" + accessTokenJson.toString()); |
||||
|
||||
Map<String, Object> map = new HashMap<>(); |
||||
map.put("openid", openid); |
||||
map.put("userInfo", null); |
||||
//根据openid获取本地用户信息
|
||||
WeChatUser user = weChatUserService.getOne(Wrappers.<WeChatUser>lambdaQuery().eq(WeChatUser::getOpenId, openid)); |
||||
if (user != null) { |
||||
map.put("userInfo", user); |
||||
// BladeTokenEndPoint point = SpringUtil.getBean(BladeTokenEndPoint.class);
|
||||
// Kv admin = point.token("000000", "admin", "21232f297a57a5a743894a0e4a801fc3", "", "");
|
||||
} |
||||
//获得响应的数据
|
||||
// Map<String,Object> responseBody = map;
|
||||
|
||||
//if (responseBody.get("errcode")!=null&&responseBody.get("errcode").equals(40029)){
|
||||
// return CommonResult.failed("code 无效");
|
||||
// }
|
||||
// 解密用户信息
|
||||
// String encryptedData = request.get("encryptedData");
|
||||
// String iv = request.get("iv");
|
||||
// String sessionKey = (String) responseBody.get("session_key");
|
||||
// Map<String, Object> userInfo = getDecryptedUserInfo(encryptedData, sessionKey, iv);
|
||||
|
||||
//这里可以对用户信息进行一些操作
|
||||
return R.data(map); |
||||
} |
||||
|
||||
/** |
||||
* 用前端请求接口获取的code换取用户手机号 |
||||
* 前端需要请求的接口:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html
|
||||
* |
||||
* @param weChatPhone |
||||
* @return |
||||
*/ |
||||
@PostMapping("/phone") |
||||
public R getPhone(@RequestBody WeChatPhone weChatPhone) { |
||||
//小程序appId appSecret
|
||||
// String appId = "wx432c2efe6df3b97a";
|
||||
// String appSecret = "859df8b167e74223e9237dee1b344524";
|
||||
String appId = DictCache.getValue(DictEnum.WECHAT_APP, "appId"); |
||||
String appSecret = DictCache.getValue(DictEnum.WECHAT_APP, "appSecret"); |
||||
|
||||
// 1.请求微信接口服务,获取accessToken
|
||||
cn.hutool.json.JSONObject accessTokenJson = WeChatUtil.getAccessToken(appId, appSecret); |
||||
System.out.println("accessTokenJson:" + accessTokenJson); |
||||
String accessToken = accessTokenJson.get("access_token", String.class); |
||||
System.out.println("accessToken:" + accessToken); |
||||
|
||||
// 2.请求微信接口服务,获取用户手机号信息
|
||||
if (StringUtils.isBlank(accessToken)) { |
||||
throw new ServiceException("获取access_token失败"); |
||||
} |
||||
|
||||
cn.hutool.json.JSONObject phoneNumberJson = WeChatUtil.getPhoneNumber(weChatPhone.getCode(), accessToken); |
||||
System.out.println("phoneNumberJson:" + phoneNumberJson); |
||||
WeChatPhoneInfo phoneInfo = phoneNumberJson.get("phone_info", WeChatPhoneInfo.class); |
||||
System.out.println("phoneInfo:" + phoneInfo.toString()); |
||||
return R.data(phoneInfo.getPurePhoneNumber()); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,45 @@ |
||||
package org.springblade.weixin.controller; |
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
||||
import lombok.AllArgsConstructor; |
||||
import org.springblade.core.mp.support.Condition; |
||||
import org.springblade.core.mp.support.Query; |
||||
import org.springblade.core.tool.api.R; |
||||
import org.springblade.core.tool.utils.Func; |
||||
import org.springblade.weixin.entity.WeChatUser; |
||||
import org.springblade.weixin.service.IWeChatUserService; |
||||
import org.springframework.web.bind.annotation.*; |
||||
|
||||
@RestController |
||||
@AllArgsConstructor |
||||
@RequestMapping("/weChatUser") |
||||
public class WeChatUserController { |
||||
|
||||
private final IWeChatUserService weChatUserService; |
||||
|
||||
@GetMapping("/list") |
||||
public R list(WeChatUser weChatUser, Query query) { |
||||
return R.data(weChatUserService.page(Condition.getPage(query), Wrappers.<WeChatUser>lambdaQuery(weChatUser))); |
||||
} |
||||
|
||||
@PostMapping("/save") |
||||
public R save(@RequestBody WeChatUser weChatUser) { |
||||
return R.status(weChatUserService.save(weChatUser)); |
||||
} |
||||
|
||||
@PostMapping("/update") |
||||
public R update(@RequestBody WeChatUser weChatUser) { |
||||
return R.status(weChatUserService.updateById(weChatUser)); |
||||
} |
||||
|
||||
@PostMapping("/delete") |
||||
public R delete(@RequestParam String ids) { |
||||
return R.status(weChatUserService.deleteLogic(Func.toLongList(ids))); |
||||
} |
||||
|
||||
@GetMapping("/getWeChatUser") |
||||
public R getWeChatUser(String openId) { |
||||
return R.data(weChatUserService.getOne(Wrappers.<WeChatUser>lambdaQuery().eq(WeChatUser::getOpenId, openId))); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
package org.springblade.weixin.entity; |
||||
|
||||
import lombok.Data; |
||||
|
||||
@Data |
||||
public class WeChatPhone { |
||||
// getPhoneNumber接口返回的code
|
||||
private String code; |
||||
// 小程序的appid(一般是在程序中配置,不需要前端传参)
|
||||
private String appid; |
||||
// 小程序的secretKey(一般是在程序中配置,不需要前端传参)
|
||||
private String secretKey; |
||||
|
||||
} |
||||
@ -0,0 +1,16 @@ |
||||
package org.springblade.weixin.entity; |
||||
|
||||
import lombok.Data; |
||||
|
||||
@Data |
||||
public class WeChatPhoneInfo { |
||||
// 用户绑定的手机号(国外手机号会有区号)
|
||||
private String phoneNumber; |
||||
// 没有区号的手机号
|
||||
private String purePhoneNumber; |
||||
// 区号
|
||||
private String countryCode; |
||||
// 数据水印
|
||||
private String watermark; |
||||
|
||||
} |
||||
@ -0,0 +1,32 @@ |
||||
package org.springblade.weixin.entity; |
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName; |
||||
import lombok.Data; |
||||
import org.springblade.core.tenant.mp.TenantEntity; |
||||
|
||||
@Data |
||||
@TableName("eh_wx_user") |
||||
public class WeChatUser extends TenantEntity { |
||||
private static final long serialVersionUID = 1L; |
||||
|
||||
/** |
||||
* 微信用户唯一标识 |
||||
*/ |
||||
private String openId; |
||||
|
||||
/** |
||||
* 微信用户唯一标识 |
||||
*/ |
||||
private String username; |
||||
|
||||
/** |
||||
* 微信用户唯一标识 |
||||
*/ |
||||
private String phone; |
||||
|
||||
/** |
||||
* 微信用户唯一标识 |
||||
*/ |
||||
private String avatar; |
||||
|
||||
} |
||||
@ -0,0 +1,28 @@ |
||||
/* |
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are met: |
||||
* |
||||
* Redistributions of source code must retain the above copyright notice, |
||||
* this list of conditions and the following disclaimer. |
||||
* Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* Neither the name of the dreamlu.net developer nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* Author: Chill 庄骞 (smallchill@163.com) |
||||
*/ |
||||
package org.springblade.weixin.mapper; |
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
import org.springblade.weixin.entity.WeChatUser; |
||||
|
||||
/** |
||||
* Mapper 接口 |
||||
* @author BladeX |
||||
*/ |
||||
public interface WeChatUserMapper extends BaseMapper<WeChatUser> { |
||||
|
||||
} |
||||
@ -0,0 +1,5 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
<mapper namespace="org.springblade.weixin.mapper.WeChatUserMapper"> |
||||
|
||||
</mapper> |
||||
@ -0,0 +1,29 @@ |
||||
/* |
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are met: |
||||
* |
||||
* Redistributions of source code must retain the above copyright notice, |
||||
* this list of conditions and the following disclaimer. |
||||
* Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* Neither the name of the dreamlu.net developer nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* Author: Chill 庄骞 (smallchill@163.com) |
||||
*/ |
||||
package org.springblade.weixin.service; |
||||
|
||||
import org.springblade.core.mp.base.BaseService; |
||||
import org.springblade.weixin.entity.WeChatUser; |
||||
|
||||
/** |
||||
* 服务类 |
||||
* |
||||
* @author BladeX |
||||
*/ |
||||
public interface IWeChatUserService extends BaseService<WeChatUser> { |
||||
|
||||
} |
||||
@ -0,0 +1,36 @@ |
||||
/* |
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are met: |
||||
* |
||||
* Redistributions of source code must retain the above copyright notice, |
||||
* this list of conditions and the following disclaimer. |
||||
* Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* Neither the name of the dreamlu.net developer nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* Author: Chill 庄骞 (smallchill@163.com) |
||||
*/ |
||||
package org.springblade.weixin.service.impl; |
||||
|
||||
import org.springblade.core.mp.base.BaseServiceImpl; |
||||
import org.springblade.modules.system.entity.ApiScope; |
||||
import org.springblade.modules.system.mapper.ApiScopeMapper; |
||||
import org.springblade.modules.system.service.IApiScopeService; |
||||
import org.springblade.weixin.entity.WeChatUser; |
||||
import org.springblade.weixin.mapper.WeChatUserMapper; |
||||
import org.springblade.weixin.service.IWeChatUserService; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
/** |
||||
* 服务实现类 |
||||
* |
||||
* @author BladeX |
||||
*/ |
||||
@Service |
||||
public class WeChatUserServiceImpl extends BaseServiceImpl<WeChatUserMapper, WeChatUser> implements IWeChatUserService { |
||||
|
||||
} |
||||
@ -0,0 +1,242 @@ |
||||
package org.springblade.weixin.utils; |
||||
|
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.http.NameValuePair; |
||||
import org.apache.http.client.config.RequestConfig; |
||||
import org.apache.http.client.entity.UrlEncodedFormEntity; |
||||
import org.apache.http.client.methods.CloseableHttpResponse; |
||||
import org.apache.http.client.methods.HttpGet; |
||||
import org.apache.http.client.methods.HttpPost; |
||||
import org.apache.http.entity.ContentType; |
||||
import org.apache.http.entity.StringEntity; |
||||
import org.apache.http.impl.client.CloseableHttpClient; |
||||
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; |
||||
import org.apache.http.impl.client.HttpClients; |
||||
import org.apache.http.message.BasicHeader; |
||||
import org.apache.http.message.BasicNameValuePair; |
||||
import org.apache.http.util.EntityUtils; |
||||
import org.slf4j.MDC; |
||||
import org.springframework.http.HttpStatus; |
||||
import org.springframework.util.CollectionUtils; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
@Slf4j |
||||
public class HttpClientSslUtils { |
||||
/** |
||||
* 默认的字符编码格式 |
||||
*/ |
||||
private static final String DEFAULT_CHAR_SET = "UTF-8"; |
||||
/** |
||||
* 默认连接超时时间 (毫秒) |
||||
*/ |
||||
private static final Integer DEFAULT_CONNECTION_TIME_OUT = 2000; |
||||
/** |
||||
* 默认socket超时时间 (毫秒) |
||||
*/ |
||||
private static final Integer DEFAULT_SOCKET_TIME_OUT = 3000; |
||||
|
||||
/** |
||||
* socketTimeOut上限 |
||||
*/ |
||||
private static final Integer SOCKET_TIME_OUT_UPPER_LIMIT = 10000; |
||||
|
||||
/** |
||||
* socketTimeOut下限 |
||||
*/ |
||||
private static final Integer SOCKET_TIME_OUT_LOWER_LIMIT = 1000; |
||||
|
||||
private static CloseableHttpClient getHttpClient() { |
||||
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(DEFAULT_SOCKET_TIME_OUT) |
||||
.setConnectTimeout(DEFAULT_CONNECTION_TIME_OUT).build(); |
||||
return HttpClients.custom().setDefaultRequestConfig(requestConfig) |
||||
.setRetryHandler(new DefaultHttpRequestRetryHandler()).build(); |
||||
} |
||||
|
||||
private static CloseableHttpClient getHttpClient(Integer socketTimeOut) { |
||||
RequestConfig requestConfig = |
||||
RequestConfig.custom().setSocketTimeout(socketTimeOut).setConnectTimeout(DEFAULT_CONNECTION_TIME_OUT) |
||||
.build(); |
||||
return HttpClients.custom().setDefaultRequestConfig(requestConfig) |
||||
.setRetryHandler(new DefaultHttpRequestRetryHandler()).build(); |
||||
} |
||||
|
||||
public static String doPost(String url, String requestBody) throws Exception { |
||||
return doPost(url, requestBody, ContentType.APPLICATION_JSON); |
||||
} |
||||
|
||||
public static String doPost(String url, String requestBody, Integer socketTimeOut) throws Exception { |
||||
return doPost(url, requestBody, ContentType.APPLICATION_JSON, null, socketTimeOut); |
||||
} |
||||
|
||||
public static String doPost(String url, String requestBody, ContentType contentType) throws Exception { |
||||
return doPost(url, requestBody, contentType, null); |
||||
} |
||||
|
||||
public static String doPost(String url, String requestBody, List<BasicHeader> headers) throws Exception { |
||||
return doPost(url, requestBody, ContentType.APPLICATION_JSON, headers); |
||||
} |
||||
|
||||
public static String doPost(String url, String requestBody, ContentType contentType, List<BasicHeader> headers) |
||||
throws Exception { |
||||
return doPost(url, requestBody, contentType, headers, getHttpClient()); |
||||
} |
||||
|
||||
public static String doPost(String url, String requestBody, ContentType contentType, List<BasicHeader> headers, |
||||
Integer socketTimeOut) throws Exception { |
||||
if (socketTimeOut < SOCKET_TIME_OUT_LOWER_LIMIT || socketTimeOut > SOCKET_TIME_OUT_UPPER_LIMIT) { |
||||
log.error("socketTimeOut非法"); |
||||
throw new Exception(); |
||||
} |
||||
return doPost(url, requestBody, contentType, headers, getHttpClient(socketTimeOut)); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 通用Post远程服务请求 |
||||
* |
||||
* @param url 请求url地址 |
||||
* @param requestBody 请求体body |
||||
* @param contentType 内容类型 |
||||
* @param headers 请求头 |
||||
* @return String 业务自行解析 |
||||
* @throws Exception |
||||
*/ |
||||
public static String doPost(String url, String requestBody, ContentType contentType, List<BasicHeader> headers, |
||||
CloseableHttpClient client) throws Exception { |
||||
|
||||
// 构造http方法,设置请求和传输超时时间,重试3次
|
||||
CloseableHttpResponse response = null; |
||||
long startTime = System.currentTimeMillis(); |
||||
try { |
||||
HttpPost post = new HttpPost(url); |
||||
if (!CollectionUtils.isEmpty(headers)) { |
||||
for (BasicHeader header : headers) { |
||||
post.setHeader(header); |
||||
} |
||||
} |
||||
StringEntity entity = |
||||
new StringEntity(requestBody, ContentType.create(contentType.getMimeType(), DEFAULT_CHAR_SET)); |
||||
post.setEntity(entity); |
||||
response = client.execute(post); |
||||
if (response.getStatusLine().getStatusCode() != HttpStatus.OK.value()) { |
||||
log.error("业务请求返回失败:{}", EntityUtils.toString(response.getEntity())); |
||||
throw new Exception(); |
||||
} |
||||
String result = EntityUtils.toString(response.getEntity()); |
||||
return result; |
||||
} finally { |
||||
releaseResourceAndLog(url, requestBody, response, startTime); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 暂时用于智慧园区业务联调方式 |
||||
* |
||||
* @param url 业务请求url |
||||
* @param param 业务参数 |
||||
* @return |
||||
* @throws Exception |
||||
*/ |
||||
public static String doPostWithUrlEncoded(String url, |
||||
Map<String, String> param) throws Exception { |
||||
// 创建Httpclient对象
|
||||
CloseableHttpClient httpClient = getHttpClient(); |
||||
CloseableHttpResponse response = null; |
||||
long startTime = System.currentTimeMillis(); |
||||
try { |
||||
// 创建Http Post请求
|
||||
HttpPost httpPost = new HttpPost(url); |
||||
// 创建参数列表
|
||||
if (param != null) { |
||||
List<NameValuePair> paramList = new ArrayList<>(); |
||||
for (String key : param.keySet()) { |
||||
paramList.add(new BasicNameValuePair(key, param.get(key))); |
||||
} |
||||
// 模拟表单
|
||||
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList, DEFAULT_CHAR_SET); |
||||
httpPost.setEntity(entity); |
||||
} |
||||
// 执行http请求
|
||||
response = httpClient.execute(httpPost); |
||||
if (response.getStatusLine().getStatusCode() != HttpStatus.OK.value()) { |
||||
log.error("业务请求返回失败:{}", EntityUtils.toString(response.getEntity())); |
||||
throw new Exception(); |
||||
} |
||||
String resultString = EntityUtils.toString(response.getEntity(), DEFAULT_CHAR_SET); |
||||
return resultString; |
||||
} finally { |
||||
releaseResourceAndLog(url, param == null ? null : param.toString(), response, startTime); |
||||
} |
||||
} |
||||
|
||||
private static void releaseResourceAndLog(String url, String request, CloseableHttpResponse response, long startTime) { |
||||
if (null != response) { |
||||
try { |
||||
response.close(); |
||||
recordInterfaceLog(startTime, url, request); |
||||
} catch (IOException e) { |
||||
log.error(e.getMessage()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public static String doGet(String url) throws Exception { |
||||
return doGet(url, ContentType.DEFAULT_TEXT); |
||||
} |
||||
|
||||
public static String doGet(String url, ContentType contentType) throws Exception { |
||||
return doGet(url, contentType, null); |
||||
} |
||||
|
||||
public static String doGet(String url, List<BasicHeader> headers) throws Exception { |
||||
return doGet(url, ContentType.DEFAULT_TEXT, headers); |
||||
} |
||||
|
||||
/** |
||||
* 通用Get远程服务请求 |
||||
* |
||||
* @param url 请求参数 |
||||
* @param contentType 请求参数类型 |
||||
* @param headers 请求头可以填充 |
||||
* @return String 业务自行解析数据 |
||||
* @throws Exception |
||||
*/ |
||||
public static String doGet(String url, ContentType contentType, List<BasicHeader> headers) throws Exception { |
||||
CloseableHttpResponse response = null; |
||||
long startTime = System.currentTimeMillis(); |
||||
try { |
||||
CloseableHttpClient client = getHttpClient(); |
||||
HttpGet httpGet = new HttpGet(url); |
||||
if (!CollectionUtils.isEmpty(headers)) { |
||||
for (BasicHeader header : headers) { |
||||
httpGet.setHeader(header); |
||||
} |
||||
} |
||||
if (contentType != null) { |
||||
httpGet.setHeader("Content-Type", contentType.getMimeType()); |
||||
} |
||||
response = client.execute(httpGet); |
||||
if (response.getStatusLine().getStatusCode() != HttpStatus.OK.value()) { |
||||
log.error("业务请求返回失败:{}", EntityUtils.toString(response.getEntity())); |
||||
throw new Exception(); |
||||
} |
||||
String result = EntityUtils.toString(response.getEntity()); |
||||
return result; |
||||
} finally { |
||||
releaseResourceAndLog(url, null, response, startTime); |
||||
} |
||||
} |
||||
|
||||
private static void recordInterfaceLog(long startTime, String url, String request) { |
||||
long endTime = System.currentTimeMillis(); |
||||
long timeCost = endTime - startTime; |
||||
MDC.put("totalTime", String.valueOf(timeCost)); |
||||
MDC.put("url", url); |
||||
MDC.put("logType", "third-platform-service"); |
||||
log.info("HttpClientSslUtils 远程请求:{} 参数:{} 耗时:{}ms", url, request, timeCost); |
||||
} |
||||
} |
||||
@ -0,0 +1,112 @@ |
||||
package org.springblade.weixin.utils; |
||||
|
||||
|
||||
import com.alibaba.fastjson.TypeReference; |
||||
import com.fasterxml.jackson.annotation.JsonInclude; |
||||
import com.fasterxml.jackson.core.JsonProcessingException; |
||||
import com.fasterxml.jackson.databind.DeserializationFeature; |
||||
import com.fasterxml.jackson.databind.JsonNode; |
||||
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
import com.fasterxml.jackson.databind.SerializationFeature; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
import java.text.SimpleDateFormat; |
||||
|
||||
|
||||
|
||||
@Slf4j |
||||
public class JsonUtil { |
||||
/** |
||||
* 定义映射对象 |
||||
*/ |
||||
public static ObjectMapper objectMapper = new ObjectMapper(); |
||||
|
||||
/** |
||||
* 日期格式化 |
||||
*/ |
||||
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; |
||||
|
||||
static { |
||||
//对象的所有字段全部列入
|
||||
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); |
||||
//取消默认转换timestamps形式
|
||||
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); |
||||
//忽略空Bean转json的错误
|
||||
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); |
||||
//所有的日期格式都统一为以下的样式,即yyyy-MM-dd HH:mm:ss
|
||||
objectMapper.setDateFormat(new SimpleDateFormat(DATE_FORMAT)); |
||||
//忽略 在json字符串中存在,但是在java对象中不存在对应属性的情况。防止错误
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* string转JsonNode |
||||
* |
||||
* @param jsonString |
||||
* @return com.fasterxml.jackson.databind.JsonNode |
||||
*/ |
||||
public static JsonNode stringToJsonNode(String jsonString) throws JsonProcessingException { |
||||
|
||||
return objectMapper.readTree(jsonString); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 对象转json字符串 |
||||
* |
||||
* @param obj |
||||
* @param <T> |
||||
*/ |
||||
public static <T> String objToString(T obj) { |
||||
|
||||
if (obj == null) { |
||||
return null; |
||||
} |
||||
try { |
||||
return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj); |
||||
} catch (JsonProcessingException e) { |
||||
log.warn("Parse Object to String error : {}", e.getMessage()); |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 对象转格式化的字符串字符串 |
||||
* |
||||
* @param obj |
||||
* @param <T> |
||||
* @return |
||||
*/ |
||||
public static <T> String objToPrettyString(T obj) { |
||||
if (obj == null) { |
||||
return null; |
||||
} |
||||
try { |
||||
return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj); |
||||
} catch (JsonProcessingException e) { |
||||
log.warn("Parse Object to String error : {}", e.getMessage()); |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* json字符串转对象 |
||||
* |
||||
* @param jsonString |
||||
* @param cls |
||||
* @param <T> |
||||
*/ |
||||
public static <T> T stringToObj(String jsonString, Class<T> cls) { |
||||
if (StringUtils.isEmpty(jsonString) || cls == null) { |
||||
return null; |
||||
} |
||||
try { |
||||
return cls.equals(String.class) ? (T) jsonString : objectMapper.readValue(jsonString, cls); |
||||
} catch (JsonProcessingException e) { |
||||
log.warn("Parse String to Object error : {}", e.getMessage()); |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,121 @@ |
||||
package org.springblade.weixin.utils; |
||||
|
||||
import cn.hutool.json.JSONObject; |
||||
import cn.hutool.json.JSONUtil; |
||||
import com.alibaba.fastjson.TypeReference; |
||||
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
import org.apache.commons.codec.binary.Base64; |
||||
import org.springblade.core.http.util.HttpUtil; |
||||
import org.springframework.http.HttpEntity; |
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.http.ResponseEntity; |
||||
import org.springframework.web.client.RestTemplate; |
||||
|
||||
import javax.crypto.Cipher; |
||||
import javax.crypto.spec.IvParameterSpec; |
||||
import javax.crypto.spec.SecretKeySpec; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
public class WeChatUtil { |
||||
|
||||
/** |
||||
* 请求微信接口服务,获取小程序全局唯一后台接口调用凭据(access_token) |
||||
* https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/access-token/auth.getAccessToken.html
|
||||
* |
||||
* @param appid |
||||
* @param secretKey |
||||
* @return |
||||
*/ |
||||
public static JSONObject getAccessToken(String appid, String secretKey) { |
||||
String result = null; |
||||
try { |
||||
String baseUrl = "https://api.weixin.qq.com/cgi-bin/token"; |
||||
HashMap<String, Object> requestParam = new HashMap<>(); |
||||
// 小程序 appId
|
||||
requestParam.put("grant_type", "client_credential"); |
||||
// 小程序唯一凭证id
|
||||
requestParam.put("appid", appid); |
||||
// 小程序 appSecret(小程序的唯一凭证密钥)
|
||||
requestParam.put("secret", secretKey); |
||||
// 发送GET请求读取调用微信接口获取openid用户唯一标识
|
||||
result = HttpUtil.get(baseUrl, requestParam); |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
return JSONUtil.parseObj(result); |
||||
} |
||||
|
||||
/** |
||||
* 请求微信接口服务,用code换取用户手机号(每个code只能使用一次,code的有效期为5min) |
||||
* https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/phonenumber/phonenumber.getPhoneNumber.html
|
||||
* |
||||
* @param code |
||||
* @param accessToken |
||||
* @return |
||||
*/ |
||||
public static JSONObject getPhoneNumber(String code, String accessToken) { |
||||
String result = null; |
||||
try { |
||||
// 接口调用凭证:accessToken
|
||||
String baseUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken; |
||||
JSONObject jsonObject = new JSONObject(); |
||||
jsonObject.put("code", code); |
||||
String reqJsonStr = JsonUtil.objToString(jsonObject); |
||||
result = HttpClientSslUtils.doPost(baseUrl, reqJsonStr); |
||||
|
||||
return JSONUtil.parseObj(result); |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
return JSONUtil.parseObj(result); |
||||
} |
||||
|
||||
/** |
||||
* 请求微信接口服务,用code换取用户手机号(每个code只能使用一次,code的有效期为5min) |
||||
* https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/phonenumber/phonenumber.getPhoneNumber.html
|
||||
* |
||||
* @param code |
||||
* @return |
||||
*/ |
||||
public static JSONObject getCode2Session(String code, String appId, String appSecret) { |
||||
//调用微信小程序登录接口
|
||||
String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + |
||||
"&secret=" + appSecret + |
||||
"&js_code=" + code + |
||||
"&grant_type=authorization_code"; |
||||
RestTemplate restTemplate = new RestTemplate(); |
||||
|
||||
ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class); |
||||
Map<String, Object> map = com.alibaba.fastjson.JSONObject.parseObject(responseEntity.getBody(), new TypeReference<Map<String, Object>>() { |
||||
}); |
||||
return JSONUtil.parseObj(map); |
||||
} |
||||
|
||||
//解密
|
||||
private Map<String, Object> getDecryptedUserInfo(String encryptedData, String sessionKey, String iv) { |
||||
byte[] encryptedDataByte = Base64.decodeBase64(encryptedData); |
||||
byte[] sessionKeyByte = Base64.decodeBase64(sessionKey); |
||||
byte[] ivByte = Base64.decodeBase64(iv); |
||||
|
||||
try { |
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); |
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(sessionKeyByte, "AES"); |
||||
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivByte); |
||||
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); |
||||
byte[] decryptedByte = cipher.doFinal(encryptedDataByte); |
||||
String decryptedData = new String(decryptedByte, StandardCharsets.UTF_8); |
||||
|
||||
// 将解密后的数据转为Map
|
||||
ObjectMapper mapper = new ObjectMapper(); |
||||
return mapper.readValue(decryptedData, Map.class); |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
|
||||
return new HashMap<>(); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue