feat: 微信公众号扫码登录完成

This commit is contained in:
landaiqing
2024-06-27 23:46:31 +08:00
parent 67362ad6b9
commit 77bba1a754
87 changed files with 2764 additions and 534 deletions

View File

@@ -40,6 +40,12 @@
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<exclusions>
<exclusion>
<artifactId>error_prone_annotations</artifactId>
<groupId>com.google.errorprone</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
@@ -67,11 +73,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.18</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
@@ -82,11 +83,6 @@
<artifactId>dom4j</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.12.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
@@ -101,6 +97,28 @@
<artifactId>caffeine</artifactId>
<version>2.9.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.11.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.4.0</version>
<exclusions>
<exclusion>
<artifactId>gson</artifactId>
<groupId>com.google.code.gson</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.schisandra</groupId>
<artifactId>schisandra-cloud-storage-auth-api</artifactId>

View File

@@ -0,0 +1,59 @@
package com.schisandra.wechat.aes;
@SuppressWarnings("serial")
public class AesException extends Exception {
public final static int OK = 0;
public final static int ValidateSignatureError = -40001;
public final static int ParseXmlError = -40002;
public final static int ComputeSignatureError = -40003;
public final static int IllegalAesKey = -40004;
public final static int ValidateAppidError = -40005;
public final static int EncryptAESError = -40006;
public final static int DecryptAESError = -40007;
public final static int IllegalBuffer = -40008;
//public final static int EncodeBase64Error = -40009;
//public final static int DecodeBase64Error = -40010;
//public final static int GenReturnXmlError = -40011;
private int code;
private static String getMessage(int code) {
switch (code) {
case ValidateSignatureError:
return "签名验证错误";
case ParseXmlError:
return "xml解析失败";
case ComputeSignatureError:
return "sha加密生成签名失败";
case IllegalAesKey:
return "SymmetricKey非法";
case ValidateAppidError:
return "appid校验失败";
case EncryptAESError:
return "aes加密失败";
case DecryptAESError:
return "aes解密失败";
case IllegalBuffer:
return "解密后得到的buffer非法";
// case EncodeBase64Error:
// return "base64加密错误";
// case DecodeBase64Error:
// return "base64解密错误";
// case GenReturnXmlError:
// return "xml生成失败";
default:
return null; // cannot be
}
}
public int getCode() {
return code;
}
public AesException(int code) {
super(getMessage(code));
this.code = code;
}
}

View File

