2023年8月18日18:05:23

master
litao 2 years ago
parent d764e17d1e
commit d15741ce8f
  1. 24
      pom.xml
  2. 4
      src/main/java/org/springblade/common/enums/DictEnum.java
  3. 112
      src/main/java/org/springblade/weixin/controller/AppEbLoginController.java
  4. 45
      src/main/java/org/springblade/weixin/controller/WeChatUserController.java
  5. 14
      src/main/java/org/springblade/weixin/entity/WeChatPhone.java
  6. 16
      src/main/java/org/springblade/weixin/entity/WeChatPhoneInfo.java
  7. 32
      src/main/java/org/springblade/weixin/entity/WeChatUser.java
  8. 28
      src/main/java/org/springblade/weixin/mapper/WeChatUserMapper.java
  9. 5
      src/main/java/org/springblade/weixin/mapper/WeChatUserMapper.xml
  10. 29
      src/main/java/org/springblade/weixin/service/IWeChatUserService.java
  11. 36
      src/main/java/org/springblade/weixin/service/impl/WeChatUserServiceImpl.java
  12. 242
      src/main/java/org/springblade/weixin/utils/HttpClientSslUtils.java
  13. 112
      src/main/java/org/springblade/weixin/utils/JsonUtil.java
  14. 121
      src/main/java/org/springblade/weixin/utils/WeChatUtil.java

@ -172,6 +172,30 @@
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>3.8.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.4.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
</dependencies>
<build>

@ -88,6 +88,10 @@ public enum DictEnum {
* 用户平台
*/
USER_TYPE("user_type"),
/**
* 微信小程序
*/
WECHAT_APP("weChat_app"),
;
final String name;

@ -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…
Cancel
Save