|
微信小程序越来越普遍,本章将分享给大家,实现微信小程序授权,推送微信通知消息。具体内容可参考微信小程序开发文档服务端(https://developers.weixin.qq.com/miniprogram/dev/api-backend/)。
==========================================================
封装工具类
==========================================================
package com.sf.vsolution.hb.sfce.util.wechat.appletService;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.sf.vsolution.hb.sfce.util.wechat.appletService.param.*; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.util.Base64Utils; import org.springframework.util.StringUtils;
import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.security.spec.AlgorithmParameterSpec; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit;
/** * @description: 微信小程序服务端开发 * @author: zhucj * @date: 2019-11-14 15:10 */ @Slf4j public class AppletServiceUtil {
@Value("${applet.appId}") private String appId; @Value("${applet.secret}") private String secret;
@Value("${wx.template.id}") private String templateId;
@Value("${wx.template.page}") private String templatePage;
@Autowired private StringRedisTemplate redis;
@ApiOperation(value = "微信小程序-获取用户openId或手机号信息") @ApiImplicitParams({ @ApiImplicitParam(name = "code",value = "微信授权code",required = true,dataType = "String"), @ApiImplicitParam(name = "encryptedData",value = "完整用户信息的加密数据(不传只获取openId)",required = false,dataType = "String"), @ApiImplicitParam(name = "iv",value = "加密算法的初始向量(如获取更多用户信息传)",required = false,dataType = "String") }) public R getUserInfo(WeChatAuthorizeDto dto){ if (StringUtils.isEmpty(dto.getCode())){ throw new AppletServiceException(AppletServiceConstants.PARAM_ERROR_CODE,AppletServiceConstants.CODE_NULL_ERROR); } //封装请求参数map Map map = new HashMap(); map.put("appid",appId); map.put("secret",secret); map.put("js_code",dto.getCode()); map.put("grant_type","authorization_code"); String resultString = HttpClientUtil.doGet(AppletServiceConstants.AUTH_CODE_API, map); AuthCodeResponse authCodeResponse = JSON.parseObject(resultString, AuthCodeResponse.class); //判断请求返回结果,errcode = 0 请求成功 if (!Objects.equals(authCodeResponse.getErrcode(),AppletServiceConstants.SUCCESS_CODE)){ log.error("小程序授权失败返回错误码:{},错误信息:{}",authCodeResponse.getErrcode(),authCodeResponse.getErrmsg()); throw new AppletServiceException(AppletServiceConstants.PARAM_ERROR_CODE,AppletServiceConstants.AUTHORIZATION_ERROR); }
HashMap<String,String> respMap = new HashMap<>(); respMap.put("openId",authCodeResponse.getOpenid()); //如果未传encryptedData和iv 即仅返回openId if (Objects.isNull(dto.getEncryptedData()) || Objects.isNull(dto.getIv())){ return R.ok(respMap); } // 解密 byte[] encrypData = Base64Utils.decodeFromString(dto.getEncryptedData()); byte[] ivData = Base64Utils.decodeFromString(dto.getIv()); byte[] sessionKey = Base64Utils.decodeFromString(authCodeResponse.getSession_key()); try{ AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivData); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec keySpec = new SecretKeySpec(sessionKey, "AES"); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); String string = new String(cipher.doFinal(encrypData), "UTF-8"); JSONObject object = JSONObject.parseObject(string); //获取手机号码 String phone = object.getString("phoneNumber"); respMap.put("phone", phone); }catch (Exception e){ e.getStackTrace(); log.error("微信小程序手机号码解密异常:{}",e.getMessage()); throw new AppletServiceException(AppletServiceConstants.EXCEPTION_ERROR_CODE,AppletServiceConstants.ENCTYP_EXECPTION); } return R.ok(resultString); }
@ApiOperation(value = "获取-小程序accessToken值") public String getToken() throws AppletServiceException { //先从缓存中判断是否存在 String redisString = redis.opsForValue().get(AppletServiceConstants.ACCESS_TOKEN); if (Objects.nonNull(redisString)){ return redisString; } String reqUrl = AppletServiceConstants.ACCESS_TOKEN_API; Map map = new HashMap(); map.put("grant_type","client_credential"); map.put("appid",appId); map.put("secret",secret); String result = HttpClientUtil.doGet(reqUrl, map); AuthAccessTokenResponse auth = JSON.parseObject(result, AuthAccessTokenResponse.class); //判断errcode ==0 即请求成功 if (Objects.equals(auth.getErrcode(),AppletServiceConstants.SUCCESS_CODE)){ log.info("获取的accessToken值:{}",auth.getAccess_token()); //将accessToken存入到redis 设置过期时间 redis.opsForValue().set(AppletServiceConstants.ACCESS_TOKEN,auth.getAccess_token(),AppletServiceConstants.EXPIRES_IN, TimeUnit.SECONDS); return auth.getAccess_token(); }else { log.error("小程序授权失败返回错误码:{},错误信息:{}",auth.getErrcode(),auth.getErrmsg()); throw new AppletServiceException(AppletServiceConstants.PARAM_ERROR_CODE,AppletServiceConstants.ACCESS_TOKRN_ERROR); }
}
@ApiOperation(value = "推送模板微信模板信息") @ApiImplicitParams({ @ApiImplicitParam(value = "openId",name = "用户openId",required = true,dataType = "String") }) public Boolean sendTemplateMsg(String openId) throws AppletServiceException { WxTemplate wxTemplate = build(openId); String str = JSON.toJSONString(wxTemplate); log.info("微信推送模版消息:{}",str); String result = HttpClientUtil.doPostJson(AppletServiceConstants.TEMPLATE_MESSAGE_URL+getToken(), str); CommonResponse commonResponse = JSON.parseObject(result, CommonResponse.class); if (Objects.equals(commonResponse.getErrcode(),AppletServiceConstants.SUCCESS_CODE)) { return true; } else { return false; } }
/** * 构建消息模板 * @return */ private WxTemplate build(String openId) { //以下是构建消息推送的model,具体情况需要根据模版做更改 WxTemplate template = new WxTemplate(); //模板Id template.setTemplate_id(templateId); //openId template.setTouser(openId); //此处小程序跳转链接,需要根据需要跳转拼接 template.setPage(templatePage); Map<String, TemplateData> param = new HashMap<>(); // 封装模板数据,依据小程序模板来设置(可设置成参数,传递过来) param.put("first", getTemplateData(xxxx)); param.put("keyword1", getTemplateData("xxxxxx")); param.put("keyword2", getTemplateData("xxxx")); param.put("keyword3", getTemplateData("xxxx")); param.put("keyword4", getTemplateData("xxxx")); param.put("keyword5", getTemplateData("xxxx")); param.put("remark", getTemplateData("xxxx")); template.setData(param); return template; }
/** * 设置 TemplateDate数据 * @param value * @return */ private static TemplateData getTemplateData(String value) { TemplateData templateData = new TemplateData(); templateData.setValue(value); templateData.setColor("#173177"); return templateData; }
}
================================================================== 封装请求参数,响应参数,常量类,枚举类和异常类 ==================================================================
package com.sf.vsolution.hb.sfce.util.wechat.appletService;
/** * @Author: zhucj * @Date: 2019/5/23 13:29 * @apiNote: 微信小程序服务端常量 */ public class AppletServiceConstants {
//TODO:服务端Api /** * 小程序登录API */ public static final String AUTH_CODE_API = "https://api.weixin.qq.com/sns/jscode2session"; /** * accessToken授权API */ public static final String ACCESS_TOKEN_API = "https://api.weixin.qq.com/cgi-bin/token";
/** * 模板消息URL */ public static String TEMPLATE_MESSAGE_URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=";
/** * accessToken缓存key */ public static final String ACCESS_TOKEN = "ACCESS_TOKEN";
/** * accessToken缓存过期时间 */ public static final Integer EXPIRES_IN = 7000;
//TODO:请求状态码 public static final Integer PARAM_ERROR_CODE = 400;
public static final Integer EXCEPTION_ERROR_CODE = 500;
public static final Integer SUCCESS_CODE = 0;
//TODO: 异常信息状态
public static final String CODE_NULL_ERROR = "微信授权码code不为空";
public static final String AUTHORIZATION_ERROR = "未获取到openId,授权失败";
public static final String ENCTYP_EXECPTION = "微信小程序手机号码解密异常";
public static final String ACCESS_TOKRN_ERROR = "小程序授权获取accessToken失败";
}
----------------------------------
package com.sf.vsolution.hb.sfce.util.wechat.appletService;
/** * @author :zhucj * @date :Created in 2019/6/17 17:46 * @description: 小程序服务异常类 */ public class AppletServiceException extends RuntimeException { private static final long serialVersionUID = -5317007026578376164L;
/** * 错误码 */ private Integer errorCode; /** * 错误描述 */ private String errorMsg;
/** * @param errorCode * @param errorMsg */ public AppletServiceException(Integer errorCode, String errorMsg) { super(errorMsg); this.errorCode = errorCode; this.errorMsg = errorMsg; }
public Integer getErrorCode() { return errorCode; }
public String getErrorMsg() { return errorMsg; } }
----------------------------------
package com.sf.vsolution.hb.sfce.util.wechat.appletService;
import com.alibaba.fastjson.JSON; import lombok.extern.slf4j.Slf4j; 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.client.utils.URIBuilder; 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.HttpClients; import org.apache.http.util.EntityUtils;
import java.io.IOException; import java.net.URI; import java.util.Map;
/** * @description: get/post请求工具类 * @author: zhucj * @date: 2019-04-26 10:10 */ @Slf4j public class HttpClientUtil {
/** * 发送get请求 * @param url * @param param * @return */ public static String doGet(String url, Map<String, String> param) { log.info("GET请求地址:{},请求参数内容:{}",url, JSON.toJSON(param)); // 创建Httpclient对象 CloseableHttpClient httpclient = HttpClients.createDefault();
String resultString = ""; CloseableHttpResponse response = null; try { // 创建uri URIBuilder builder = new URIBuilder(url); if (param != null) { for (String key : param.keySet()) { builder.addParameter(key, param.get(key)); } } URI uri = builder.build();
// 创建http GET请求 HttpGet httpGet = new HttpGet(uri);
// 执行请求 response = httpclient.execute(httpGet); // 判断返回状态是否为200 if (response.getStatusLine().getStatusCode() == 200) { resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); } } catch (Exception e) { e.printStackTrace(); log.error("网络出现异常:{}",e.getMessage()); } finally { try { if (response != null) { response.close(); } httpclient.close(); } catch (IOException e) { e.printStackTrace(); log.error("IO出现异常:{}",e.getMessage()); } } log.info("GET响应参数:{}",resultString); return resultString; }
/** * 发post请求 传入xml/json字符串 * @param url * @param json * @return */ public static String doPostJson(String url, String json) { log.info("POST请求地址:{},请求参数内容:{}",url,json); // 创建Httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { // 创建Http Post请求 HttpPost httpPost = new HttpPost(url); // 创建请求内容 StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); httpPost.setEntity(entity); // 执行http请求 response = httpClient.execute(httpPost); resultString = EntityUtils.toString(response.getEntity(), "utf-8"); } catch (Exception e) { e.printStackTrace(); log.error("网络出现异常:{}",e.getMessage()); } finally { try { response.close(); } catch (IOException e) { e.printStackTrace(); log.error("IO出现异常:{}",e.getMessage()); } } log.info("POST响应参数:{}",resultString); return resultString; }
}
-------------------------------------
package com.sf.vsolution.hb.sfce.util.wechat.appletService;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.ToString;
import java.io.Serializable;
/** * 返回类型 * @author choleece * @date 2018/9/27 */ @ApiModel @ToString public class R<T> implements Serializable {
private static final long serialVersionUID = -6287952131441663819L;
/** * 编码 */ @ApiModelProperty(value = "响应码", example = "200") private int code = 200;
/** * 成功标志 */ @ApiModelProperty(value = "成功标志", example = "true") private Boolean success;
/** * 返回消息 */ @ApiModelProperty(value = "返回消息说明", example = "操作成功") private String msg="操作成功";
/** * 返回数据 */ @ApiModelProperty(value = "返回数据") private T data;
/** * 创建实例 * @return */ public static R instance() { return new R(); }
public int getCode() { return code; }
public R setCode(int code) { this.code = code; return this; }
public Boolean getSuccess() { return success; }
public R setSuccess(Boolean success) { this.success = success; return this; }
public String getMsg() { return msg; }
public R setMsg(String msg) { this.msg = msg; return this; }
public T getData() { return data; } public R setData(T data) { this.data = data; return this; }
public static R ok() { return R.instance().setSuccess(true); }
public static R ok(Object data) { return ok().setData(data); }
public static R ok(Object data, String msg) { return ok(data).setMsg(msg); }
public static R error() { return R.instance().setSuccess(false); }
public static R error(String msg) { return error().setMsg(msg); }
/** * 无参 */ public R() { }
public R(int code, String msg) { this.code = code; this.msg = msg; }
public R(int code, T data){ this.code = code; this.data = data; }
/** * 有全参 * @param code * @param msg * @param data * @param success */ public R(int code, String msg, T data, Boolean success) { this.code = code; this.msg = msg; this.data = data; this.success = success; }
/** * 有参 * @param code * @param msg * @param data */ public R(int code, String msg, T data) { this.code = code; this.msg = msg; this.data = data; }
}
---------------------------------------
package com.sf.vsolution.hb.sfce.util.wechat.appletService.param;
import lombok.*;
/** * @description: 小程序端返回共用参数 * @author: zhucj * @date: 2019-11-14 16:41 */ @Data @Builder @NoArgsConstructor @AllArgsConstructor @ToString public class CommonResponse {
/** * 错误码 */ private Integer errcode;
/** * 错误信息 */ private String errmsg; }
-----------------------------
package com.sf.vsolution.hb.sfce.util.wechat.appletService.param;
import lombok.*;
/** * @description: 小程序获取AccessToken返回参数 * @author: zhucj * @date: 2019-11-14 16:43 */ @Data @Builder @NoArgsConstructor @AllArgsConstructor @ToString public class AuthAccessTokenResponse extends CommonResponse {
/** * 获取到的凭证 */ private String access_token;
/** * 凭证有效时间,单位:秒。目前是7200秒之内的值。 */ private Integer expires_in; }
---------------------------
package com.sf.vsolution.hb.sfce.util.wechat.appletService.param;
import lombok.*;
/** * @description: 小程序获取openId返回参数 * @author: zhucj * @date: 2019-11-14 16:13 */ @Data @Builder @NoArgsConstructor @AllArgsConstructor @ToString public class AuthCodeResponse extends CommonResponse {
/** * 用户唯一标识 */ private String openid;
/** * 会话密钥 */ private String session_key;
/** * 用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见 UnionID 机制说明。 */ private String unionid;
}
------------------------------
package com.sf.vsolution.hb.sfce.util.wechat.appletService.param;
import lombok.*;
/** * Template * @author zhucj */ @Data @NoArgsConstructor @AllArgsConstructor @ToString @Builder public class TemplateData {
/** * 模板显示值 */ private String value; /** * 模板显示颜色 */ private String color; }
---------------------------
package com.sf.vsolution.hb.sfce.util.wechat.appletService.param;
import lombok.*;
/** * @description: 微信授权参数 * @author: zhucj * @date: 2019-09-26 15:01 */ @Data @NoArgsConstructor @AllArgsConstructor @ToString @Builder public class WeChatAuthorizeDto {
/** * 微信授权code */ private String code;
/** * 包括敏感数据在内的完整用户信息的加密数据 */ private String encryptedData;
/** * 加密算法的初始向量,详见 用户数据的签名验证和加解密 */ private String iv; }
-----------------------------
package com.sf.vsolution.hb.sfce.util.wechat.appletService.param; import lombok.*;
import java.util.Map;
@Data @NoArgsConstructor @AllArgsConstructor @ToString @Builder public class WxTemplate {
/** * 微信通知模板ID */ private String template_id; /** * 微信openId */ private String touser;
/** 小程序首页地址 */ private String page;
/**支付返回的 prepay_id,部分模板必传*/ private String form_id;
private Map<String, String> miniprogram;
private Map<String, TemplateData> data; }
================================================================= yml配置文件 =================================================================
#微信支付参数 wx: #微信小程序appId appId: ****** #小程序secret secret: ******* template: #模板Id id: ****** #通知跳转小程序页面 page: ******
================================================================= 项目maven支持 ===============================================
<!-- swagger API --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.6.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.6.0</version> </dependency>
<!--htttpclient依赖--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.5</version> </dependency>
<!--fastjson依赖--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency>
<!-- redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
<!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.2</version> <scope>provided</scope> </dependency>
来源:https://www.cnblogs.com/zhucj-java/p/11864317.html |