@@ -0,0 +1,58 @@
package com.schisandra.wechat.common.constant;
public enum ApiResponseCode {
/**
* 成功
*/
SUCCESS(0, "success"),
/**
* 参数错误
*/
PARAMETER_INVALID(100, "parameter_invalid"),
/**
* 业务错误
*/
BUSINESS_ERROR(200, "业务错误"),
/**
* 登录错误
*/
LOGIN_ERROR(201, "登录失败"),
/**
* 账号或密码错误
*/
ACCOUNT_PASSWORD_ERROR(202, "账号或密码错误"),
/**
* 账号错误
*/
ACCOUNT_ERROR(203, "账号错误"),
/**
* 服务异常
*/
SERVICE_ERROR(500, "service_error");
private Integer code;
private String message;
private ApiResponseCode(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
}

View File

@@ -0,0 +1,29 @@
package com.schisandra.wechat.common.constant;
public class CommonConstant {
public static final String TOKEN_CACHE_KEY = "TOKEN:";
public static final String USER_CACHE_KEY = "USER:";
public static final String UTF8 = "UTF-8";
public static final String GBK = "GBK";
/**
* 角色菜单权限
*/
public static final String ROLE_MENU_PERMISSIONS = "ROLE_MENU_PERMISSIONS:";
/**
* 角色资源权限
*/
public static final String ROLE_RESOURCE_PERMISSIONS = "ROLE_RESOURCE_PERMISSIONS:";
/**
* 管理员
*/
public static final Long ROLE_ADMIN = 1L;
/**
* 普通会员
*/
public static final Long ROLE_MEMBER = 2L;
public CommonConstant() {
}
}

View File

@@ -1,7 +1,14 @@
package com.schisandra.wechat.entity;
package com.schisandra.wechat.common.entity;
import com.schisandra.wechat.common.enums.ResultCodeEnum;
import lombok.Data;
/**
* @description: 返回结果泛型类
* @author: schisandra
* @date: 2024/3/22 13:09
*/
@Data
public class Result<T> {
@@ -13,7 +20,7 @@ public class Result<T> {
private T data;
public static Result ok(){
public static Result ok() {
Result result = new Result();
result.setSuccess(true);
result.setCode(ResultCodeEnum.SUCCESS.getCode());
@@ -21,7 +28,7 @@ public class Result<T> {
return result;
}
public static <T> Result ok(T data){
public static <T> Result ok(T data) {
Result result = new Result();
result.setSuccess(true);
result.setCode(ResultCodeEnum.SUCCESS.getCode());
@@ -30,7 +37,7 @@ public class Result<T> {
return result;
}
public static Result fail(){
public static Result fail() {
Result result = new Result();
result.setSuccess(false);
result.setCode(ResultCodeEnum.FAIL.getCode());
@@ -38,7 +45,7 @@ public class Result<T> {
return result;
}
public static <T> Result fail(T data){
public static <T> Result fail(T data) {
Result result = new Result();
result.setSuccess(false);
result.setCode(ResultCodeEnum.FAIL.getCode());

View File

@@ -0,0 +1,34 @@
package com.schisandra.wechat.common.enums;
import lombok.Getter;
/**
* @description: 返回结果状态枚举
* @author: schisandra
* @date: 2024/3/22 13:10
*/
@Getter
public enum ResultCodeEnum {
SUCCESS(200, "成功"),
FAIL(500, "失败");
public int code;
public String desc;
ResultCodeEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
public static ResultCodeEnum getByCode(int codeVal) {
for (ResultCodeEnum resultCodeEnum : ResultCodeEnum.values()) {
if (resultCodeEnum.code == codeVal) {
return resultCodeEnum;
}
}
return null;
}
}

View File

@@ -0,0 +1,43 @@
package com.schisandra.wechat.common.exception;
import com.schisandra.wechat.common.constant.ApiResponseCode;
/**
* 处理异常
*
*/
public class BaseException extends RuntimeException {
private static final long serialVersionUID = 2612992235262400823L;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
private Integer code = null;
public BaseException(String message) {
super(message);
this.code = ApiResponseCode.SERVICE_ERROR.getCode();
}
public BaseException(String message, Throwable t) {
super(message, t);
this.code = ApiResponseCode.SERVICE_ERROR.getCode();
}
public BaseException(Integer code, String message) {
super(message);
this.code = code;
}
public BaseException(Integer code, String message, Throwable t) {
super(message, t);
this.code = code;
}
}

View File

@@ -0,0 +1,46 @@
package com.schisandra.wechat.common.exception;
import com.schisandra.wechat.common.constant.ApiResponseCode;
import java.util.HashMap;
import java.util.Map;
/**
* 参数异常
*/
public class ParameterException extends BaseException {
private static final long serialVersionUID = 1L;
private Map<String, String> fieldErrors;
public ParameterException(String message) {
super(ApiResponseCode.PARAMETER_INVALID.getCode(), message);
}
public ParameterException(int code, String message) {
super(code, message);
}
public ParameterException(Map<String, String> fieldErrors) {
super(ApiResponseCode.PARAMETER_INVALID.getCode(), ApiResponseCode.PARAMETER_INVALID.getMessage());
this.fieldErrors = fieldErrors;
}
public ParameterException(String key, String value) {
super(ApiResponseCode.PARAMETER_INVALID.getCode(), ApiResponseCode.PARAMETER_INVALID.getMessage());
Map<String, String> fieldErrors = new HashMap<>();
fieldErrors.put(key, value);
this.fieldErrors = fieldErrors;
}
public Map<String, String> getFieldErrors() {
return fieldErrors;
}
public ParameterException(String message, Throwable t) {
super(message, t);
}
}

View File

@@ -1,4 +1,5 @@
package com.schisandra.wechat.redis;
package com.schisandra.wechat.common.redis;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@@ -16,8 +17,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Redis的config处理
*
* @author: ChickenWing
* @date: 2023/10/28
* @author: schisandra
*/
@Configuration
public class RedisConfig {

View File

@@ -1,4 +1,5 @@
package com.schisandra.wechat.redis;
package com.schisandra.wechat.common.redis;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
@@ -13,8 +14,8 @@ import java.util.stream.Stream;
/**
* RedisUtil工具类
*
* @author: ChickenWing
* @date: 2023/10/28
* @author: schisandra
* @date: 2024/2/19
*/
@Component
@Slf4j

View File

@@ -0,0 +1,24 @@
package com.schisandra.wechat.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
@Configuration
@Slf4j
public class WebClientDefaultConfig {
@Bean
public WebClient webClient() {
log.info("WebClientDefaultConfig init start ...");
HttpClient httpClient = HttpClient.create();
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
log.info("WebClientDefaultConfig init end");
return webClient;
}
}

View File

@@ -0,0 +1,23 @@
package com.schisandra.wechat.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(
prefix = "wx"
)
@Data
public class WxConfig {
/**
* 公众号配置
*/
private WxDetailConfig mp;
/**
* 小程序配置
*/
private WxDetailConfig miniApp;
}

View File

@@ -0,0 +1,33 @@
package com.schisandra.wechat.config;
import lombok.Data;
@Data
public class WxDetailConfig {
/**
* appid
*/
private String appId;
/**
* 秘钥
*/
private String secret;
/**
* 二维码过期时间
* 默认15分钟
*/
private Integer codeExpire = 900;
/**
* token
*/
private String token;
/**
* 消息加密密钥
*/
private String encodingAESKey;
}

View File

@@ -0,0 +1,78 @@
package com.schisandra.wechat.config;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* xxl-job config
*
* @author xuxueli 2017-04-28
*/
@Configuration
public class XxlJobConfig {
private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.appname}")
private String appname;
@Value("${xxl.job.executor.address}")
private String address;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private int port;
@Value("${xxl.job.executor.logpath}")
private String logPath;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appname);
xxlJobSpringExecutor.setAddress(address);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}
/**
* 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP
*
* 1、引入依赖
* <dependency>
* <groupId>org.springframework.cloud</groupId>
* <artifactId>spring-cloud-commons</artifactId>
* <version>${version}</version>
* </dependency>
*
* 2、配置文件或者容器启动变量
* spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
*
* 3、获取IP
* String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
*/
}

View File

@@ -0,0 +1,15 @@
package com.schisandra.wechat.constant;
public class WXApiConstant {
/**
* 获取微信token
*/
public static final String ACCESS_TOKEN_API = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
/**
* 生成临时公众号二维码
* 文档地址https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
*/
public static final String MP_QRCODE_CREATE ="https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=%s";
}

View File

@@ -1,84 +0,0 @@
package com.schisandra.wechat.controller;
import com.schisandra.wechat.handler.WeChatMsgFactory;
import com.schisandra.wechat.handler.WeChatMsgHandler;
import com.schisandra.wechat.utils.MessageUtil;
import com.schisandra.wechat.utils.SHA1;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Map;
import java.util.Objects;
/**
* @Classname CallBackController
* @BelongsProject: schisandra-cloud-storage
* @BelongsPackage: com.schisandra.wechat.controller
* @Author: schisandra
* @CreateTime: 2024-02-21 16:41
* @Description: TODO
* @Version: 1.0
*/
@RestController
@Slf4j
public class CallBackController {
@Resource
private WeChatMsgFactory weChatMsgFactory;
@Value("${wechat.token}")
private String token;
/**
* @description: 回调消息校验
* @param: [signature, timestamp, nonce, echostr]
* @return: java.lang.String
* @author schisandra
* @date: 2024/2/21 17:16
*/
@GetMapping("/callback")
public String callback(@RequestParam("signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("echostr") String echostr) {
log.info("get验签请求参数:signature: {}timestamp: {} nonce: {}echostr: {}",
signature, timestamp, nonce, echostr);
String shaStr = SHA1.getSHA1(token, timestamp, nonce, "");
if (signature.equals(shaStr)) {
return echostr;
}
return "unKnown";
}
@PostMapping(value = "callback", produces = "application/xml;charset=UTF-8")
public String callback(
@RequestBody String requestBody,
@RequestParam("signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam(value = "msg_signature", required = false) String msgSignature) {
log.info("接收到微信消息requestBody{}", requestBody);
Map<String, String> messageMap = MessageUtil.parseXml(requestBody);
String msgType = messageMap.get("MsgType");
String event = messageMap.get("Event") == null ? "" : messageMap.get("Event");
log.info("msgType:{},event:{}", msgType, event);
StringBuilder sb = new StringBuilder();
sb.append(msgType);
if (!StringUtils.isEmpty(event)) {
sb.append(".");
sb.append(event);
}
String msgTypeKey = sb.toString();
WeChatMsgHandler weChatMsgHandler = weChatMsgFactory.getHandlerByMsgType(msgTypeKey);
if (Objects.isNull(weChatMsgHandler)) {
return "unknown";
}
String replyContent = weChatMsgHandler.dealMsg(messageMap);
log.info("replyContent:{}", replyContent);
return replyContent;
}
}

View File

@@ -0,0 +1,75 @@
package com.schisandra.wechat.controller;
import com.schisandra.wechat.common.entity.Result;
import com.schisandra.wechat.config.WxConfig;
import com.schisandra.wechat.dto.AccessTokenResult;
import com.schisandra.wechat.dto.MpQrCodeCreateRequest;
import com.schisandra.wechat.dto.MpQrCodeCreateResult;
import com.schisandra.wechat.service.WXService;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.UUID;
/**
* @Classname WxController
* @BelongsProject: schisandra-cloud-storage
* @BelongsPackage: com.schisandra.wechat.controller
* @Author: landaiqing
* @CreateTime: 2024-06-26 16:29
* @Description: TODO
* @Version: 1.0
*/
@RestController
@RequestMapping("/wx/")
@Slf4j
public class WxController {
@Resource
private WXService wxService;
@Resource
WxConfig wxConfig;
/**
* @description: 获取微信access_token
* @param: []
* @return: void
* @author: landaiqing
* @date: 2024/6/27 上午11:34
*/
@GetMapping("getAccessToken")
public Result getAccessToken() {
AccessTokenResult accessTokenResult = wxService.getMpAccessToken(wxConfig.getMp().getAppId(), wxConfig.getMp().getSecret());
return Result.ok(accessTokenResult);
}
/**
* @description: 生成微信二维码
* @param: []
* @return: com.schisandra.wechat.common.entity.Result
* @author: landaiqing
* @date: 2024/6/27 上午11:34
*/
@GetMapping("generateQRCode")
public Result generateQRCode(@RequestParam("clientId") String clientId) {
if (Strings.isBlank(clientId)){
return Result.fail("客户端获取失败,请刷新页面!");
}
String mpAccessTokenByCache = wxService.getMpAccessTokenByCache(wxConfig.getMp().getAppId());
if (Strings.isBlank(mpAccessTokenByCache)) {
return Result.fail("获取微信access_token失败");
}
MpQrCodeCreateRequest request = new MpQrCodeCreateRequest();
request.setExpireSeconds(wxConfig.getMp().getCodeExpire());
request.setActionName("QR_STR_SCENE");
request.setActionInfo(request.new ActionInfo());
request.getActionInfo().setScene(request.new scene());
request.getActionInfo().getScene().setSceneStr("ScanReg_" + wxConfig.getMp().getAppId() + "_" + clientId);
MpQrCodeCreateResult result = wxService.createMpQrcodeCreate(mpAccessTokenByCache, request);
return Result.ok(result);
}
}

View File

@@ -0,0 +1,45 @@
package com.schisandra.wechat.controller;
import com.schisandra.wechat.aes.AesException;
import com.schisandra.wechat.dto.MpCommonRequest;
import com.schisandra.wechat.service.WxMpEventService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@RestController
@RequiredArgsConstructor
@Slf4j
public class WxEventController {
@Resource
WxMpEventService wxMpEventService;
/**
* 接收微信事件推送
* 参考文档:
* https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html
* *
*
* @param mpCommonRequest
* @return
* @throws HttpRequestMethodNotSupportedException
*/
@RequestMapping(value = "/callback",
method = {RequestMethod.GET, RequestMethod.POST})
public String receiveMpEvent(@Validated @ModelAttribute MpCommonRequest mpCommonRequest, HttpServletRequest httpServletRequest) throws IOException, AesException {
return wxMpEventService.receiveMpEvent(mpCommonRequest, httpServletRequest);
}
}

View File

@@ -0,0 +1,32 @@
package com.schisandra.wechat.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 获取微信token返回接口实体信息
* 参考文档https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/access-token/auth.getAccessToken.html
*/
@Data
public class AccessTokenResult {
/**
* 获取到的凭证
*/
@JsonProperty(value = "access_token", required = true)
private String accessToken;
/**
* 凭证有效时间单位秒。目前是7200秒之内的值。
*/
@JsonProperty(value = "expires_in", required = true)
private String expiresIn;
/**
* 错误码
*/
@JsonProperty(value = "errcode", required = true)
private String errCode;
/**
* 错误信息
*/
@JsonProperty(value = "errmsg", required = true)
private String errMsg;
}

View File

@@ -0,0 +1,82 @@
package com.schisandra.wechat.dto;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 接收微信事件推送
* 具体参数意思参考文档地址:
* https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class MpBaseEventRequest implements Serializable {
private static final long serialVersionUID = 9194059022155191588L;
/**
* 开发者微信号
*/
@JsonProperty(value = "ToUserName")
private String toUserName;
/**
* 发送方帐号一个OpenID
*/
@JsonProperty(value = "FromUserName")
private String fromUserName;
/**
* 消息创建时间 (整型)
*/
@JsonProperty(value = "CreateTime")
private Integer createTime;
/**
* 消息类型event
*/
@JsonProperty(value = "MsgType")
private String msgType;
// /**
// * 事件类型
// * subscribe(订阅/)
// * unsubscribe(取消订阅)
// * SCAN(用户已关注扫描)
// * LOCATION 上报地理位置
// * CLICK 菜单点击事件
// */
// @JsonProperty(value = "Event")
// private String event;
// /**
// * 事件 KEY 值是一个32位无符号整数即创建二维码时的二维码scene_id
// */
// @JsonProperty(value = "EventKey")
// private String eventKey;
//
// /**
// * 二维码的ticket可用来换取二维码图片
// */
// @JsonProperty(value = "Ticket")
// private String ticket;
//
// /**
// * 地理位置纬度
// */
// @JsonProperty(value = "Latitude")
// private Double latitude;
//
// /**
// * 地理位置经度
// */
// @JsonProperty(value = "Longitude")
// private Double longitude;
//
// /**
// * 地理位置精度
// */
// @JsonProperty(value = "Precision")
// private Double precision;
}

View File

@@ -0,0 +1,27 @@
package com.schisandra.wechat.dto;
import lombok.Data;
@Data
public class MpCommonRequest {
/**
* 微信加密签名signature结合了开发者填写的 token 参数和请求中的 timestamp 参数、nonce参数。
*/
private String signature;
/**
* 时间戳
*/
private String timestamp;
/**
* 随机数
*/
private String nonce;
/**
* 随机字符串
*/
private String echostr;
}

View File

@@ -0,0 +1,46 @@
package com.schisandra.wechat.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
@Data
public class MpQrCodeCreateRequest {
/**
* 该二维码有效时间,以秒为单位。 最大不超过2592000即30天此字段如果不填则默认有效期为60秒。
*/
@JsonProperty(value = "expire_seconds")
private Integer expireSeconds;
/**
* 二维码类型QR_SCENE为临时的整型参数值QR_STR_SCENE为临时的字符串参数值QR_LIMIT_SCENE为永久的整型参数值QR_LIMIT_STR_SCENE为永久的字符串参数值
*/
@JsonProperty(value = "action_name")
private String actionName;
/**
* 二维码详细信息
*/
@JsonProperty(value = "action_info")
private ActionInfo actionInfo;
@Data
public class ActionInfo {
private scene scene;
}
@Data
public class scene {
/**
* 场景值ID临时二维码时为32位非0整型永久二维码时最大值为100000目前参数只支持1--100000
*/
@JsonProperty(value = "scene_id")
private Integer sceneId;
/**
* 场景值ID字符串形式的ID字符串类型长度限制为1到64
*/
@JsonProperty(value = "scene_str")
private String sceneStr;
}
}

View File

@@ -0,0 +1,41 @@
package com.schisandra.wechat.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.Serializable;
@Data
public class MpQrCodeCreateResult implements Serializable {
/**
* 获取的二维码ticket凭借此 ticket 可以在有效时间内换取二维码。
*/
private String ticket;
/**
* 该二维码有效时间,以秒为单位。 最大不超过2592000即30天
*/
@JsonProperty(value = "expire_seconds")
private Integer expireSeconds;
/**
* 二维码图片解析后的地址,开发者可根据该地址自行生成需要的二维码图片
*/
private String url;
/**
* 错误码
*/
@JsonProperty(value = "errcode")
private String errCode;
/**
* 错误信息
*/
@JsonProperty(value = "errmsg")
private String errMsg;
/**
* 二维码地址
*/
private String qrCodeUrl;
}

View File

@@ -0,0 +1,37 @@
package com.schisandra.wechat.dto;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 接收微信事件推送
* 具体参数意思参考文档地址:
* https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class MpSubscribeEventRequest extends MpBaseEventRequest {
/**
* 事件类型
* subscribe(订阅/)
* unsubscribe(取消订阅)
* SCAN(用户已关注扫描)
* LOCATION 上报地理位置
* CLICK 菜单点击事件
*/
@JsonProperty(value = "Event")
private String event;
/**
* 事件 KEY 值qrscene_为前缀后面为二维码的参数值
*/
@JsonProperty(value = "EventKey")
private String eventKey;
/**
* 二维码的ticket可用来换取二维码图片
*/
@JsonProperty(value = "Ticket")
private String ticket;
}

View File

@@ -0,0 +1,18 @@
package com.schisandra.wechat.dto;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 接收微信事件推送
* 具体参数意思参考文档地址:
* https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class MpTextEventRequest extends MpBaseEventRequest {
private static final long serialVersionUID = 4209945653336582616L;
@JsonProperty(value = "Content")
private String content;
}

View File

@@ -1,29 +0,0 @@
package com.schisandra.wechat.entity;
import lombok.Getter;
@Getter
public enum ResultCodeEnum {
SUCCESS(200,"成功"),
FAIL(500,"失败");
public int code;
public String desc;
ResultCodeEnum(int code,String desc){
this.code = code;
this.desc = desc;
}
public static ResultCodeEnum getByCode(int codeVal){
for(ResultCodeEnum resultCodeEnum : ResultCodeEnum.values()){
if(resultCodeEnum.code == codeVal){
return resultCodeEnum;
}
}
return null;
}
}

View File

@@ -1,57 +0,0 @@
package com.schisandra.wechat.handler;
import com.schisandra.wechat.redis.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@Component
@Slf4j
public class ReceiveTextMsgHandler implements WeChatMsgHandler {
private static final String KEY_WORD = "验证码";
private static final String LOGIN_KEY_WORD = "登录";
private static final String LOGIN_PREFIX = "loginCode";
@Resource
private RedisUtil redisUtil;
@Override
public WeChatMsgTypeEnum getMsgType() {
return WeChatMsgTypeEnum.TEXT_MSG;
}
@Override
public String dealMsg(Map<String, String> messageMap) {
String content = messageMap.get("Content");
String fromUserName = messageMap.get("FromUserName");
String toUserName = messageMap.get("ToUserName");
// 验证码
if (KEY_WORD.equals(content)) {
Random random = new Random();
int num = random.nextInt(9000) + 1000;
String numKey = redisUtil.buildKey(LOGIN_PREFIX, String.valueOf(num));
redisUtil.setNx(numKey, fromUserName, 1L, TimeUnit.MINUTES);
String numContent = "您当前的验证码是:【" + num + " 1分钟内有效";
String replyContent = "<xml>\n" +
" <ToUserName><![CDATA[" + fromUserName + "]]></ToUserName>\n" +
" <FromUserName><![CDATA[" + toUserName + "]]></FromUserName>\n" +
" <CreateTime>12345678</CreateTime>\n" +
" <MsgType><![CDATA[text]]></MsgType>\n" +
" <Content><![CDATA[" + numContent + "]]></Content>\n" +
"</xml>";
return replyContent;
}
return null;
}
}

View File

@@ -1,33 +0,0 @@
package com.schisandra.wechat.handler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
@Slf4j
public class SubscribeMsgHandler implements WeChatMsgHandler {
@Override
public WeChatMsgTypeEnum getMsgType() {
return WeChatMsgTypeEnum.SUBSCRIBE;
}
@Override
public String dealMsg(Map<String, String> messageMap) {
log.info("触发用户关注事件!");
String fromUserName = messageMap.get("FromUserName");
String toUserName = messageMap.get("ToUserName");
String subscribeContent = "感谢您的关注!";
String content = "<xml>\n" +
" <ToUserName><![CDATA[" + fromUserName + "]]></ToUserName>\n" +
" <FromUserName><![CDATA[" + toUserName + "]]></FromUserName>\n" +
" <CreateTime>12345678</CreateTime>\n" +
" <MsgType><![CDATA[text]]></MsgType>\n" +
" <Content><![CDATA[" + subscribeContent + "]]></Content>\n" +
"</xml>";
return content;
}
}

View File

@@ -1,31 +0,0 @@
package com.schisandra.wechat.handler;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class WeChatMsgFactory implements InitializingBean {
@Resource
private List<WeChatMsgHandler> weChatMsgHandlerList;
private Map<WeChatMsgTypeEnum, WeChatMsgHandler> handlerMap = new HashMap<>();
public WeChatMsgHandler getHandlerByMsgType(String msgType) {
WeChatMsgTypeEnum msgTypeEnum = WeChatMsgTypeEnum.getByMsgType(msgType);
return handlerMap.get(msgTypeEnum);
}
@Override
public void afterPropertiesSet() throws Exception {
for (WeChatMsgHandler weChatMsgHandler : weChatMsgHandlerList) {
handlerMap.put(weChatMsgHandler.getMsgType(), weChatMsgHandler);
}
}
}

View File

@@ -1,11 +0,0 @@
package com.schisandra.wechat.handler;
import java.util.Map;
public interface WeChatMsgHandler {
WeChatMsgTypeEnum getMsgType();
String dealMsg(Map<String, String> messageMap);
}

View File

@@ -1,26 +0,0 @@
package com.schisandra.wechat.handler;
public enum WeChatMsgTypeEnum {
SUBSCRIBE("event.subscribe", "用户关注事件"),
TEXT_MSG("text", "接收用户文本消息");
private String msgType;
private String desc;
WeChatMsgTypeEnum(String msgType, String desc) {
this.msgType = msgType;
this.desc = desc;
}
public static WeChatMsgTypeEnum getByMsgType(String msgType) {
for (WeChatMsgTypeEnum weChatMsgTypeEnum : WeChatMsgTypeEnum.values()) {
if (weChatMsgTypeEnum.msgType.equals(msgType)) {
return weChatMsgTypeEnum;
}
}
return null;
}
}

View File

@@ -0,0 +1,38 @@
package com.schisandra.wechat.job;
import com.schisandra.wechat.config.WxConfig;
import com.schisandra.wechat.service.WXService;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @Classname GetAccessTokenJob
* @BelongsProject: schisandra-cloud-storage
* @BelongsPackage: com.schisandra.auth.application.job
* @Author: landaiqing
* @CreateTime: 2024-06-26 22:57
* @Description: TODO
* @Version: 1.0
*/
@Component
@Slf4j
public class GenerateAccessTokenJob {
@Resource
private WXService wxService;
@Resource
private WxConfig wxConfig;
@XxlJob("generateAccessTokenJobHandler")
public void GenerateAccessTokenJobHandler() throws Exception {
XxlJobHelper.log("generateAccessTokenJobHandler.starter");
try {
wxService.setMpAccessTokenCache(wxConfig.getMp().getAppId(), wxConfig.getMp().getSecret());
} catch (Exception e) {
XxlJobHelper.log("generateAccessTokenJobHandler.error");
}
}
}

View File

@@ -0,0 +1,29 @@
package com.schisandra.wechat.rpc;
import com.schisandra.auth.api.SchisandraAuthFeignService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @Classname AuthUserRpc
* @BelongsProject: schisandra-cloud-storage
* @BelongsPackage: com.schisandra.wechat.rpc
* @Author: landaiqing
* @CreateTime: 2024-06-27 17:00
* @Description: TODO
* @Version: 1.0
*/
@Component
public class AuthUserRpc {
@Resource
private SchisandraAuthFeignService schisandraAuthFeignService;
public Boolean wechatRegister(String appId, String openId, String clientId) {
Boolean result = schisandraAuthFeignService.wechatRegister(appId, openId, clientId);
if (result) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,42 @@
package com.schisandra.wechat.service;
import com.schisandra.wechat.dto.AccessTokenResult;
import com.schisandra.wechat.dto.MpQrCodeCreateRequest;
import com.schisandra.wechat.dto.MpQrCodeCreateResult;
public interface WXService {
/**
* 从缓存中获取公众号token
* @param appid
* @return
*/
String getMpAccessTokenByCache(String appid);
/**
* 设置公众号token的redis缓存
*
* @param appid
* @param secret
*/
void setMpAccessTokenCache(String appid, String secret);
/**
* 获取微信公众号token
* 参考接口文档https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/access-token/auth.getAccessToken.html
*
* @param appid
* @param secret
* @return
*/
AccessTokenResult getMpAccessToken(String appid, String secret);
/**
* 生成临时公众号二维码
* 文档地址https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
* @param token
* @param request
* @return
*/
MpQrCodeCreateResult createMpQrcodeCreate(String token, MpQrCodeCreateRequest request);
}

View File

@@ -0,0 +1,34 @@
package com.schisandra.wechat.service;
import com.schisandra.wechat.aes.AesException;
import com.schisandra.wechat.dto.MpCommonRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public interface WxMpEventService {
/**
* 接收公众号推送的事件
*
* @param mpCommonRequest
* @param httpServletRequest
* @return
* @throws IOException
*/
String receiveMpEvent(MpCommonRequest mpCommonRequest, HttpServletRequest httpServletRequest) throws IOException, AesException;
/**
* 用SHA1算法生成安全签名
*
* @param signature 签名
* @param token 票据
* @param timestamp 时间戳
* @param nonce 随机字符串
* @param encrypt 密文
* @return 安全签名
* @throws AesException
*/
void checkSignature(String signature, String token, String timestamp, String nonce, String encrypt) throws AesException;
}

View File

@@ -0,0 +1,100 @@
package com.schisandra.wechat.service.impl;
import com.schisandra.wechat.common.redis.RedisUtil;
import com.schisandra.wechat.constant.WXApiConstant;
import com.schisandra.wechat.dto.AccessTokenResult;
import com.schisandra.wechat.dto.MpQrCodeCreateRequest;
import com.schisandra.wechat.dto.MpQrCodeCreateResult;
import com.schisandra.wechat.service.WXService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
@RequiredArgsConstructor
public class WXServiceImpl implements WXService {
@Resource
WebClient webClient;
@Resource
RedisTemplate<String, Object> redisTemplate;
@Resource
RedisUtil redisUtil;
final String WX_ACCESS_TOKEN_REDIS_KEY = "wx_access_token";
//接口重试次数
int retry = 3;
/**
* 从缓存中获取公众号token
* @param appid
* @return
*/
@Override
public String getMpAccessTokenByCache(String appid) {
String key = redisUtil.buildKey(WX_ACCESS_TOKEN_REDIS_KEY, appid);
return redisUtil.get(key);
}
/**
* 设置公众号token的redis缓存
*
* @param appid
* @param secret
*/
@Override
public void setMpAccessTokenCache(String appid, String secret) {
AccessTokenResult accessTokenResult = getMpAccessToken(appid, secret);
String key = redisUtil.buildKey(WX_ACCESS_TOKEN_REDIS_KEY, appid);
redisUtil.setNx(key,accessTokenResult.getAccessToken(), Long.valueOf(accessTokenResult.getExpiresIn()), TimeUnit.SECONDS);
}
/**
* 获取微信公众号token
* 参考接口文档https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/access-token/auth.getAccessToken.html
*
* @param appid
* @param secret
* @return
*/
@Override
public AccessTokenResult getMpAccessToken(String appid, String secret) {
String url = String.format(WXApiConstant.ACCESS_TOKEN_API, appid, secret);
return webClient.get().uri(url).retrieve().bodyToMono(AccessTokenResult.class)
.retry(retry)
.block();
}
/**
* 生成临时公众号二维码
* 文档地址https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
*
* @param token
* @param request
* @return
*/
@Override
public MpQrCodeCreateResult createMpQrcodeCreate(String token, MpQrCodeCreateRequest request) {
String url = String.format(WXApiConstant.MP_QRCODE_CREATE, token);
MpQrCodeCreateResult result = webClient.post().uri(url).contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(request))
.retrieve().bodyToMono(MpQrCodeCreateResult.class).retry(retry)
.block();
if (result == null || Strings.isBlank(result.getTicket())) {
return result;
}
result.setQrCodeUrl("https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + result.getTicket());
return result;
}
}

View File

@@ -0,0 +1,159 @@
package com.schisandra.wechat.service.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.schisandra.wechat.aes.AesException;
import com.schisandra.wechat.common.exception.ParameterException;
import com.schisandra.wechat.config.WxConfig;
import com.schisandra.wechat.dto.MpBaseEventRequest;
import com.schisandra.wechat.dto.MpCommonRequest;
import com.schisandra.wechat.dto.MpSubscribeEventRequest;
import com.schisandra.wechat.dto.MpTextEventRequest;
import com.schisandra.wechat.rpc.AuthUserRpc;
import com.schisandra.wechat.service.WxMpEventService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Objects;
@Service
@RequiredArgsConstructor
@Slf4j
public class WxMpEventServiceImpl implements WxMpEventService {
@Resource
ApplicationContext applicationContext;
@Resource
WxConfig wxConfig;
@Resource
private AuthUserRpc authUserRpc;
/**
* 接收公众号推送的事件
*
* @param mpCommonRequest
* @param httpServletRequest
* @return
* @throws IOException
*/
@Override
public String receiveMpEvent(MpCommonRequest mpCommonRequest, HttpServletRequest httpServletRequest) throws IOException, AesException {
checkSignature(mpCommonRequest.getSignature(),
wxConfig.getMp().getToken(),
mpCommonRequest.getTimestamp(),
mpCommonRequest.getNonce(),
null);
if (Strings.isBlank(httpServletRequest.getHeader("content-type"))) {
log.info("content-type is null");
return mpCommonRequest.getEchostr();
}
log.info("content-type:" + httpServletRequest.getHeader("content-type"));
log.info(mpCommonRequest.toString());
XmlMapper xmlMapper = new XmlMapper();
Object object = xmlMapper.readValue(httpServletRequest.getInputStream(), Object.class);
ObjectMapper mapper = new ObjectMapper();
log.info(object.toString());
MpBaseEventRequest mpBaseEventRequest = mapper.convertValue(object, MpBaseEventRequest.class);
if ("text".equals(mpBaseEventRequest.getMsgType())) {
MpTextEventRequest mpTextEventRequest = mapper.convertValue(object, MpTextEventRequest.class);
log.info(mpTextEventRequest.toString());
log.info("推送消息MpTextEventRequest...");
// applicationContext.publishEvent(mpTextEventRequest);
}
if ("event".equals(mpBaseEventRequest.getMsgType())) {
MpSubscribeEventRequest mpSubscribeEventRequest = mapper.convertValue(object, MpSubscribeEventRequest.class);
log.info(mpSubscribeEventRequest.toString());
log.info("推送消息MpSubscribeEventRequest...");
// applicationContext.publishEvent(mpSubscribeEventRequest);
if ("subscribe".equals(mpSubscribeEventRequest.getEvent())
&& Strings.isNotBlank(mpSubscribeEventRequest.getEventKey())) {
String[] keys = mpSubscribeEventRequest.getEventKey().split("_");
if ("qrscene".equals(keys[0]) && "ScanReg".equals(keys[1])) {
log.info("AppId{}ClientId{}", keys[2], keys[3]);
authUserRpc.wechatRegister(keys[2], mpSubscribeEventRequest.getToUserName(), keys[3]);
return null;
}
}
if ("SCAN".equals(mpSubscribeEventRequest.getEvent()) &&
Strings.isNotBlank(mpSubscribeEventRequest.getEventKey())) {
String[] keys = mpSubscribeEventRequest.getEventKey().split("_");
if ("ScanReg".equals(keys[0])) {
log.info("AppId{}ClientId{}", keys[1], keys[2]);
authUserRpc.wechatRegister(keys[1], mpSubscribeEventRequest.getFromUserName(), keys[2]);
return null;
}
}
}
log.info("微信公众号推送事件处理完成");
return mpCommonRequest.getEchostr();
}
/**
* 用SHA1算法生成安全签名
*
* @param signature 签名
* @param token 票据
* @param timestamp 时间戳
* @param nonce 随机字符串
* @param encrypt 密文
* @return 安全签名
* @throws AesException
*/
@Override
public void checkSignature(String signature, String token, String timestamp, String nonce, String encrypt) throws AesException {
try {
if (Strings.isBlank(signature) || Strings.isBlank(token)
|| Strings.isBlank(timestamp) || Strings.isBlank(nonce)) {
throw new ParameterException("签名参数非法");
}
String[] array = null;
if (Strings.isBlank(encrypt)) {
array = new String[]{token, timestamp, nonce};
} else {
array = new String[]{token, timestamp, nonce, encrypt};
}
StringBuffer sb = new StringBuffer();
// 字符串排序
Arrays.sort(array);
for (int i = 0; i < array.length; i++) {
sb.append(array[i]);
}
String str = sb.toString();
// SHA1签名生成
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(str.getBytes());
byte[] digest = md.digest();
StringBuffer hexstr = new StringBuffer();
String shaHex = "";
for (int i = 0; i < digest.length; i++) {
shaHex = Integer.toHexString(digest[i] & 0xFF);
if (shaHex.length() < 2) {
hexstr.append(0);
}
hexstr.append(shaHex);
}
log.info("wx_signature:{},my_signature{}", signature, hexstr.toString());
if (!Objects.equals(signature, hexstr.toString())) {
throw new AesException(AesException.ValidateSignatureError);
}
} catch (Exception e) {
e.printStackTrace();
throw new AesException(AesException.ComputeSignatureError);
}
}
}

View File

@@ -1,47 +0,0 @@
package com.schisandra.wechat.utils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MessageUtil {
/**
* 解析微信发来的请求XML.
*
* @param msg 消息
* @return map
*/
public static Map<String, String> parseXml(final String msg) {
// 将解析结果存储在HashMap中
Map<String, String> map = new HashMap<String, String>();
// 从request中取得输入流
try (InputStream inputStream = new ByteArrayInputStream(msg.getBytes(StandardCharsets.UTF_8.name()))) {
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List<Element> elementList = root.elements();
// 遍历所有子节点
for (Element e : elementList) {
map.put(e.getName(), e.getText());
}
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
}

View File

@@ -1,56 +0,0 @@
package com.schisandra.wechat.utils;
import lombok.extern.slf4j.Slf4j;
import java.security.MessageDigest;
import java.util.Arrays;
/**
* sha1生成签名工具
*
* @author: schisandra
* @date: 2023/11/5
*/
@Slf4j
public class SHA1 {
/**
* 用SHA1算法生成安全签名
*
* @param token 票据
* @param timestamp 时间戳
* @param nonce 随机字符串
* @param encrypt 密文
* @return 安全签名
*/
public static String getSHA1(String token, String timestamp, String nonce, String encrypt) {
try {
String[] array = new String[]{token, timestamp, nonce, encrypt};
StringBuffer sb = new StringBuffer();
// 字符串排序
Arrays.sort(array);
for (int i = 0; i < 4; i++) {
sb.append(array[i]);
}
String str = sb.toString();
// SHA1签名生成
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(str.getBytes());
byte[] digest = md.digest();
StringBuffer hexStr = new StringBuffer();
String shaHex = "";
for (int i = 0; i < digest.length; i++) {
shaHex = Integer.toHexString(digest[i] & 0xFF);
if (shaHex.length() < 2) {
hexStr.append(0);
}
hexStr.append(shaHex);
}
return hexStr.toString();
} catch (Exception e) {
log.error("sha加密生成签名失败:", e);
return null;
}
}
}

View File

@@ -23,5 +23,26 @@ spring:
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
wechat:
token: LDQ20020618xxx
# 微信公众号
wx:
mp:
token: LDQ20020618xxx
appid: wx55251c2f83b9fc25
secret: d511800cd53d248afe1260bb8aeed230
codeExpire: 3600
encodingAESKey:
# xxl-job配置
xxl:
job:
admin:
addresses: http://127.0.0.1:8081/xxl-job-admin
accessToken: default_token
executor:
appname: schisandra-cloud-storage-wechat
address:
ip: 127.0.0.1
port: 9998
logpath: /data/applogs/xxl-job/jobhandler
logretentiondays: 30