From 8883d654f52171657b62aaec85027f29ef0a4dd0 Mon Sep 17 00:00:00 2001 From: puzhibing <393733352@qq.com> Date: 星期日, 26 二月 2023 22:30:39 +0800 Subject: [PATCH] 新增加用户端接口 --- user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Category.java | 18 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/Html.java | 38 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/ByteGroup.java | 26 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/PKCS7Encoder.java | 67 + user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IHtmlService.java | 11 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpResult.java | 31 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCryptTest.java | 147 ++ user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/RedisUtil.java | 250 +++- user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/HtmlMapper.java | 11 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/KeywordEnum.java | 17 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplatetitles.java | 26 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AES.java | 72 + user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WxPKCS7Encoder.java | 63 + user/guns-admin/src/main/java/com/supersavedriving/user/core/common/aspect/ServiceLogAspect.java | 72 + user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/HtmlServiceImpl.java | 16 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SubscribeMessageUtil.java | 264 ++++ user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/HtmlMapper.xml | 12 user/guns-admin/pom.xml | 61 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AesException.java | 59 + user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/AppUserMapper.xml | 25 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IAppUserService.java | 27 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/XMLParse.java | 71 + user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/MessageTemplate.java | 36 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/ResultUtil.java | 59 user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/HtmlController.java | 50 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/AppUser.java | 103 + user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/AppUserServiceImpl.java | 156 ++ user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCrypt.java | 288 +++++ user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/SignInToRegister.java | 23 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplateKeywords.java | 26 /dev/null | 266 ---- user/guns-admin/src/main/java/com/supersavedriving/user/core/common/annotion/ServiceLog.java | 23 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXCore.java | 52 user/guns-admin/src/main/java/com/supersavedriving/user/config/RedisConfig.java | 46 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpClientUtil.java | 269 ++++ user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WeChatUtil.java | 415 +++++++ user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/ResponseWarpper.java | 49 user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/AppUserMapper.java | 7 user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/AppUserController.java | 66 + user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SHA1.java | 61 + user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Code2Session.java | 33 41 files changed, 3,001 insertions(+), 411 deletions(-) diff --git a/user/guns-admin/pom.xml b/user/guns-admin/pom.xml index d8673f4..98fd737 100644 --- a/user/guns-admin/pom.xml +++ b/user/guns-admin/pom.xml @@ -63,6 +63,12 @@ </dependency> <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-core</artifactId> + <version>2.11.3</version> + </dependency> + + <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> @@ -104,16 +110,11 @@ <artifactId>ehcache</artifactId> </dependency> - <!--需要分布式session的话需要放开注释--> - <!--<dependency>--> - <!--<groupId>org.springframework.session</groupId>--> - <!--<artifactId>spring-session-data-redis</artifactId>--> - <!--</dependency>--> - <!--<dependency>--> - <!--<groupId>org.springframework.boot</groupId>--> - <!--<artifactId>spring-boot-starter-data-redis</artifactId>--> - <!--</dependency>--> - + <dependency> + <groupId>redis.clients</groupId> + <artifactId>jedis</artifactId> + <version>2.9.0</version> + </dependency> <dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha</artifactId> @@ -181,44 +182,12 @@ <artifactId>geodesy</artifactId> <version>1.1.3</version> </dependency> - - <!--引入本地工行支付jar start--> <dependency> - <groupId>com.icbc</groupId> - <artifactId>icbc</artifactId> - <version>v2</version> - <scope>system</scope> - <systemPath>${project.basedir}/lib/icbc-api-sdk-cop.jar</systemPath> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>1.18.20</version> + <scope>provided</scope> </dependency> - <dependency> - <groupId>com.icbc.api</groupId> - <artifactId>icbc</artifactId> - <version>v2</version> - <scope>system</scope> - <systemPath>${project.basedir}/lib/icbc-api-sdk-cop-io.jar</systemPath> - </dependency> - <dependency> - <groupId>cn.com.infosec</groupId> - <artifactId>icbc</artifactId> - <version>v2</version> - <scope>system</scope> - <systemPath>${project.basedir}/lib/icbc-ca.jar</systemPath> - </dependency> - <dependency> - <groupId>cn.com.infosecCrypto</groupId> - <artifactId>icbc</artifactId> - <version>v2</version> - <scope>system</scope> - <systemPath>${project.basedir}/lib/InfosecCrypto_Java1_02_JDK14+.jar</systemPath> - </dependency> - <dependency> - <groupId>proguard</groupId> - <artifactId>icbc</artifactId> - <version>v2</version> - <scope>system</scope> - <systemPath>${project.basedir}/lib/proguard.jar</systemPath> - </dependency> - <!--引入本地工行支付jar end--> <dependency> <groupId>commons-codec</groupId> diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/config/RedisConfig.java b/user/guns-admin/src/main/java/com/supersavedriving/user/config/RedisConfig.java new file mode 100644 index 0000000..949a267 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/config/RedisConfig.java @@ -0,0 +1,46 @@ +package com.supersavedriving.user.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; + +@Configuration +@PropertySource("classpath:redis.properties") +public class RedisConfig { + @Value("${spring.redis.host}") + private String host; + + @Value("${spring.redis.port}") + private int port; + + @Value("${spring.redis.timeout}") + private int timeout; + + @Value("${spring.redis.jedis.pool.max-idle}") + private int maxIdle; + + @Value("${spring.redis.jedis.pool.max-wait}") + private long maxWaitMillis; + + @Value("${spring.redis.password}") + private String password; + + @Value("${spring.redis.block-when-exhausted}") + private boolean blockWhenExhausted; + + @Bean + public JedisPool redisPoolFactory() throws Exception{ + JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); + jedisPoolConfig.setMaxIdle(maxIdle); + jedisPoolConfig.setMaxWaitMillis(maxWaitMillis); + // 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true + jedisPoolConfig.setBlockWhenExhausted(blockWhenExhausted); + // 是否启用pool的jmx管理功能, 默认true + jedisPoolConfig.setJmxEnabled(true); + JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password); + return jedisPool; + } +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/core/common/annotion/ServiceLog.java b/user/guns-admin/src/main/java/com/supersavedriving/user/core/common/annotion/ServiceLog.java new file mode 100644 index 0000000..668ab0f --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/core/common/annotion/ServiceLog.java @@ -0,0 +1,23 @@ +package com.supersavedriving.user.core.common.annotion; + +import java.lang.annotation.*; + +/** + * 接口日志注解 + */ +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +public @interface ServiceLog { + /** + * 接口名称 + * @return + */ + String name() default ""; + + /** + * 接口地址 + * @return + */ + String url() default ""; +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/core/common/aspect/ServiceLogAspect.java b/user/guns-admin/src/main/java/com/supersavedriving/user/core/common/aspect/ServiceLogAspect.java new file mode 100644 index 0000000..4850d1c --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/core/common/aspect/ServiceLogAspect.java @@ -0,0 +1,72 @@ +package com.supersavedriving.user.core.common.aspect; + +import com.alibaba.fastjson.JSONObject; +import com.supersavedriving.user.core.common.annotion.ServiceLog; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; + +@Aspect +@Component +public class ServiceLogAspect { + + Logger logger = LoggerFactory.getLogger("ServiceLog"); + + /** + * //切面点为标记了@ServiceLog注解的方法 + */ + @Pointcut("@annotation(com.supersavedriving.user.core.common.annotion.ServiceLog)") + public void serviceLog(){ + } + + + //环绕通知 + @Around("serviceLog()") + @SuppressWarnings("unchecked") + public Object around(ProceedingJoinPoint joinPoint) throws Throwable { + long starTime = System.currentTimeMillis(); + //通过反射获取被调用方法的Class + Class type = joinPoint.getSignature().getDeclaringType(); + //获取类名 + String typeName = type.getSimpleName(); + //方法名 + String methodName = joinPoint.getSignature().getName(); + //获取参数列表 + Object[] args = joinPoint.getArgs(); + //参数Class的数组 + Class[] clazz = new Class[args.length]; + for (int i = 0; i < args.length; i++) { + clazz[i] = args[i].getClass(); + } + //通过反射获取调用的方法method + Method method = type.getMethod(methodName, clazz); + ServiceLog serviceLog = method.getAnnotation(ServiceLog.class); + //获取方法的参数 + Parameter[] parameters = method.getParameters(); + JSONObject jsonObject = new JSONObject(); + for (int i = 0; i < parameters.length; i++) { + Parameter parameter = parameters[i]; + String name = parameter.getName(); + jsonObject.put(name, args[i]); + } + //执行结果 + //执行目标方法,获取执行结果 + Object res = joinPoint.proceed(); + logger.debug("调用{}.{}方法成功\n" + + "接口名称:{}\n" + + "接口地址:{}\n" + + "耗时:{}ms\n" + + "参数为:{}\n" + + "返回结果:{}", typeName, methodName, serviceLog.name(), serviceLog.url(), + (System.currentTimeMillis() - starTime), jsonObject.toJSONString(), JSONObject.toJSONString(res)); + //返回执行结果 + return res; + } +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/AppUserController.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/AppUserController.java new file mode 100644 index 0000000..7781fe9 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/AppUserController.java @@ -0,0 +1,66 @@ +package com.supersavedriving.user.modular.api; + +import com.supersavedriving.user.core.common.annotion.ServiceLog; +import com.supersavedriving.user.core.util.ToolUtil; +import com.supersavedriving.user.modular.system.service.IAppUserService; +import com.supersavedriving.user.modular.system.util.ResultUtil; +import com.supersavedriving.user.modular.system.warpper.ResponseWarpper; +import com.supersavedriving.user.modular.system.warpper.SignInToRegister; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * 用户控制器 + */ +@RestController +@RequestMapping("") +public class AppUserController { + + @Autowired + private IAppUserService appUserService; + + + @ResponseBody + @PostMapping("/base/appUser/appUserLogin") + @ServiceLog(name = "微信登录", url = "/base/appUser/appUserLogin") + @ApiOperation(value = "微信登录", tags = {"用户端-首页"}, notes = "") + @ApiImplicitParams({ + @ApiImplicitParam(value = "微信jscode", name = "jscode", required = true, dataType = "string"), + }) + public ResponseWarpper appUserLogin(String jscode){ + if(ToolUtil.isEmpty(jscode)){ + return ResponseWarpper.success(ResultUtil.paranErr("jscode")); + } + try { + ResultUtil resultUtil = appUserService.appUserLogin(jscode); + return ResponseWarpper.success(resultUtil); + }catch (Exception e){ + e.printStackTrace(); + return new ResponseWarpper(500, e.getMessage()); + } + } + + + + @ResponseBody + @PostMapping("/base/appUser/signInToRegister") + @ServiceLog(name = "微信手机授权登录", url = "/base/appUser/signInToRegister") + @ApiOperation(value = "微信手机授权登录", tags = {"用户端-首页"}, notes = "") + @ApiImplicitParams({ + }) + public ResponseWarpper signInToRegister(SignInToRegister signInToRegister){ + try { + ResultUtil resultUtil = appUserService.signInToRegister(signInToRegister); + return ResponseWarpper.success(resultUtil); + }catch (Exception e){ + e.printStackTrace(); + return new ResponseWarpper(500, e.getMessage()); + } + } +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/HtmlController.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/HtmlController.java new file mode 100644 index 0000000..4e10ba7 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/api/HtmlController.java @@ -0,0 +1,50 @@ +package com.supersavedriving.user.modular.api; + +import com.baomidou.mybatisplus.mapper.EntityWrapper; +import com.supersavedriving.user.core.common.annotion.ServiceLog; +import com.supersavedriving.user.modular.system.model.Html; +import com.supersavedriving.user.modular.system.service.IHtmlService; +import com.supersavedriving.user.modular.system.util.ResultUtil; +import com.supersavedriving.user.modular.system.warpper.ResponseWarpper; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * 协议控制器 + */ +@RestController +@RequestMapping("") +public class HtmlController { + + @Autowired + private IHtmlService htmlService; + + + + + @ResponseBody + @PostMapping("/base/html/queryHtml") + @ServiceLog(name = "获取各种协议和说明", url = "/base/html/queryHtml") + @ApiOperation(value = "获取各种协议和说明", tags = {"用户端-首页"}, notes = "") + @ApiImplicitParams({ + @ApiImplicitParam(value = "类型(1=用户协议,2=隐私政策,3=法律条款,4=代驾服务协议,5=个人信息处理规则,6=积分说明,7=佣金规则说明,8=行程录音说明,9=预估价格说明,10=加盟基本要求,11=加盟流程,12=起步价说明,13=注销协议,14=关于我们,15=司机消单说明)", name = "type", required = true, dataType = "int"), + }) + public ResponseWarpper<String> queryHtml(Integer type){ + if(null == type){ + return ResponseWarpper.success(ResultUtil.paranErr("type")); + } + try { + Html html = htmlService.selectOne(new EntityWrapper<Html>().eq("type", type)); + return ResponseWarpper.success(null == html ? "" : html.getHtml()); + }catch (Exception e){ + e.printStackTrace(); + return new ResponseWarpper(500, e.getMessage()); + } + } +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/AppUserMapper.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/AppUserMapper.java new file mode 100644 index 0000000..ee22cc5 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/AppUserMapper.java @@ -0,0 +1,7 @@ +package com.supersavedriving.user.modular.system.dao; + +import com.baomidou.mybatisplus.mapper.BaseMapper; +import com.supersavedriving.user.modular.system.model.AppUser; + +public interface AppUserMapper extends BaseMapper<AppUser> { +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/HtmlMapper.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/HtmlMapper.java new file mode 100644 index 0000000..d5089c0 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/HtmlMapper.java @@ -0,0 +1,11 @@ +package com.supersavedriving.user.modular.system.dao; + +import com.baomidou.mybatisplus.mapper.BaseMapper; +import com.supersavedriving.user.modular.system.model.Html; + +/** + * @author 39373 + * @date 2023/2/26 17:29 + */ +public interface HtmlMapper extends BaseMapper<Html> { +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/AppUserMapper.xml b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/AppUserMapper.xml new file mode 100644 index 0000000..235b934 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/AppUserMapper.xml @@ -0,0 +1,25 @@ +<?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="com.supersavedriving.user.modular.system.dao.AppUserMapper"> + + <!-- 通用查询映射结果 --> + <resultMap id="BaseResultMap" type="com.supersavedriving.user.modular.system.model.AppUser"> + <id column="id" property="id"/> + <result column="nickname" property="nickname"/> + <result column="phone" property="phone"/> + <result column="sex" property="sex"/> + <result column="avatar" property="avatar"/> + <result column="openid" property="openid"/> + <result column="unionid" property="unionid"/> + <result column="emergencyContact" property="emergencyContact"/> + <result column="emergencyPhone" property="emergencyPhone"/> + <result column="accountBalance" property="accountBalance"/> + <result column="userTagId" property="userTagId"/> + <result column="status" property="status"/> + <result column="createTime" property="createTime"/> + <result column="is_exception" property="isException"/> + <result column="remark" property="remark"/> + <result column="inviterType" property="inviterType"/> + <result column="inviterId" property="inviterId"/> + </resultMap> +</mapper> \ No newline at end of file diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/HtmlMapper.xml b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/HtmlMapper.xml new file mode 100644 index 0000000..f6500c6 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/dao/mapping/HtmlMapper.xml @@ -0,0 +1,12 @@ +<?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="com.supersavedriving.user.modular.system.dao.HtmlMapper"> + + <!-- 通用查询映射结果 --> + <resultMap id="BaseResultMap" type="com.supersavedriving.user.modular.system.model.Html"> + <id column="id" property="id"/> + <result column="type" property="type"/> + <result column="html" property="html"/> + <result column="createTime" property="createTime"/> + </resultMap> +</mapper> \ No newline at end of file diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/AppUser.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/AppUser.java new file mode 100644 index 0000000..7564177 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/AppUser.java @@ -0,0 +1,103 @@ +package com.supersavedriving.user.modular.system.model; + +import com.baomidou.mybatisplus.annotations.TableField; +import com.baomidou.mybatisplus.annotations.TableId; +import com.baomidou.mybatisplus.annotations.TableName; +import com.baomidou.mybatisplus.enums.IdType; +import lombok.Data; + +import java.util.Date; + +/** + * 用户 + */ +@Data +@TableName("t_app_user") +public class AppUser { + /** + * 主键 + */ + @TableId(value = "id", type = IdType.AUTO) + @TableField("id") + private Integer id; + /** + * 昵称 + */ + @TableField("nickname") + private String nickname; + /** + * 电话 + */ + @TableField("phone") + private String phone; + /** + * 性别(1=男,2=女) + */ + @TableField("sex") + private Integer sex; + /** + * 头像 + */ + @TableField("avatar") + private String avatar; + /** + * 微信openid + */ + @TableField("openid") + private String openid; + /** + * 微信unionid + */ + @TableField("unionid") + private String unionid; + /** + * 紧急联系人 + */ + @TableField("emergencyContact") + private String emergencyContact; + /** + * 紧急联系人电话 + */ + @TableField("emergencyPhone") + private String emergencyPhone; + /** + * 账户余额 + */ + @TableField("accountBalance") + private Double accountBalance; + /** + * 用户标签id + */ + @TableField("userTagId") + private Integer userTagId; + /** + * 状态(1=正常,2=冻结,3=删除) + */ + @TableField("status") + private Integer status; + /** + * 添加时间 + */ + @TableField("createTime") + private Date createTime; + /** + * 是否异常 1正常 2异常 + */ + @TableField("is_exception") + private Integer isException; + /** + * 启动冻结理由 + */ + @TableField("remark") + private String remark; + /** + * 邀约人类型(1=用户,2=司机) + */ + @TableField("inviterType") + private Integer inviterType; + /** + * 邀约人id + */ + @TableField("inviterId") + private Integer inviterId; +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/Html.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/Html.java new file mode 100644 index 0000000..cad5459 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/model/Html.java @@ -0,0 +1,38 @@ +package com.supersavedriving.user.modular.system.model; + +import com.baomidou.mybatisplus.annotations.TableField; +import com.baomidou.mybatisplus.annotations.TableId; +import com.baomidou.mybatisplus.annotations.TableName; +import com.baomidou.mybatisplus.enums.IdType; +import lombok.Data; + +import java.util.Date; + +/** + * 协议 + */ +@Data +@TableName("t_html") +public class Html { + /** + * 主键 + */ + @TableId(value = "id", type = IdType.AUTO) + @TableField("id") + private Integer id; + /** + * 类型(1=用户协议,2=隐私政策,3=法律条款,4=代驾服务协议,5=个人信息处理规则,6=积分说明,7=佣金规则说明,8=行程录音说明,9=预估价格说明,10=加盟基本要求,11=加盟流程,12=起步价说明,13=注销协议,14=关于我们) + */ + @TableField("type") + private Integer type; + /** + * H5内容 + */ + @TableField("html") + private String html; + /** + * 添加时间 + */ + @TableField("createTime") + private Date createTime; +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IAppUserService.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IAppUserService.java new file mode 100644 index 0000000..e392733 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IAppUserService.java @@ -0,0 +1,27 @@ +package com.supersavedriving.user.modular.system.service; + +import com.baomidou.mybatisplus.service.IService; +import com.supersavedriving.user.modular.system.model.AppUser; +import com.supersavedriving.user.modular.system.util.ResultUtil; +import com.supersavedriving.user.modular.system.warpper.SignInToRegister; + +public interface IAppUserService extends IService<AppUser> { + + + /** + * 用户登录 + * @param jscode + * @return + * @throws Exception + */ + ResultUtil appUserLogin(String jscode) throws Exception; + + + /** + * 微信授权注册登录 + * @param signInToRegister + * @return + * @throws Exception + */ + ResultUtil signInToRegister(SignInToRegister signInToRegister) throws Exception; +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IHtmlService.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IHtmlService.java new file mode 100644 index 0000000..264b1bf --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/IHtmlService.java @@ -0,0 +1,11 @@ +package com.supersavedriving.user.modular.system.service; + +import com.baomidou.mybatisplus.service.IService; +import com.supersavedriving.user.modular.system.model.Html; + +/** + * @author 39373 + * @date 2023/2/26 17:30 + */ +public interface IHtmlService extends IService<Html> { +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/AppUserServiceImpl.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/AppUserServiceImpl.java new file mode 100644 index 0000000..ab94bfa --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/AppUserServiceImpl.java @@ -0,0 +1,156 @@ +package com.supersavedriving.user.modular.system.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.mapper.EntityWrapper; +import com.baomidou.mybatisplus.service.impl.ServiceImpl; +import com.supersavedriving.user.core.shiro.ShiroKit; +import com.supersavedriving.user.core.shiro.ShiroUser; +import com.supersavedriving.user.core.util.JwtTokenUtil; +import com.supersavedriving.user.core.util.ToolUtil; +import com.supersavedriving.user.modular.system.dao.AppUserMapper; +import com.supersavedriving.user.modular.system.model.AppUser; +import com.supersavedriving.user.modular.system.service.IAppUserService; +import com.supersavedriving.user.modular.system.util.RedisUtil; +import com.supersavedriving.user.modular.system.util.ResultUtil; +import com.supersavedriving.user.modular.system.util.weChat.WXCore; +import com.supersavedriving.user.modular.system.util.weChat.WeChatUtil; +import com.supersavedriving.user.modular.system.util.weChat.model.Code2Session; +import com.supersavedriving.user.modular.system.warpper.SignInToRegister; +import org.apache.shiro.authc.SimpleAuthenticationInfo; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.authc.credential.HashedCredentialsMatcher; +import org.apache.shiro.crypto.hash.Md5Hash; +import org.apache.shiro.util.ByteSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; + + +/** + * 用户 + */ +@Service +public class AppUserServiceImpl extends ServiceImpl<AppUserMapper, AppUser> implements IAppUserService { + + @Autowired + private WeChatUtil weChatUtil; + + @Autowired + private RedisUtil redisUtil; + + private final String salt = "s5d1"; + + + + @Override + public ResultUtil<String> appUserLogin(String jscode) throws Exception { + Code2Session code2Session = weChatUtil.code2Session(jscode); + if(code2Session.getErrcode() != 0){ + return ResultUtil.error(code2Session.getErrmsg()); + } + String openid = code2Session.getOpenid(); + AppUser appUser = this.selectOne(new EntityWrapper<AppUser>().eq("openid", openid).ne("status", 3)); + if(null == appUser){ + return ResultUtil.error("无效的账号"); + } + if(appUser.getStatus() == 2){ + return ResultUtil.error("账号被冻结"); + } + String token = getToken(appUser); + if(ToolUtil.isEmpty(token)){ + return ResultUtil.error("获取身份凭证失败"); + } + return ResultUtil.success(token); + } + + + /** + * 获取身份凭证 + * @return + */ + public String getToken(AppUser appUser){ + //封装请求账号密码为shiro可验证的token + String phone = appUser.getPhone(); + UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(phone, phone.toCharArray()); + + String credentials = ShiroKit.md5(phone, salt); + ByteSource credentialsSalt = new Md5Hash(salt); + SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo( + new ShiroUser(), credentials, credentialsSalt, ""); + + //校验用户账号密码 + HashedCredentialsMatcher md5CredentialsMatcher = new HashedCredentialsMatcher(); + md5CredentialsMatcher.setHashAlgorithmName(ShiroKit.hashAlgorithmName); + md5CredentialsMatcher.setHashIterations(ShiroKit.hashIterations); + boolean passwordTrueFlag = md5CredentialsMatcher.doCredentialsMatch( + usernamePasswordToken, simpleAuthenticationInfo); + + if (passwordTrueFlag) { + String token = JwtTokenUtil.generateToken(phone); + String key = token; + if(token.length() > 16){ + key = token.substring(token.length() - 16); + } + redisUtil.setStrValue(key, appUser.getId().toString(), 7 * 24 * 60 * 60); + return token; + } + return null; + } + + + /** + * 微信授权注册登录 + * @param signInToRegister + * @return + * @throws Exception + */ + @Override + public ResultUtil signInToRegister(SignInToRegister signInToRegister) throws Exception { + if(ToolUtil.isEmpty(signInToRegister.getJscode())){ + return ResultUtil.paranErr("jscode"); + } + if(ToolUtil.isEmpty(signInToRegister.getEncryptedDataPhone())){ + return ResultUtil.paranErr("encryptedDataPhone"); + } + if(ToolUtil.isEmpty(signInToRegister.getIvPhone())){ + return ResultUtil.paranErr("ivPhone"); + } + Code2Session code2Session = weChatUtil.code2Session(signInToRegister.getJscode()); + if(code2Session.getErrcode() != 0){ + return ResultUtil.error(code2Session.getErrmsg()); + } + String openid = code2Session.getOpenid(); + String session_key = code2Session.getSession_key(); + String decrypt = WXCore.decrypt(signInToRegister.getEncryptedDataPhone(), session_key, signInToRegister.getIvPhone()); + if(ToolUtil.isEmpty(decrypt)){ + return ResultUtil.error("获取手机号失败"); + } + JSONObject phone = JSON.parseObject(decrypt); + String purePhoneNumber = phone.getString("purePhoneNumber"); + AppUser appUser = this.selectOne(new EntityWrapper<AppUser>().eq("phone", purePhoneNumber).ne("status", 3)); + if(null == appUser){ + appUser = new AppUser(); + appUser.setNickname("亲爱的用户"); + appUser.setPhone(purePhoneNumber); + appUser.setOpenid(openid); + appUser.setUnionid(code2Session.getUnionid()); + appUser.setAccountBalance(0D); + appUser.setStatus(1); + appUser.setCreateTime(new Date()); + appUser.setIsException(1); + appUser.setInviterId(signInToRegister.getInviterId()); + appUser.setInviterType(signInToRegister.getInviterType()); + this.insert(appUser); + } + if(appUser.getStatus() == 2){ + return ResultUtil.error("账号被冻结"); + } + String token = getToken(appUser); + if(ToolUtil.isEmpty(token)){ + return ResultUtil.error("获取身份凭证失败"); + } + return ResultUtil.success(token); + } +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/HtmlServiceImpl.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/HtmlServiceImpl.java new file mode 100644 index 0000000..91834bf --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/service/impl/HtmlServiceImpl.java @@ -0,0 +1,16 @@ +package com.supersavedriving.user.modular.system.service.impl; + +import com.baomidou.mybatisplus.service.impl.ServiceImpl; +import com.supersavedriving.user.modular.system.dao.HtmlMapper; +import com.supersavedriving.user.modular.system.model.Html; +import com.supersavedriving.user.modular.system.service.IHtmlService; +import org.springframework.stereotype.Service; + +/** + * TODO 协议 + * @author 39373 + * @date 2023/2/26 17:31 + */ +@Service +public class HtmlServiceImpl extends ServiceImpl<HtmlMapper, Html> implements IHtmlService { +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/HttpClientUtil.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/HttpClientUtil.java deleted file mode 100644 index 4d19840..0000000 --- a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/HttpClientUtil.java +++ /dev/null @@ -1,266 +0,0 @@ -package com.supersavedriving.user.modular.system.util; - -import com.fasterxml.jackson.databind.ObjectMapper; -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.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.util.EntityUtils; -import org.springframework.stereotype.Component; - -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.*; -import java.util.concurrent.TimeUnit; - -/** - * http工具类 - */ -@Component -public class HttpClientUtil { - - private CloseableHttpClient httpClient; - - private CloseableHttpResponse httpResponse; - - private RequestConfig requestConfig; - - - /** - * 创建一个httpClient对象 - */ - private void getHttpCline(){ - //1.创建连接池管理器 - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(60000, - TimeUnit.MILLISECONDS); - connectionManager.setMaxTotal(1000); - connectionManager.setDefaultMaxPerRoute(50); - - //2.创建httpclient对象 - this.httpClient = HttpClients.custom() - .setConnectionManager(connectionManager) - .disableAutomaticRetries() - .build(); - } - - private RequestConfig getRequestConfig(){ - return RequestConfig.custom() - .setConnectTimeout(60000) - .setSocketTimeout(60000) - .build(); - } - - - - /** - * 创建一个POST请求实例 - * @param url 请求地址 - * @param params 请求参数 - */ - private void setPostHttpRequset(String url, Map<String, Object> params, Map<String, String> header, String contentType){ - HttpPost httpPost = new HttpPost(url); - httpPost.setConfig(this.getRequestConfig()); - if(null != header){ - for(String key : header.keySet()){ - httpPost.setHeader(key, header.get(key)); - } - } - List<NameValuePair> list = new ArrayList<>(); - if(null != params){ - Set<String> keys = params.keySet(); - for(String key : keys){ - list.add(new BasicNameValuePair(key, params.get(key).toString())); - } - } - try { - switch (contentType){ - case "form": - httpPost.setEntity(new UrlEncodedFormEntity(list, "UTF-8")); - break; - case "json": - ObjectMapper objectMapper = new ObjectMapper(); - String s =objectMapper.writeValueAsString(params); - System.err.println(s); - httpPost.setEntity(new StringEntity(s, Charset.forName("UTF-8"))); - break; - } - this.getHttpCline(); - if(null == this.httpClient){ - this.getHttpCline(); - } - httpResponse = this.httpClient.execute(httpPost); - } catch (IOException e) { - e.printStackTrace(); - this.close(); - } - } - - - /** - * 获取get请求实例 - * @param url 请求地址 - * @param params 请求参数 - */ - private void setGetHttpRequset(String url, Map<String, Object> params, Map<String, String> header){ - StringBuffer sb = new StringBuffer(); - String p = ""; - if(null != params){ - Set<String> keys = params.keySet(); - for(String key : keys){ - sb.append(key + "=" + params.get(key) + "&"); - } - p = "?" + sb.substring(0, sb.length() - 1); - } - HttpGet httpGet = new HttpGet(url + p); - if(null != header){ - for(String key : header.keySet()){ - httpGet.setHeader(key, header.get(key)); - } - } - this.getHttpCline(); - if(null == this.httpClient){ - this.getHttpCline(); - } - try { - httpResponse = this.httpClient.execute(httpGet); - } catch (IOException e) { - e.printStackTrace(); - this.close(); - } - } - - - /** - * 发送http请求 - * @param mothed "GET、POST、PUT、HEAD、DELETE、HEAD、OPTIONS" - * @param url 请求地址 - * @param params 请求参数 - * @param header 请求头 - * @param contentType 参数请求方式form/json - * @return - */ - public String pushHttpRequset(String mothed, String url, Map<String, Object> params, Map<String, String> header, String contentType){ - String content = null; - switch (mothed){ - case "GET": - this.setGetHttpRequset(url, params, header); - break; - case "POST": - this.setPostHttpRequset(url, params, header, contentType); - break; - } - if(httpResponse.getStatusLine().getStatusCode() == 200){ - try { - content = EntityUtils.toString(httpResponse.getEntity()); - this.close(); - return content; - } catch (IOException e) { - e.printStackTrace(); - this.close(); - } - } - if(httpResponse.getStatusLine().getStatusCode() == 201){ - content = "{\"status\":201}"; - this.close(); - return content; - }else{ - try { - System.err.println("返回状态码:" + httpResponse.getStatusLine() + "。"); - content = EntityUtils.toString(httpResponse.getEntity()); - this.close(); - return content; - } catch (IOException e) { - e.printStackTrace(); - this.close(); - } - } - this.close(); - return content; - } - - - /** - * 发送XML请求 - * @param url 请求地址 - * @param xml XML数据 - * @param header 自定义请求头 - * @return - */ - public String pushHttpRequsetXml(String url, String xml, Map<String, String> header){ - HttpPost httpPost = new HttpPost(url); - for(String key : header.keySet()){ - httpPost.setHeader(key, header.get(key)); - } - httpPost.setHeader("Content-Type", "application/xml"); - try { - httpPost.setEntity(new StringEntity(xml, "UTF-8")); - this.getHttpCline(); - if(null == this.httpClient){ - this.getHttpCline(); - } - httpResponse = this.httpClient.execute(httpPost); - String content = null; - if(httpResponse.getStatusLine().getStatusCode() == 200){ - try { - content = EntityUtils.toString(httpResponse.getEntity(), "UTF-8"); - this.close(); - return content; - } catch (IOException e) { - e.printStackTrace(); - this.close(); - } - }else{ - try { - content = "返回状态码:" + httpResponse.getStatusLine() + "。" + EntityUtils.toString(httpResponse.getEntity()); - this.close(); - return content; - } catch (IOException e) { - e.printStackTrace(); - this.close(); - } - } - this.close(); - return content; - } catch (IOException e) { - e.printStackTrace(); - this.close(); - } - return null; - } - - - - /** - * 关闭资源 - */ - private void close(){ - try { - if(null != httpClient){ - httpClient.close(); - } - if(null != httpResponse){ - httpResponse.close(); - } - } catch (IOException e) { - e.printStackTrace(); - }finally { - try { - if(null != httpClient){ - httpClient.close(); - } - if(null != httpResponse){ - httpResponse.close(); - } - }catch (Exception e){ - e.printStackTrace(); - } - } - } -} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/RedisUtil.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/RedisUtil.java index 7594824..218ef97 100644 --- a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/RedisUtil.java +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/RedisUtil.java @@ -1,16 +1,15 @@ package com.supersavedriving.user.modular.system.util; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import com.supersavedriving.user.core.util.ToolUtil; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; import org.springframework.stereotype.Component; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; +import org.springframework.util.StringUtils; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.Pipeline; + +import java.io.IOException; +import java.util.*; /** @@ -20,7 +19,9 @@ public class RedisUtil { @Autowired - private RestTemplate internalRestTemplate; + private JedisPool jedisPool; + + private Timer timer; /** @@ -29,23 +30,11 @@ * @param value */ public void setStrValue(String key, String value){ - if(ToolUtil.isNotEmpty(key)){ - //发送验证码短信 - HttpHeaders headers = new HttpHeaders(); - // 以表单的方式提交 - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - //将请求头部和参数合成一个请求 - MultiValueMap<String, Object> params = new LinkedMultiValueMap<>(); - params.add("key", key); - params.add("value", value); - HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(params, headers); - String s = internalRestTemplate.postForObject("http://zuul-gateway/redis/setValue_", requestEntity, String.class); - JSONObject jsonObject = JSON.parseObject(s, JSONObject.class); - if(jsonObject.getIntValue("code") != 200){ - System.err.println("调用redis出错了"); - } + if(ToolUtil.isNotEmpty(key) && ToolUtil.isNotEmpty(value)){ + Jedis resource = jedisPool.getResource(); + String set = resource.set(key, value); + closeJedis(resource); } - } @@ -56,22 +45,10 @@ * @param time 秒 */ public void setStrValue(String key, String value, int time){ - if(ToolUtil.isNotEmpty(key)){ - //发送验证码短信 - HttpHeaders headers = new HttpHeaders(); - // 以表单的方式提交 - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - //将请求头部和参数合成一个请求 - MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); - params.add("key", key); - params.add("value", value); - params.add("time", String.valueOf(time)); - HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(params, headers); - String s = internalRestTemplate.postForObject("http://zuul-gateway/redis/setValue", requestEntity, String.class); - JSONObject jsonObject = JSON.parseObject(s, JSONObject.class); - if(jsonObject.getIntValue("code") != 200){ - System.err.println("调用redis出错了"); - } + if(ToolUtil.isNotEmpty(key) && ToolUtil.isNotEmpty(value)){ + Jedis resource = jedisPool.getResource(); + String setex = resource.setex(key, time, value); + closeJedis(resource); } } @@ -83,45 +60,180 @@ */ public String getValue(String key){ if(ToolUtil.isNotEmpty(key)){ - HttpHeaders headers = new HttpHeaders(); - // 以表单的方式提交 - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - //将请求头部和参数合成一个请求 - MultiValueMap<String, Object> params = new LinkedMultiValueMap<>(); - params.add("key", key); - HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(params, headers); - String s = internalRestTemplate.postForObject("http://zuul-gateway/redis/getValue",requestEntity , String.class); - JSONObject jsonObject = JSON.parseObject(s, JSONObject.class); - if(jsonObject.getIntValue("code") != 200){ - System.err.println("调用redis出错了"); - } - return jsonObject.getString("data"); + Jedis resource = jedisPool.getResource(); + String data = resource.get(key); + closeJedis(resource); + return data; } return null; } + + /** + * 批量获取 + * @param kes + * @return + */ + public List<Object> getValues(List<String> kes){ + if(null != kes){ + Jedis resource = jedisPool.getResource(); + Pipeline pipelined = resource.pipelined(); + for(String key : kes){ + pipelined.get(key); + } + List<Object> list = pipelined.syncAndReturnAll(); + + closeJedis(resource); + pipelined.clear(); + try { + pipelined.close(); + } catch (IOException e) { + e.printStackTrace(); + } + List<Object> data = new ArrayList<>(); + for(Object o : list){ + if(null != o){ + data.add(o); + } + } + return data; + } + return null; + } /** * 删除key * @param key */ - public String remove(String key){ + public void remove(String key){ if(ToolUtil.isNotEmpty(key)){ - HttpHeaders headers = new HttpHeaders(); - // 以表单的方式提交 - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - //将请求头部和参数合成一个请求 - MultiValueMap<String, Object> params = new LinkedMultiValueMap<>(); - params.add("key", key); - HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(params, headers); - String s = internalRestTemplate.postForObject("http://zuul-gateway/redis/remove",requestEntity , String.class); - JSONObject jsonObject = JSON.parseObject(s, JSONObject.class); - if(jsonObject.getIntValue("code") != 200){ - System.err.println("调用redis出错了"); - } - return jsonObject.getString("data"); + Jedis resource = jedisPool.getResource(); + Long del = resource.del(key); + closeJedis(resource); } - return null; + } + + + /** + * 向集合key添加数据 + * @param key + * @param members + */ + public void addSetValue(String key, String...members){ + if(ToolUtil.isNotEmpty(key) && ToolUtil.isNotEmpty(members)){ + Jedis resource = jedisPool.getResource(); + Long sadd = resource.sadd(key, members); + resource.close(); + } + } + + + /** + * 返回Set集合数据 + * @param key + * @return + */ + public Set<String> getSetAllValue(String key){ + Set<String> smembers = new HashSet<>(); + if(ToolUtil.isNotEmpty(key)){ + Jedis resource = jedisPool.getResource(); + smembers = resource.smembers(key); + resource.close(); + } + return smembers; + } + + + /** + * 删除Set集合中的值 + * @param key + * @param members + */ + public void delSetValue(String key, String...members){ + if(ToolUtil.isNotEmpty(key) && ToolUtil.isNotEmpty(members)){ + Jedis resource = jedisPool.getResource(); + Long sadd = resource.srem(key, members); + resource.close(); + } + } + + + /** + * 删除资源 + * @param jedis + */ + public void closeJedis(Jedis jedis){ + if(null != jedis){ + jedis.close(); + } + } + + + /** + * redis加锁 + * @param key + * @param value + * @param time + * @return + */ + public boolean lock(String key, String value, int time){ + if(!StringUtils.isEmpty(key)){ + key += "_lock"; + Jedis resource = jedisPool.getResource(); + String set = resource.set(key, value, "nx", "ex", time); + if("OK".equals(set)){ + String finalKey = key; + timer = new Timer(); + timer.schedule(new TimerTask() { + @Override + public void run() { + System.err.println("定时任务启动"); + Jedis resource = jedisPool.getResource(); + resource.setex(finalKey, time, value); + resource.close(); + } + }, 1000, 500); + } + resource.close(); + return "OK".equals(set) ? true : false; + } + return false; + } + + /** + * 获取redis锁 + * @param time + * @return + */ + public boolean lock(int time){ + String uuid = UUID.randomUUID().toString(); + return lock("redis", uuid, time); + } + + + /** + * redis释放锁 + * @param key + * @return + */ + public boolean unlock(String key){ + if(!StringUtils.isEmpty(key)){ + key += "_lock"; + Jedis resource = jedisPool.getResource(); + timer.cancel();//取消定时任务 + Long del = resource.del(key); + resource.close(); + return del != 0 ? true : false; + } + return false; + } + + /** + * 删除锁 + * @return + */ + public boolean unlock(){ + return unlock("redis"); } } diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/ResultUtil.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/ResultUtil.java index e78e01f..8134c57 100644 --- a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/ResultUtil.java +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/ResultUtil.java @@ -1,6 +1,5 @@ package com.supersavedriving.user.modular.system.util; -import com.alibaba.fastjson.JSONObject; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -10,23 +9,23 @@ @ApiModel(value = "统一返回结果集") public class ResultUtil<T> { - public static final Integer SUCCESS = 200; + public static final Integer SUCCESS = 10000; - public static final Integer PARAM_ERROR = 300; + public static final Integer PARAM_ERROR = 10010; - public static final Integer RUNTIME_ERROR = 400; + public static final Integer SYSTEM_INFO = 10020; - public static final Integer ERROR = 500; + public static final Integer TOKEN_ERROR = 10030; - public static final Integer TOKEN_ERROR = 600; + public static final Integer SIGN_ERROR = 10040; - public static final Integer SIGN_ERROR = 700; + public static final Integer RUNTIME_ERROR = 10050; - public static final String Token = "TOKEN_INVALID"; + public static final String Token = "token无效"; - public static final String SIGN = "SIGN_INVALID"; + public static final String SIGN = "签名无效"; - @ApiModelProperty(name = "code", value = "业务状态码 200:成功,300:参数错误,400:运行异常,500:其他异常, 600:token无效,需重新登录,700:签名无效") + @ApiModelProperty(name = "code", value = "业务状态码 10000:成功,10010:参数错误,10020:系统提示, 10030:身份校验异常,10040:签名不通过,10050:系统运行异常") private Integer code;//备用状态码 @ApiModelProperty(name = "msg", value = "返回结果说明") @@ -75,15 +74,15 @@ * @return */ public static ResultUtil error(String mag){ - return ResultUtil.getResult(ResultUtil.ERROR, mag, new JSONObject()); + return ResultUtil.getResult(ResultUtil.SYSTEM_INFO, mag, new Object()); } /** * 错误信息 * @return */ - public static <T> ResultUtil<T> error(String mag, T obj){ - return ResultUtil.getResult(ResultUtil.ERROR, mag, obj); + public static <T> ResultUtil <T> error(String mag, T obj){ + return ResultUtil.getResult(ResultUtil.SYSTEM_INFO, mag, obj); } /** @@ -91,7 +90,7 @@ * @return */ public static ResultUtil tokenErr(){ - return ResultUtil.getResult(ResultUtil.TOKEN_ERROR, ResultUtil.Token, new JSONObject()); + return ResultUtil.getResult(ResultUtil.TOKEN_ERROR, ResultUtil.Token, new Object()); } /** @@ -99,7 +98,15 @@ * @return */ public static ResultUtil tokenErr(String msg){ - return ResultUtil.getResult(ResultUtil.TOKEN_ERROR, msg, new JSONObject()); + return ResultUtil.getResult(ResultUtil.TOKEN_ERROR, msg, new Object()); + } + + /** + * 参数异常 + * @return + */ + public static ResultUtil paranErr(String...ages){ + return ResultUtil.getResult(ResultUtil.PARAM_ERROR, "【" + ages + "】参数异常", new Object()); } /** @@ -107,15 +114,7 @@ * @return */ public static ResultUtil paranErr(){ - return ResultUtil.getResult(ResultUtil.PARAM_ERROR, "PARAM_ERROR", new JSONObject()); - } - - /** - * 参数异常 - * @return - */ - public static <T> ResultUtil<T> paranErr(T data){ - return ResultUtil.getResult(ResultUtil.PARAM_ERROR, "SYSTEM_RUN_ERROR", data); + return ResultUtil.getResult(ResultUtil.PARAM_ERROR, "参数异常", new Object()); } /** @@ -123,7 +122,7 @@ * @return */ public static ResultUtil runErr(){ - return ResultUtil.getResult(ResultUtil.RUNTIME_ERROR, "SYSTEM_RUN_ERROR", new JSONObject()); + return ResultUtil.getResult(ResultUtil.RUNTIME_ERROR, "系统运行异常", new Object()); } @@ -131,15 +130,15 @@ * 运行异常 * @return */ - public static <T> ResultUtil<T> runErr(T data){ - return ResultUtil.getResult(ResultUtil.RUNTIME_ERROR, "SYSTEM_RUN_ERROR", data); + public static <T>ResultUtil<T> runErr(T data){ + return ResultUtil.getResult(ResultUtil.RUNTIME_ERROR, "系统运行异常", data); } /** * 运行异常 * @return */ - public static <T> ResultUtil<T> runErr(T data, String msg){ + public static <T>ResultUtil<T> runErr(T data, String msg){ return ResultUtil.getResult(ResultUtil.RUNTIME_ERROR, msg, data); } @@ -150,7 +149,7 @@ * @return */ public static ResultUtil success(){ - return ResultUtil.getResult(ResultUtil.SUCCESS, "SUCCESS", new JSONObject()); + return ResultUtil.getResult(ResultUtil.SUCCESS, "成功", new Object()); } @@ -161,7 +160,7 @@ * @return */ public static <T> ResultUtil<T> success(T data){ - return ResultUtil.getResult(ResultUtil.SUCCESS, "SUCCESS", data); + return ResultUtil.getResult(ResultUtil.SUCCESS, "成功", data); } /** diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpClientUtil.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpClientUtil.java new file mode 100644 index 0000000..35921fe --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpClientUtil.java @@ -0,0 +1,269 @@ +package com.supersavedriving.user.modular.system.util.httpClinet; + +import com.alibaba.fastjson.JSON; +import com.fasterxml.jackson.databind.ObjectMapper; +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.conn.ssl.SSLConnectionSocketFactory; +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.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.ssl.SSLContexts; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.net.ssl.SSLContext; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.security.KeyStore; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * http工具类 + */ + +public class HttpClientUtil { + + private static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class); + + private static PoolingHttpClientConnectionManager connectionManager; + + + { + //1.创建连接池管理器 + connectionManager = new PoolingHttpClientConnectionManager(60000, + TimeUnit.MILLISECONDS); + connectionManager.setMaxTotal(1000); + connectionManager.setDefaultMaxPerRoute(50); + } + + /** + * 创建一个httpClient对象 + */ + private static CloseableHttpClient getHttpCline(){ + return HttpClients.custom() + .setConnectionManager(connectionManager) + .disableAutomaticRetries() + .build(); + } + + private static RequestConfig getRequestConfig(){ + RequestConfig.Builder builder = RequestConfig.custom(); + builder.setSocketTimeout(60000)//3.1设置客户端等待服务端返回数据的超时时间 + .setConnectTimeout(30000)//3.2设置客户端发起TCP连接请求的超时时间 + .setExpectContinueEnabled(true) + .setConnectionRequestTimeout(30000);//3.3设置客户端从连接池获取链接的超时时间 + return builder.build(); + } + + + + /** + * 创建一个POST请求实例 + * @param url 请求地址 + * @param params 请求参数 + */ + private static CloseableHttpResponse setPostHttpRequset(String url, Map<String, Object> params, Map<String, String> header, String contentType) throws Exception{ + HttpPost httpPost = new HttpPost(url); + httpPost.setConfig(getRequestConfig()); + if(null != header){ + for(String key : header.keySet()){ + httpPost.setHeader(key, header.get(key)); + } + } + List<NameValuePair> list = new ArrayList<>(); + if(null != params){ + Set<String> keys = params.keySet(); + for(String key : keys){ + list.add(new BasicNameValuePair(key, null == params.get(key) ? null : params.get(key).toString())); + } + } + switch (contentType){ + case "form": + httpPost.setEntity(new UrlEncodedFormEntity(list, "UTF-8")); + break; + case "json": + ObjectMapper objectMapper = new ObjectMapper(); + String s =objectMapper.writeValueAsString(params); + httpPost.setEntity(new StringEntity(s, ContentType.create(ContentType.APPLICATION_JSON.getMimeType(), Charset.forName("UTF-8")))); + break; + } + return getHttpCline().execute(httpPost); + } + + + /** + * 获取get请求实例 + * @param url 请求地址 + * @param params 请求参数 + */ + private static CloseableHttpResponse setGetHttpRequset(String url, Map<String, Object> params, Map<String, String> header) throws Exception{ + StringBuffer sb = new StringBuffer(); + String p = ""; + if(null != params){ + Set<String> keys = params.keySet(); + for(String key : keys){ + sb.append(key + "=" + params.get(key) + "&"); + } + p = "?" + sb.substring(0, sb.length() - 1); + } + HttpGet httpGet = new HttpGet(url + p); + httpGet.setConfig(getRequestConfig()); + if(null != header){ + for(String key : header.keySet()){ + httpGet.setHeader(key, header.get(key)); + } + } + return getHttpCline().execute(httpGet); + } + + + /** + * 发送http请求 + * @param mothed "GET、POST、PUT、HEAD、DELETE、HEAD、OPTIONS" + * @param url 请求地址 + * @param params 请求参数 + * @param header 请求头 + * @param contentType 参数请求方式form/json + * @return + */ + public static HttpResult pushHttpRequset(String mothed, String url, Map<String, Object> params, Map<String, String> header, String contentType) throws Exception{ + String randome = UUID.randomUUID().toString(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S"); + logger.info(sdf.format(new Date()) + "----(" + randome + ")请求参数:" + JSON.toJSONString(params)); + CloseableHttpResponse httpResponse = null; + switch (mothed){ + case "GET": + httpResponse = setGetHttpRequset(url, params, header); + break; + case "POST": + httpResponse = setPostHttpRequset(url, params, header, contentType); + break; + } + int statusCode = httpResponse.getStatusLine().getStatusCode(); + String content = EntityUtils.toString(httpResponse.getEntity(), "UTF-8"); + logger.info(sdf.format(new Date()) + "----(" + randome + ")返回结果:" + content); + HttpResult httpResult = HttpResult.getHttpResult(statusCode, content); + close(httpResponse); + return httpResult; + } + + + /** + * 发送XML请求 + * @param url 请求地址 + * @param xml XML数据 + * @param header 自定义请求头 + * @return + */ + public static HttpResult pushHttpRequsetXml(String url, String xml, Map<String, String> header) throws Exception{ + HttpPost httpPost = new HttpPost(url); + httpPost.setConfig(getRequestConfig()); + for(String key : header.keySet()){ + httpPost.setHeader(key, header.get(key)); + } + httpPost.setHeader("Content-Type", "application/xml"); + httpPost.setEntity(new StringEntity(xml, "UTF-8")); + CloseableHttpResponse httpResponse = getHttpCline().execute(httpPost); + int statusCode = httpResponse.getStatusLine().getStatusCode(); + String content = EntityUtils.toString(httpResponse.getEntity(), "UTF-8"); + HttpResult httpResult = HttpResult.getHttpResult(statusCode, content); + close(httpResponse); + return httpResult; + } + + + + /** + * 请求https发送XML请求 + * @param url 接口路径 + * @param xml 内容 + * @param header 请求头 + * @param certPassword 证书密码 + * @param certPath 证书路径 + * @param certType 证书类型 + * @return + * @throws Exception + */ + public static String pushHttpsRequsetXml(String url, String xml, Map<String, String> header, String certPassword, String certPath, String certType) throws Exception{ + HttpPost httpPost = new HttpPost(url); + for(String key : header.keySet()){ + httpPost.setHeader(key, header.get(key)); + } + httpPost.setHeader("Content-Type", "application/xml"); + httpPost.setEntity(new StringEntity(xml, "UTF-8")); + CloseableHttpClient httpCline = initCert(certPassword, certPath, certType); + CloseableHttpResponse httpResponse = httpCline.execute(httpPost); + String content = null; + if(httpResponse.getStatusLine().getStatusCode() == 200){ + content = EntityUtils.toString(httpResponse.getEntity(), "UTF-8"); + }else{ + content = "返回状态码:" + httpResponse.getStatusLine() + "。" + EntityUtils.toString(httpResponse.getEntity()); + } + close(httpResponse); + httpCline.close(); + return content; + } + + + /** + * 初始化https对象(带证书) + * @param key 证书密码 + * @param certPath 证书路径 + * @param certType 证书类型 + * @throws Exception + */ + private static CloseableHttpClient initCert(String key, String certPath, String certType) throws Exception { + KeyStore keyStore = KeyStore.getInstance(certType); + InputStream inputStream = new FileInputStream(new File(certPath)); + try { + keyStore.load(inputStream, key.toCharArray()); + } finally { + inputStream.close(); + } + SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build(); + SSLConnectionSocketFactory sslsf = + new SSLConnectionSocketFactory(sslcontext, new String[] {"TLSv1"}, null, + SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + return HttpClients.custom().setSSLSocketFactory(sslsf).build(); + } + + + + /** + * 关闭资源 + */ + private static void close(CloseableHttpResponse httpResponse){ + try { + if(null != httpResponse){ + EntityUtils.consume(httpResponse.getEntity());//此处高能,通过源码分析,由EntityUtils是否回收HttpEntity + httpResponse.close(); + } + } catch (Exception e) { + e.printStackTrace(); + }finally { + try { + if(null != httpResponse){ + httpResponse.close(); + } + }catch (Exception e){ + e.printStackTrace(); + } + } + } + + +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpResult.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpResult.java new file mode 100644 index 0000000..6ff434a --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/httpClinet/HttpResult.java @@ -0,0 +1,31 @@ +package com.supersavedriving.user.modular.system.util.httpClinet; + +import lombok.Data; + +/** + * http请求返回封装 + */ +@Data +public class HttpResult { + /** + * 返回状态码 + */ + private Integer code; + /** + * 返回结果 + */ + private String data; + + /** + * 返回封装结果 + * @param code + * @param data + * @return + */ + public static HttpResult getHttpResult(Integer code, String data){ + HttpResult httpResult = new HttpResult(); + httpResult.setCode(code); + httpResult.setData(data); + return httpResult; + } +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AES.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AES.java new file mode 100644 index 0000000..5ec0eb8 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AES.java @@ -0,0 +1,72 @@ +package com.supersavedriving.user.modular.system.util.weChat; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.*; + +/** +* AES加密 +* @author pzb +* @Date 2021/12/3 15:43 +*/ +public class AES { + + public static boolean initialized = false; + + /** + * AES解密 + * + * @param content + * 密文 + * @return + * @throws InvalidAlgorithmParameterException + * @throws NoSuchProviderException + */ + public byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) throws InvalidAlgorithmParameterException { + initialize(); + try { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); + Key sKeySpec = new SecretKeySpec(keyByte, "AES"); + cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));// 初始化 + byte[] result = cipher.doFinal(content); + return result; + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + } catch (BadPaddingException e) { + e.printStackTrace(); + } catch (NoSuchProviderException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return null; + } + + public static void initialize() { + if (initialized) + return; + Security.addProvider(new BouncyCastleProvider()); + initialized = true; + } + + // 生成iv + public static AlgorithmParameters generateIV(byte[] iv) throws Exception { + AlgorithmParameters params = AlgorithmParameters.getInstance("AES"); + params.init(new IvParameterSpec(iv)); + return params; + } +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AesException.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AesException.java new file mode 100644 index 0000000..178e466 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/AesException.java @@ -0,0 +1,59 @@ +package com.supersavedriving.user.modular.system.util.weChat; + +@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; + } + + AesException(int code) { + super(getMessage(code)); + this.code = code; + } + +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/ByteGroup.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/ByteGroup.java new file mode 100644 index 0000000..e35ba9d --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/ByteGroup.java @@ -0,0 +1,26 @@ +package com.supersavedriving.user.modular.system.util.weChat; + +import java.util.ArrayList; + +class ByteGroup { + ArrayList<Byte> byteContainer = new ArrayList<Byte>(); + + public byte[] toBytes() { + byte[] bytes = new byte[byteContainer.size()]; + for (int i = 0; i < byteContainer.size(); i++) { + bytes[i] = byteContainer.get(i); + } + return bytes; + } + + public ByteGroup addBytes(byte[] bytes) { + for (byte b : bytes) { + byteContainer.add(b); + } + return this; + } + + public int size() { + return byteContainer.size(); + } +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/PKCS7Encoder.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/PKCS7Encoder.java new file mode 100644 index 0000000..0513b21 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/PKCS7Encoder.java @@ -0,0 +1,67 @@ +/** + * 对公众平台发送给公众账号的消息加解密示例代码. + * + * @copyright Copyright (c) 1998-2014 Tencent Inc. + */ + +// ------------------------------------------------------------------------ + +package com.supersavedriving.user.modular.system.util.weChat; + +import java.nio.charset.Charset; +import java.util.Arrays; + +/** + * 提供基于PKCS7算法的加解密接口. + */ +class PKCS7Encoder { + static Charset CHARSET = Charset.forName("utf-8"); + static int BLOCK_SIZE = 32; + + /** + * 获得对明文进行补位填充的字节. + * + * @param count 需要进行填充补位操作的明文字节个数 + * @return 补齐用的字节数组 + */ + static byte[] encode(int count) { + // 计算需要填充的位数 + int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); + if (amountToPad == 0) { + amountToPad = BLOCK_SIZE; + } + // 获得补位所用的字符 + char padChr = chr(amountToPad); + String tmp = new String(); + for (int index = 0; index < amountToPad; index++) { + tmp += padChr; + } + return tmp.getBytes(CHARSET); + } + + /** + * 删除解密后明文的补位字符 + * + * @param decrypted 解密后的明文 + * @return 删除补位字符后的明文 + */ + static byte[] decode(byte[] decrypted) { + int pad = (int) decrypted[decrypted.length - 1]; + if (pad < 1 || pad > 32) { + pad = 0; + } + return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); + } + + /** + * 将数字转化成ASCII码对应的字符,用于对明文进行补码 + * + * @param a 需要转化的数字 + * @return 转化得到的字符 + */ + static char chr(int a) { + byte target = (byte) (a & 0xFF); + return (char) target; + } + +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SHA1.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SHA1.java new file mode 100644 index 0000000..5099cfe --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SHA1.java @@ -0,0 +1,61 @@ +/** + * 对公众平台发送给公众账号的消息加解密示例代码. + * + * @copyright Copyright (c) 1998-2014 Tencent Inc. + */ + +// ------------------------------------------------------------------------ + +package com.supersavedriving.user.modular.system.util.weChat; + +import java.security.MessageDigest; +import java.util.Arrays; + +/** + * SHA1 class + * + * 计算公众平台的消息签名接口. + */ +public class SHA1 { + + /** + * 用SHA1算法生成安全签名 + * @param token 票据 + * @param timestamp 时间戳 + * @param nonce 随机字符串 + * @param encrypt 密文 + * @return 安全签名 + * @throws AesException + */ + public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException + { + 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) { + e.printStackTrace(); + throw new AesException(AesException.ComputeSignatureError); + } + } +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SubscribeMessageUtil.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SubscribeMessageUtil.java new file mode 100644 index 0000000..9c6d06d --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/SubscribeMessageUtil.java @@ -0,0 +1,264 @@ +package com.supersavedriving.user.modular.system.util.weChat; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.stylefeng.guns.modular.system.util.RedisUtil; +import com.stylefeng.guns.modular.system.util.ResultUtil; +import com.stylefeng.guns.modular.system.util.httpClinet.HttpClientUtil; +import com.stylefeng.guns.modular.system.util.httpClinet.HttpResult; +import com.stylefeng.guns.modular.system.util.weChat.model.Category; +import com.stylefeng.guns.modular.system.util.weChat.model.MessageTemplate; +import com.stylefeng.guns.modular.system.util.weChat.model.PubTemplateKeywords; +import com.stylefeng.guns.modular.system.util.weChat.model.PubTemplatetitles; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 订阅消息 + */ +@Component +public class SubscribeMessageUtil { + + @Value("${wx.appletsAppid}") + private String wxAppletsAppid; + + @Autowired + private RedisUtil redisUtil; + + @Autowired + private HttpClientUtil httpClientUtil; + + + /** + * 获取消息类目 + * @return + */ + public ResultUtil<List<Category>> getcategory(){ + try { + String accessToken = redisUtil.getValue("wxAppletsAccessToken"); + String url = "https://api.weixin.qq.com/wxaapi/newtmpl/getcategory?access_token=" + accessToken; + HttpResult httpResult = httpClientUtil.pushHttpRequset("GET", url, null, null, "form"); + if(httpResult.getCode() != 200){ + return ResultUtil.error("获取消息类目失败"); + } + JSONObject jsonObject = JSON.parseObject(httpResult.getData()); + Integer errcode = jsonObject.getInteger("errcode"); + if(0 != errcode){ + return ResultUtil.error(jsonObject.getString("errmsg")); + } + JSONArray data = jsonObject.getJSONArray("data"); + List<Category> categories = data.toJavaList(Category.class); + return ResultUtil.success(categories); + }catch (Exception e){ + e.printStackTrace(); + return ResultUtil.runErr(); + } + } + + + /** + * 获取所属类目下的公共模板 + * @param ids + * @return + */ + public ResultUtil<List<PubTemplatetitles>> getpubtemplatetitles(String ids){ + List<PubTemplatetitles> pubTemplatetitles = new ArrayList<>(); + try { + Integer start = 0; + while (true){ + String accessToken = redisUtil.getValue("wxAppletsAccessToken"); + String url = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatetitles?access_token=" + accessToken; + Map<String, Object> params = new HashMap<>(); + params.put("ids", ids); + params.put("start", start + ""); + params.put("limit", 30); + HttpResult httpResult = httpClientUtil.pushHttpRequset("GET", url, params, null, "form"); + if(httpResult.getCode() != 200){ + return ResultUtil.error("获取消息类目失败"); + } + JSONObject jsonObject = JSON.parseObject(httpResult.getData()); + Integer errcode = jsonObject.getInteger("errcode"); + if(0 != errcode){ + return ResultUtil.error(jsonObject.getString("errmsg")); + } + JSONArray data = jsonObject.getJSONArray("data"); + List<PubTemplatetitles> pubTemplatetitles1 = data.toJavaList(PubTemplatetitles.class); + if(pubTemplatetitles1.size() == 0){ + break; + } + pubTemplatetitles.addAll(pubTemplatetitles1); + start += 30; + } + return ResultUtil.success(pubTemplatetitles); + }catch (Exception e){ + e.printStackTrace(); + return ResultUtil.runErr(); + } + } + + + /** + * 获取关键词列表 + * @param tid + * @return + */ + public ResultUtil<List<PubTemplateKeywords>> getpubtemplatekeywords(String tid){ + try { + String accessToken = redisUtil.getValue("wxAppletsAccessToken"); + String url = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatekeywords?access_token=" + accessToken; + Map<String, Object> params = new HashMap<>(); + params.put("tid", tid); + HttpResult httpResult = httpClientUtil.pushHttpRequset("GET", url, params, null, "form"); + if(httpResult.getCode() != 200){ + return ResultUtil.error("获取消息类目失败"); + } + JSONObject jsonObject = JSON.parseObject(httpResult.getData()); + Integer errcode = jsonObject.getInteger("errcode"); + if(0 != errcode){ + return ResultUtil.error(jsonObject.getString("errmsg")); + } + JSONArray data = jsonObject.getJSONArray("data"); + List<PubTemplateKeywords> pubTemplateKeywords = data.toJavaList(PubTemplateKeywords.class); + return ResultUtil.success(pubTemplateKeywords); + }catch (Exception e){ + e.printStackTrace(); + return ResultUtil.runErr(); + } + } + + + /** + * 获取个人模板列表 + * @return + */ + public ResultUtil<List<MessageTemplate>> getMessageTemplateList(){ + try { + String accessToken = redisUtil.getValue("wxAppletsAccessToken"); + String url = "https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate?access_token=" + accessToken; + HttpResult httpResult = httpClientUtil.pushHttpRequset("GET", url, null, null, "form"); + if(httpResult.getCode() != 200){ + return ResultUtil.error("获取消息模板失败"); + } + JSONObject jsonObject = JSON.parseObject(httpResult.getData()); + Integer errcode = jsonObject.getInteger("errcode"); + if(0 != errcode){ + return ResultUtil.error(jsonObject.getString("errmsg")); + } + JSONArray data = jsonObject.getJSONArray("data"); + List<MessageTemplate> messageTemplates = data.toJavaList(MessageTemplate.class); + return ResultUtil.success(messageTemplates); + }catch (Exception e){ + e.printStackTrace(); + return ResultUtil.runErr(); + } + } + + + /** + * 添加消息模板 + * @param tid 模板标题 id + * @param kidList 开发者自行组合好的模板关键词列表,关键词顺序可以自由搭配(例如 [3,5,4] 或 [4,5,3]),最多支持5个,最少2个关键词组合 + * @param sceneDesc 服务场景描述,15个字以内 + * @return + */ + public ResultUtil<String> addtemplate(String tid, String[] kidList, String sceneDesc){ + try { + String accessToken = redisUtil.getValue("wxAppletsAccessToken"); + String url = "https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate?access_token=" + accessToken; + Map<String, Object> params = new HashMap<>(); + params.put("tid", tid); + params.put("kidList", kidList); + params.put("sceneDesc", sceneDesc); + HttpResult httpResult = httpClientUtil.pushHttpRequset("POST", url, params, null, "json"); + if(httpResult.getCode() != 200){ + return ResultUtil.error("获取消息模板失败"); + } + JSONObject jsonObject = JSON.parseObject(httpResult.getData()); + Integer errcode = jsonObject.getInteger("errcode"); + if(0 != errcode){ + return ResultUtil.error(jsonObject.getString("errmsg")); + } + String priTmplId = jsonObject.getString("priTmplId"); + return ResultUtil.success(priTmplId); + }catch (Exception e){ + e.printStackTrace(); + return ResultUtil.runErr(); + } + + } + + + /** + * 删除模板 + * @param priTmplId 模板id + * @return + */ + public ResultUtil deltemplate(String priTmplId){ + try { + String accessToken = redisUtil.getValue("wxAppletsAccessToken"); + String url = "https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate?access_token=" + accessToken; + Map<String, Object> params = new HashMap<>(); + params.put("priTmplId", priTmplId); + HttpResult httpResult = httpClientUtil.pushHttpRequset("POST", url, params, null, "json"); + if(httpResult.getCode() != 200){ + return ResultUtil.error("获取消息模板失败"); + } + JSONObject jsonObject = JSON.parseObject(httpResult.getData()); + Integer errcode = jsonObject.getInteger("errcode"); + if(0 != errcode){ + return ResultUtil.error(jsonObject.getString("errmsg")); + } + return ResultUtil.success(); + }catch (Exception e){ + e.printStackTrace(); + return ResultUtil.runErr(); + } + } + + + /** + * 发送消息 + * @param template_id 所需下发的订阅模板id + * @param page 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转 + * @param touser 接收者(用户)的 openid + * @param data 模板内容,格式形如 { "key1": { "value": any }, "key2": { "value": any } }的object + * @param miniprogram_state 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版 + * @param lang 进入小程序查看”的语言类型,支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN + * @return + */ + public ResultUtil send(String template_id, String page, String touser, String data, String miniprogram_state, String lang){ + try { + String accessToken = redisUtil.getValue("wxAppletsAccessToken"); + String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken; + Map<String, Object> params = new HashMap<>(); + params.put("template_id", template_id); + params.put("page", page); + params.put("touser", touser); + params.put("data", JSON.parseObject(data)); + params.put("miniprogram_state", miniprogram_state); + params.put("lang", lang); + HttpResult httpResult = httpClientUtil.pushHttpRequset("POST", url, params, null, "json"); + if(httpResult.getCode() != 200){ + return ResultUtil.error("获取消息模板失败"); + } + JSONObject jsonObject = JSON.parseObject(httpResult.getData()); + Integer errcode = jsonObject.getInteger("errcode"); + if(0 != errcode){ + return ResultUtil.error(jsonObject.getString("errmsg")); + } + return ResultUtil.success(); + }catch (Exception e){ + e.printStackTrace(); + return ResultUtil.runErr(); + } + } + + +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCrypt.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCrypt.java new file mode 100644 index 0000000..5b606fa --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCrypt.java @@ -0,0 +1,288 @@ +/** + * 对公众平台发送给公众账号的消息加解密示例代码. + * + * @copyright Copyright (c) 1998-2014 Tencent Inc. + */ + +// ------------------------------------------------------------------------ + +/** + * 针对org.apache.commons.codec.binary.Base64, + * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) + * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi + */ +package com.supersavedriving.user.modular.system.util.weChat; + +import org.apache.commons.codec.binary.Base64; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.Random; + +/** + * 提供接收和推送给公众平台消息的加解密接口(UTF8编码的字符串). + * <ol> + * <li>第三方回复加密消息给公众平台</li> + * <li>第三方收到公众平台发送的消息,验证消息的安全性,并对消息进行解密。</li> + * </ol> + * 说明:异常java.security.InvalidKeyException:illegal Key Size的解决方案 + * <ol> + * <li>在官方网站下载JCE无限制权限策略文件(JDK7的下载地址: + * http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html</li> + * <li>下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li> + * <li>如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件</li> + * <li>如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件</li> + * </ol> + */ +public class WXBizMsgCrypt { + static Charset CHARSET = Charset.forName("utf-8"); + Base64 base64 = new Base64(); + byte[] aesKey; + String token; + String appId; + + /** + * 构造函数 + * @param token 公众平台上,开发者设置的token + * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey + * @param appId 公众平台appid + * + * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 + */ + public WXBizMsgCrypt(String token, String encodingAesKey, String appId) throws AesException { + if (encodingAesKey.length() != 43) { + throw new AesException(AesException.IllegalAesKey); + } + + this.token = token; + this.appId = appId; + aesKey = Base64.decodeBase64(encodingAesKey + "="); + } + + // 生成4个字节的网络字节序 + byte[] getNetworkBytesOrder(int sourceNumber) { + byte[] orderBytes = new byte[4]; + orderBytes[3] = (byte) (sourceNumber & 0xFF); + orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF); + orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF); + orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF); + return orderBytes; + } + + // 还原4个字节的网络字节序 + int recoverNetworkBytesOrder(byte[] orderBytes) { + int sourceNumber = 0; + for (int i = 0; i < 4; i++) { + sourceNumber <<= 8; + sourceNumber |= orderBytes[i] & 0xff; + } + return sourceNumber; + } + + // 随机生成16位字符串 + String getRandomStr() { + String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + Random random = new Random(); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < 16; i++) { + int number = random.nextInt(base.length()); + sb.append(base.charAt(number)); + } + return sb.toString(); + } + + /** + * 对明文进行加密. + * + * @param text 需要加密的明文 + * @return 加密后base64编码的字符串 + * @throws AesException aes加密失败 + */ + String encrypt(String randomStr, String text) throws AesException { + ByteGroup byteCollector = new ByteGroup(); + byte[] randomStrBytes = randomStr.getBytes(CHARSET); + byte[] textBytes = text.getBytes(CHARSET); + byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length); + byte[] appidBytes = appId.getBytes(CHARSET); + + // randomStr + networkBytesOrder + text + appid + byteCollector.addBytes(randomStrBytes); + byteCollector.addBytes(networkBytesOrder); + byteCollector.addBytes(textBytes); + byteCollector.addBytes(appidBytes); + + // ... + pad: 使用自定义的填充方式对明文进行补位填充 + byte[] padBytes = PKCS7Encoder.encode(byteCollector.size()); + byteCollector.addBytes(padBytes); + + // 获得最终的字节流, 未加密 + byte[] unencrypted = byteCollector.toBytes(); + + try { + // 设置加密模式为AES的CBC模式 + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); + + // 加密 + byte[] encrypted = cipher.doFinal(unencrypted); + + // 使用BASE64对加密后的字符串进行编码 + String base64Encrypted = base64.encodeToString(encrypted); + + return base64Encrypted; + } catch (Exception e) { + e.printStackTrace(); + throw new AesException(AesException.EncryptAESError); + } + } + + /** + * 对密文进行解密. + * + * @param text 需要解密的密文 + * @return 解密得到的明文 + * @throws AesException aes解密失败 + */ + String decrypt(String text) throws AesException { + byte[] original; + try { + // 设置解密模式为AES的CBC模式 + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16)); + cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); + + // 使用BASE64对密文进行解码 + byte[] encrypted = Base64.decodeBase64(text); + + // 解密 + original = cipher.doFinal(encrypted); + } catch (Exception e) { + e.printStackTrace(); + throw new AesException(AesException.DecryptAESError); + } + + String xmlContent, from_appid; + try { + // 去除补位字符 + byte[] bytes = PKCS7Encoder.decode(original); + + // 分离16位随机字符串,网络字节序和AppId + byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20); + + int xmlLength = recoverNetworkBytesOrder(networkOrder); + + xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET); + from_appid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), + CHARSET); + } catch (Exception e) { + e.printStackTrace(); + throw new AesException(AesException.IllegalBuffer); + } + + // appid不相同的情况 + if (!from_appid.equals(appId)) { + throw new AesException(AesException.ValidateAppidError); + } + return xmlContent; + + } + + /** + * 将公众平台回复用户的消息加密打包. + * <ol> + * <li>对要发送的消息进行AES-CBC加密</li> + * <li>生成安全签名</li> + * <li>将消息密文和安全签名打包成xml格式</li> + * </ol> + * + * @param replyMsg 公众平台待回复用户的消息,xml格式的字符串 + * @param timeStamp 时间戳,可以自己生成,也可以用URL参数的timestamp + * @param nonce 随机串,可以自己生成,也可以用URL参数的nonce + * + * @return 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串 + * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 + */ + public String encryptMsg(String replyMsg, String timeStamp, String nonce) throws AesException { + // 加密 + String encrypt = encrypt(getRandomStr(), replyMsg); + + // 生成安全签名 + if (timeStamp == "") { + timeStamp = Long.toString(System.currentTimeMillis()); + } + + String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt); + + // System.out.println("发送给平台的签名是: " + signature[1].toString()); + // 生成发送的xml + String result = XMLParse.generate(encrypt, signature, timeStamp, nonce); + return result; + } + + /** + * 检验消息的真实性,并且获取解密后的明文. + * <ol> + * <li>利用收到的密文生成安全签名,进行签名验证</li> + * <li>若验证通过,则提取xml中的加密消息</li> + * <li>对消息进行解密</li> + * </ol> + * + * @param msgSignature 签名串,对应URL参数的msg_signature + * @param timeStamp 时间戳,对应URL参数的timestamp + * @param nonce 随机串,对应URL参数的nonce + * @param postData 密文,对应POST请求的数据 + * + * @return 解密后的原文 + * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 + */ + public String decryptMsg(String msgSignature, String timeStamp, String nonce, String postData) + throws AesException { + + // 密钥,公众账号的app secret + // 提取密文 + Object[] encrypt = XMLParse.extract(postData); + + // 验证安全签名 + String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt[1].toString()); + + // 和URL中的签名比较是否相等 + // System.out.println("第三方收到URL中的签名:" + msg_sign); + // System.out.println("第三方校验签名:" + signature); + if (!signature.equals(msgSignature)) { + throw new AesException(AesException.ValidateSignatureError); + } + + // 解密 + String result = decrypt(encrypt[1].toString()); + return result; + } + + /** + * 验证URL + * @param msgSignature 签名串,对应URL参数的msg_signature + * @param timeStamp 时间戳,对应URL参数的timestamp + * @param nonce 随机串,对应URL参数的nonce + * @param echoStr 随机串,对应URL参数的echostr + * + * @return 解密之后的echostr + * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 + */ + public String verifyUrl(String msgSignature, String timeStamp, String nonce, String echoStr) + throws AesException { + String signature = SHA1.getSHA1(token, timeStamp, nonce, ""); + + if (!signature.equals(msgSignature)) { + throw new AesException(AesException.ValidateSignatureError); + } + + String result = decrypt(echoStr); + return result; + } + +} \ No newline at end of file diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCryptTest.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCryptTest.java new file mode 100644 index 0000000..e8331f6 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXBizMsgCryptTest.java @@ -0,0 +1,147 @@ +package com.supersavedriving.user.modular.system.util.weChat; + +import org.junit.*; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.StringReader; + +import static org.junit.Assert.*; + +public class WXBizMsgCryptTest { + String encodingAesKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG"; + String token = "pamtest"; + String timestamp = "1409304348"; + String nonce = "xxxxxx"; + String appId = "wxb11529c136998cb6"; + String replyMsg = "我是中文abcd123"; + String xmlFormat = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%1$s]]></Encrypt></xml>"; + String afterAesEncrypt = "jn1L23DB+6ELqJ+6bruv21Y6MD7KeIfP82D6gU39rmkgczbWwt5+3bnyg5K55bgVtVzd832WzZGMhkP72vVOfg=="; + String randomStr = "aaaabbbbccccdddd"; + + String replyMsg2 = "<xml><ToUserName><![CDATA[oia2Tj我是中文jewbmiOUlr6X-1crbLOvLw]]></ToUserName><FromUserName><![CDATA[gh_7f083739789a]]></FromUserName><CreateTime>1407743423</CreateTime><MsgType><![CDATA[video]]></MsgType><Video><MediaId><![CDATA[eYJ1MbwPRJtOvIEabaxHs7TX2D-HV71s79GUxqdUkjm6Gs2Ed1KF3ulAOA9H1xG0]]></MediaId><Title><![CDATA[testCallBackReplyVideo]]></Title><Description><![CDATA[testCallBackReplyVideo]]></Description></Video></xml>"; + String afterAesEncrypt2 = "jn1L23DB+6ELqJ+6bruv23M2GmYfkv0xBh2h+XTBOKVKcgDFHle6gqcZ1cZrk3e1qjPQ1F4RsLWzQRG9udbKWesxlkupqcEcW7ZQweImX9+wLMa0GaUzpkycA8+IamDBxn5loLgZpnS7fVAbExOkK5DYHBmv5tptA9tklE/fTIILHR8HLXa5nQvFb3tYPKAlHF3rtTeayNf0QuM+UW/wM9enGIDIJHF7CLHiDNAYxr+r+OrJCmPQyTy8cVWlu9iSvOHPT/77bZqJucQHQ04sq7KZI27OcqpQNSto2OdHCoTccjggX5Z9Mma0nMJBU+jLKJ38YB1fBIz+vBzsYjrTmFQ44YfeEuZ+xRTQwr92vhA9OxchWVINGC50qE/6lmkwWTwGX9wtQpsJKhP+oS7rvTY8+VdzETdfakjkwQ5/Xka042OlUb1/slTwo4RscuQ+RdxSGvDahxAJ6+EAjLt9d8igHngxIbf6YyqqROxuxqIeIch3CssH/LqRs+iAcILvApYZckqmA7FNERspKA5f8GoJ9sv8xmGvZ9Yrf57cExWtnX8aCMMaBropU/1k+hKP5LVdzbWCG0hGwx/dQudYR/eXp3P0XxjlFiy+9DMlaFExWUZQDajPkdPrEeOwofJb"; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + } + + @Before + public void setUp() throws Exception { + + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testNormal() throws ParserConfigurationException, SAXException, IOException { + try { + WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId); + String afterEncrpt = pc.encryptMsg(replyMsg, timestamp, nonce); + + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + StringReader sr = new StringReader(afterEncrpt); + InputSource is = new InputSource(sr); + Document document = db.parse(is); + + Element root = document.getDocumentElement(); + NodeList nodelist1 = root.getElementsByTagName("Encrypt"); + NodeList nodelist2 = root.getElementsByTagName("MsgSignature"); + + String encrypt = nodelist1.item(0).getTextContent(); + String msgSignature = nodelist2.item(0).getTextContent(); + String fromXML = String.format(xmlFormat, encrypt); + + // 第三方收到公众号平台发送的消息 + String afterDecrpt = pc.decryptMsg(msgSignature, timestamp, nonce, fromXML); + assertEquals(replyMsg, afterDecrpt); + } catch (AesException e) { + fail("正常流程,怎么就抛出异常了??????"); + } + } + + @Test + public void testAesEncrypt() { + try { + WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId); + assertEquals(afterAesEncrypt, pc.encrypt(randomStr, replyMsg)); + } catch (AesException e) { + e.printStackTrace(); + fail("no异常"); + } + } + + @Test + public void testAesEncrypt2() { + try { + WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId); + assertEquals(afterAesEncrypt2, pc.encrypt(randomStr, replyMsg2)); + + } catch (AesException e) { + e.printStackTrace(); + fail("no异常"); + } + } + + @Test + public void testIllegalAesKey() { + try { + new WXBizMsgCrypt(token, "abcde", appId); + } catch (AesException e) { + assertEquals(AesException.IllegalAesKey, e.getCode()); + return; + } + fail("错误流程不抛出异常???"); + } + + @Test + public void testValidateSignatureError() throws ParserConfigurationException, SAXException, + IOException { + try { + WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId); + String afterEncrpt = pc.encryptMsg(replyMsg, timestamp, nonce); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + StringReader sr = new StringReader(afterEncrpt); + InputSource is = new InputSource(sr); + Document document = db.parse(is); + + Element root = document.getDocumentElement(); + NodeList nodelist1 = root.getElementsByTagName("Encrypt"); + + String encrypt = nodelist1.item(0).getTextContent(); + String fromXML = String.format(xmlFormat, encrypt); + pc.decryptMsg("12345", timestamp, nonce, fromXML); // 这里签名错误 + } catch (AesException e) { + assertEquals(AesException.ValidateSignatureError, e.getCode()); + return; + } + fail("错误流程不抛出异常???"); + } + + @Test + public void testVerifyUrl() throws AesException { + WXBizMsgCrypt wxcpt = new WXBizMsgCrypt("QDG6eK", + "jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C", "wx5823bf96d3bd56c7"); + String verifyMsgSig = "5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3"; + String timeStamp = "1409659589"; + String nonce = "263014780"; + String echoStr = "P9nAzCzyDtyTWESHep1vC5X9xho/qYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp+4RPcs8TgAE7OaBO+FZXvnaqQ=="; + wxcpt.verifyUrl(verifyMsgSig, timeStamp, nonce, echoStr); + // 只要不抛出异常就好 + } +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXCore.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXCore.java new file mode 100644 index 0000000..d673c1a --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WXCore.java @@ -0,0 +1,52 @@ +package com.supersavedriving.user.modular.system.util.weChat; + +import org.apache.commons.codec.binary.Base64; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; + +public class WXCore { + + private static Logger logger = LoggerFactory.getLogger(WXCore.class); + + private static final String WATERMARK = "watermark"; + + @Value("${wx.appletsAppid}") + private static String appid ; + + + + /** + * 解密数据 + * @return + * @throws Exception + */ + public static String decrypt(String encryptedData, String sessionKey, String iv){ + String result = ""; + try { + AES aes = new AES(); + byte[] resultByte = aes.decrypt(Base64.decodeBase64(encryptedData), Base64.decodeBase64(sessionKey), Base64.decodeBase64(iv)); + if(null != resultByte && resultByte.length > 0){ + result = new String(WxPKCS7Encoder.decode(resultByte), "UTF-8"); +// JSONObject jsonObject = JSON.parseObject(result); +// String decryptAppid = jsonObject.getJSONObject(WATERMARK).getString("appid"); +// if(!appid.equals(decryptAppid)){ +// result = ""; +// } + } + } catch (Exception e) { + result = ""; + e.printStackTrace(); + } + return result; + } + + + public static void main(String[] args) throws Exception{ + String appId = "wx4f4bc4dec97d474b"; + String encryptedData = "ajhisfk0EhWCBcoxt/5QJA3bKPTygQXANbCSev92MyqIqGxRhzZFS1SlQrIWAo3tx8YfsB+VlScZNOlRyfnXKqPUl9h+PDeKsTkTst9V4pq4mUbI+r3AautXBffVk/rpyjMfanVsWeOlxkupdv2U9U4BrueR/Rak+TCmHTWUUf8eDLoi6nioL/Pft/rYaO3JD54hgpcY0Ef/k7Boyap4E0/uKVowY1ANO7KVydSXE6S7OKzXuzmDTbV38v+7ryMHtglUzoKzF1gL8y9OZRwkaesfHN1kfItjpsAibjSgkIiiW6ZEHLT2n1UDkJtfUqg63je2cJAH7gzeUm9TCDDftdhLU+NuPk3j/LXyFQ05pJ8B19+kIXF3dcmHOH7lFvi7yGmVuAD/9AnetGWgId3TZDS/OjbiVUM30RogeziAw98VpAyAAq1r2ULFwi8e928m"; + String sessionKey = "CdxFGwXIluQFZ+qD+NSFKQ=="; + String iv = "3FD8r1Spwlf7LG8YEq41+Q=="; + logger.debug(decrypt(encryptedData, sessionKey, iv)); + } +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WeChatUtil.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WeChatUtil.java new file mode 100644 index 0000000..c23f490 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WeChatUtil.java @@ -0,0 +1,415 @@ +package com.supersavedriving.user.modular.system.util.weChat; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.supersavedriving.user.core.util.ToolUtil; +import com.supersavedriving.user.modular.system.util.RedisUtil; +import com.supersavedriving.user.modular.system.util.UUIDUtil; +import com.supersavedriving.user.modular.system.util.httpClinet.HttpClientUtil; +import com.supersavedriving.user.modular.system.util.httpClinet.HttpResult; +import com.supersavedriving.user.modular.system.util.weChat.model.Code2Session; +import org.apache.commons.codec.digest.DigestUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.*; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; + +/** + * 微信工具类 + */ +@Component +public class WeChatUtil { + + private static Logger logger = LoggerFactory.getLogger(WeChatUtil.class); + + @Value("${wx.appletsAppid}") + private String wxAppletsAppid; + + @Value("${wx.appletsAppSecret}") + private String wxAppletsAppSecret; + + @Value("${wx.officialAccountAppid}") + private String officialAccountAppid; + + @Value("{wx.officialAccountAppSecret}") + private String officialAccountAppSecret; + + @Value("${wx.appid}") + private String webAppId; + + @Value("${wx.appSecret}") + private String webAppSecret; + + @Autowired + private RestTemplate restTemplate; + + @Autowired + private RedisUtil redisUtil; + + + + { + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(30000); + new Timer().schedule(new TimerTask() { + @Override + public void run() { + try { + String wxAppletsAccessToken = getWxAppletsAccessToken(); + if(ToolUtil.isEmpty(wxAppletsAccessToken)){ + System.err.println("获取微信小程序access_token失败"); + return; + } + redisUtil.setStrValue("wxAppletsAccessToken", wxAppletsAccessToken, 7000); + }catch (Exception e){ + e.printStackTrace(); + } + } + }, 0, 7000000); + }catch (Exception e){ + e.printStackTrace(); + } + } + }).start(); + + } + + + /** + * 小程序使用jscode获取openid + * @param jscode + * @return + */ + public Code2Session code2Session(String jscode) throws Exception{ + String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + wxAppletsAppid + "&secret=" + wxAppletsAppSecret + + "&js_code=" + jscode + "&grant_type=authorization_code"; + HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", url, null, null, "form"); + if(null == httpResult || httpResult.getCode() != 200){ + return null; + } + Code2Session code2Session = JSON.parseObject(httpResult.getData(), Code2Session.class); + return code2Session; + } + + + /** + * 获取微信小程序token + * @return + */ + public String getWxAppletsAccessToken() throws Exception{ + String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + wxAppletsAppid + "&secret=" + wxAppletsAppSecret; + HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", url, null, null, "form"); + if(httpResult.getCode() != 200){ + return ""; + } + JSONObject jsonObject = JSON.parseObject(httpResult.getData()); + return jsonObject.getString("access_token"); + } + + + /*** + * 获取jsapiTicket(小程序) + * 来源 www.vxzsk.com + * @return + */ + public String getWxAppletsJSApiTicket() throws Exception{ + String wxAppletsAccessToken = redisUtil.getValue("wxAppletsAccessToken"); + String urlStr = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + wxAppletsAccessToken + "&type=jsapi"; + HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", urlStr, null, null, "form"); + if(httpResult.getCode() != 200){ + return null; + } + logger.debug(httpResult.getData()); + String ticket = JSONObject.parseObject(httpResult.getData()).getString("ticket"); + return ticket; + } + + /** + * 通过config接口注入权限验证配置(小程序) + * 附录1-JS-SDK使用权限签名算法, + * @return + */ + public Map<String,Object> getWxAppletsSignatureConfig(String url) throws Exception{ + //获取token + try { + url = URLDecoder.decode(url, "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + String ticket = getWxAppletsJSApiTicket(); + String noncestr = UUIDUtil.getRandomCode(); + Long timestamp = System.currentTimeMillis(); + String content = "jsapi_ticket=" + ticket + "&noncestr=" + noncestr + "×tamp=" + timestamp + "&url=" + url; + String signature = DigestUtils.sha1Hex(content); + Map<String,Object> map=new HashMap<>(); + map.put("appId", wxAppletsAppid); + map.put("timestamp", timestamp); + map.put("nonceStr", noncestr); + map.put("signature", signature); + return map; + } + + + + /** + * 网站应用登录 + * @param code + * @return + */ + public Map<String, String> webAccessToken(String code) throws Exception{ + String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + webAppId + "&secret=" + webAppSecret + "&code=" + code + "&grant_type=authorization_code"; + HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", url, null, null, "form"); + if(httpResult.getCode() != 200){ + return null; + } + JSONObject jsonObject = JSON.parseObject(httpResult.getData()); + int errcode = jsonObject.getIntValue("errcode"); + Map<String, String> map = new HashMap<>(); + if(errcode == 0){//成功 + map.put("access_token", jsonObject.getString("access_token")); + map.put("openid", jsonObject.getString("openid")); + map.put("refresh_token", jsonObject.getString("refresh_token")); + map.put("unionid", jsonObject.getString("unionid")); + return map; + } + if(errcode == -1){//系统繁忙,此时请开发者稍候再试 + map.put("msg", jsonObject.getString("errmsg")); + return map; + } + if(errcode == 40029){//code 无效 + map.put("msg", jsonObject.getString("errmsg")); + return map; + } + if(errcode == 45011){//频率限制,每个用户每分钟100次 + map.put("msg", jsonObject.getString("errmsg")); + return map; + } + return map; + } + + + /** + * 获取微信个人信息 + * @param access_token + * @param openid + * @return + */ + public Map<String, Object> getUserInfo(String access_token, String openid) throws Exception{ + String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + access_token + "&openid=" + openid; + HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", url, null, null, "form"); + if(httpResult.getCode() != 200){ + return null; + } + JSONObject jsonObject = JSON.parseObject(httpResult.getData()); + int errcode = jsonObject.getIntValue("errcode"); + Map<String, Object> map = new HashMap<>(); + if(errcode == 0){//成功 + map.put("nickname", jsonObject.getString("nickname")); + map.put("openid", jsonObject.getString("openid")); + map.put("sex", jsonObject.getString("sex")); + map.put("headimgurl", jsonObject.getString("headimgurl")); + return map; + } + if(errcode == -1){//系统繁忙,此时请开发者稍候再试 + map.put("msg", jsonObject.getString("errmsg")); + return map; + } + if(errcode == 40029){//code 无效 + map.put("msg", jsonObject.getString("errmsg")); + return map; + } + if(errcode == 45011){//频率限制,每个用户每分钟100次 + map.put("msg", jsonObject.getString("errmsg")); + return map; + } + return map; + } + + + /** + * 公众号获取openid + * @param code + * @return + */ + public Map<String,Object> getOpenId(String code) throws Exception{ + if (code == null || code.length() == 0) { + return null; + } + String grantType = "authorization_code"; + String params = "appid=" + officialAccountAppid + "&secret=" + officialAccountAppSecret + "&code=" + code + "&grant_type=" + grantType; + logger.debug("sssss"+params); + HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", "https://api.weixin.qq.com/sns/oauth2/access_token?" + params, null, null, "form"); + if(httpResult.getCode() != 200){ + return null; + } + JSONObject json = JSON.parseObject(httpResult.getData()); + logger.debug(json.toJSONString()); + String openId = json.get("openid").toString(); + String accessToken = json.get("access_token").toString(); + Integer expiresIn = json.getInteger("expires_in"); + String refresh_token = json.getString("refresh_token"); + String unionid = json.getString("unionid"); + Map<String,Object> map=new HashMap<>(); + map.put("openId",openId); + map.put("accessToken",accessToken); + map.put("expiresIn", expiresIn); + map.put("refreshToken", refresh_token); + map.put("unionid", unionid); + return map; + } + + + /*** + * 获取acess_token (公众号) + * 来源www.vxzsk.com + * @return + */ + public String getAccessToken() throws Exception{ + String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + officialAccountAppid + + "&secret=" + officialAccountAppSecret; + HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", url, null, null, "form"); + if(httpResult.getCode() != 200){ + return null; + } + String accessToken = JSONObject.parseObject(httpResult.getData()).getString("access_token"); + return accessToken; + } + + /*** + * 获取jsapiTicket(公众号) + * 来源 www.vxzsk.com + * @return + */ + public String getJSApiTicket() throws Exception{ + //获取token + String acess_token = redisUtil.getValue("acess_token"); + String urlStr = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + acess_token + "&type=jsapi"; + HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", urlStr, null, null, "form"); + if(httpResult.getCode() != 200){ + return null; + } + logger.debug(httpResult.getData()); + String ticket = JSONObject.parseObject(httpResult.getData()).getString("ticket"); + return ticket; + } + + /** + * 通过config接口注入权限验证配置(公众号) + * 附录1-JS-SDK使用权限签名算法, + * @return + */ + public Map<String,Object> getSignatureConfig(String url) throws Exception{ + //获取token + try { + url = URLDecoder.decode(url, "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + String ticket = getJSApiTicket(); + String noncestr = UUIDUtil.getRandomCode(); + Long timestamp = System.currentTimeMillis(); + String content = "jsapi_ticket=" + ticket + "&noncestr=" + noncestr + "×tamp=" + timestamp + "&url=" + url; + String signature = DigestUtils.sha1Hex(content); + Map<String,Object> map=new HashMap<>(); + map.put("appId", officialAccountAppid); + map.put("timestamp", timestamp); + map.put("nonceStr", noncestr); + map.put("signature", signature); + return map; + } + + + /** + * 公众号获取用户个人信息 + * @param access_token + * @param openid + * @return + */ + public Map<String, Object> queryUserInfo(String access_token, String openid) throws Exception{ + String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + access_token + "&openid=" + openid + "&lang=zh_CN"; + HttpResult httpResult = HttpClientUtil.pushHttpRequset("GET", url, null, null, "form"); + if(httpResult.getCode() != 200){ + return null; + } + logger.debug(httpResult.getData()); + JSONObject j = JSON.parseObject(httpResult.getData()); + Map<String, Object> map = new HashMap<>(); + map.put("nickname", j.getString("nickname")); + map.put("sex", j.getInteger("sex")); + map.put("headimgurl", j.getString("headimgurl")); + map.put("unionid", j.getString("unionid")); + return map; + } + + + /** + * 获取小程序二维码 + * @param page 跳转页 例如 pages/index/index + * @param scene 参数 a=1&b=2 + */ + public InputStream getwxacodeunlimit(String page, String scene, String envVersion) throws Exception{ + try { + String wxAppletsAccessToken = redisUtil.getValue("wxAppletsAccessToken"); + String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + wxAppletsAccessToken; + Map<String, Object> param = new HashMap<>(); + param.put("scene", scene); + param.put("page", page); + param.put("env_version", envVersion); + HttpHeaders httpHeaders = new HttpHeaders(); + MediaType type=MediaType.parseMediaType("application/json;charset=UTF-8"); + httpHeaders.setContentType(type); + HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(param, httpHeaders); + ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class, new Object[0]); + String body1 = exchange.getBody(); +// System.err.println(body1); + ResponseEntity<byte[]> entity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, byte[].class, new Object[0]); + byte[] body = entity.getBody(); +// System.err.println(Base64.encodeBase64String(body)); + return new ByteArrayInputStream(body); + }catch (Exception e){ + e.printStackTrace(); + } + return null; + } + + + /** + * 获取小程序urlscheme码 + * @return + */ + public String getUrlscheme(String path) throws Exception{ + String wxAppletsAccessToken = redisUtil.getValue("wxAppletsAccessToken"); + String url = "https://api.weixin.qq.com/wxa/generatescheme?access_token=" + wxAppletsAccessToken; + Map<String, Object> param = new HashMap<>(); + param.put("is_expire", true); + param.put("expire_type", 1); + param.put("expire_interval", 180); + Map<String, Object> map1 = new HashMap<>(); + map1.put("path", path); + map1.put("query", ""); + map1.put("env_version", "release"); + param.put("jump_wxa", map1); + HttpHeaders httpHeaders = new HttpHeaders(); + MediaType type=MediaType.parseMediaType("application/json;charset=UTF-8"); + httpHeaders.setContentType(type); + HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(param, httpHeaders); + ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); + String body1 = exchange.getBody(); + return body1; + } +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WxPKCS7Encoder.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WxPKCS7Encoder.java new file mode 100644 index 0000000..8524463 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/WxPKCS7Encoder.java @@ -0,0 +1,63 @@ +package com.supersavedriving.user.modular.system.util.weChat; + +import java.nio.charset.Charset; +import java.util.Arrays; + +/** +* 微信小程序加解密 +* @author pzb +* @Date 2021/12/3 15:43 +*/ +public class WxPKCS7Encoder { + private static final Charset CHARSET = Charset.forName("utf-8"); + private static final int BLOCK_SIZE = 32; + + /** + * 获得对明文进行补位填充的字节. + * + * @param count + * 需要进行填充补位操作的明文字节个数 + * @return 补齐用的字节数组 + */ + public static byte[] encode(int count) { + // 计算需要填充的位数 + int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); + if (amountToPad == 0) { + amountToPad = BLOCK_SIZE; + } + // 获得补位所用的字符 + char padChr = chr(amountToPad); + String tmp = new String(); + for (int index = 0; index < amountToPad; index++) { + tmp += padChr; + } + return tmp.getBytes(CHARSET); + } + + /** + * 删除解密后明文的补位字符 + * + * @param decrypted + * 解密后的明文 + * @return 删除补位字符后的明文 + */ + public static byte[] decode(byte[] decrypted) { + int pad = decrypted[decrypted.length - 1]; + if (pad < 1 || pad > 32) { + pad = 0; + } + return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); + } + + /** + * 将数字转化成ASCII码对应的字符,用于对明文进行补码 + * + * @param a + * 需要转化的数字 + * @return 转化得到的字符 + */ + public static char chr(int a) { + byte target = (byte) (a & 0xFF); + return (char) target; + } +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/XMLParse.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/XMLParse.java new file mode 100644 index 0000000..61dedfd --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/XMLParse.java @@ -0,0 +1,71 @@ +/** + * 对公众平台发送给公众账号的消息加解密示例代码. + * + * @copyright Copyright (c) 1998-2014 Tencent Inc. + */ + +// ------------------------------------------------------------------------ + +package com.supersavedriving.user.modular.system.util.weChat; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.StringReader; + +/** + * XMLParse class + * + * 提供提取消息格式中的密文及生成回复消息格式的接口. + */ +class XMLParse { + + /** + * 提取出xml数据包中的加密消息 + * @param xmltext 待提取的xml字符串 + * @return 提取出的加密消息字符串 + * @throws AesException + */ + public static Object[] extract(String xmltext) throws AesException { + Object[] result = new Object[3]; + try { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + StringReader sr = new StringReader(xmltext); + InputSource is = new InputSource(sr); + Document document = db.parse(is); + + Element root = document.getDocumentElement(); + NodeList nodelist1 = root.getElementsByTagName("Encrypt"); + NodeList nodelist2 = root.getElementsByTagName("ToUserName"); + result[0] = 0; + result[1] = nodelist1.item(0).getTextContent(); + result[2] = nodelist2.item(0).getTextContent(); + return result; + } catch (Exception e) { + e.printStackTrace(); + throw new AesException(AesException.ParseXmlError); + } + } + + /** + * 生成xml消息 + * @param encrypt 加密后的消息密文 + * @param signature 安全签名 + * @param timestamp 时间戳 + * @param nonce 随机字符串 + * @return 生成的xml字符串 + */ + public static String generate(String encrypt, String signature, String timestamp, String nonce) { + + String format = "<xml>\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n" + + "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n" + + "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n" + "</xml>"; + return String.format(format, encrypt, signature, timestamp, nonce); + + } +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Category.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Category.java new file mode 100644 index 0000000..61a2eac --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Category.java @@ -0,0 +1,18 @@ +package com.supersavedriving.user.modular.system.util.weChat.model; + +import lombok.Data; + +/** + * 订阅消息类目 + */ +@Data +public class Category { + /** + * 类目id + */ + private String id; + /** + * 类目名称 + */ + private String name; +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Code2Session.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Code2Session.java new file mode 100644 index 0000000..c5e8b80 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/Code2Session.java @@ -0,0 +1,33 @@ +package com.supersavedriving.user.modular.system.util.weChat.model; + +import lombok.Data; + +/** + * TODO + * + * @author 39373 + * @date 2023/2/26 19:42 + */ +@Data +public class Code2Session { + /** + * 状态码(-1=系统繁忙,0=成功,40029=code无效,45011=频率限制,每个用户每分钟100次,40226=高风险等级用户,小程序登录拦截 ) + */ + private Integer errcode; + /** + * 状态说明 + */ + private String errmsg; + /** + * openid + */ + private String openid; + /** + * sessionKey + */ + private String session_key; + /** + * unionid + */ + private String unionid; +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/KeywordEnum.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/KeywordEnum.java new file mode 100644 index 0000000..7a15e62 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/KeywordEnum.java @@ -0,0 +1,17 @@ +package com.supersavedriving.user.modular.system.util.weChat.model; + +import lombok.Data; + +import java.util.List; + +@Data +public class KeywordEnum { + /** + * 枚举参数的 key + */ + private String keywordCode; + /** + * 枚举参数值范围列表 + */ + private List<String> enumValueList; +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/MessageTemplate.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/MessageTemplate.java new file mode 100644 index 0000000..badf006 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/MessageTemplate.java @@ -0,0 +1,36 @@ +package com.supersavedriving.user.modular.system.util.weChat.model; + +import lombok.Data; + +import java.util.List; + +/** + * 消息模板 + */ +@Data +public class MessageTemplate { + /** + * 添加至帐号下的模板 id,发送小程序订阅消息时所需 + */ + private String priTmplId; + /** + * 模版标题 + */ + private String title; + /** + * 模版内容 + */ + private String content; + /** + * 模板内容示例 + */ + private String example; + /** + * 模版类型,2 为一次性订阅,3 为长期订阅 + */ + private Integer type; + /** + * 枚举参数值范围 + */ + private List<KeywordEnum> keywordEnumValueList; +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplateKeywords.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplateKeywords.java new file mode 100644 index 0000000..5e9f9ef --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplateKeywords.java @@ -0,0 +1,26 @@ +package com.supersavedriving.user.modular.system.util.weChat.model; + +import lombok.Data; + +/** + * 公共消息模板关键字 + */ +@Data +public class PubTemplateKeywords { + /** + * 关键词 id,选用模板时需要 + */ + private Integer kid; + /** + * 关键词内容 + */ + private String name; + /** + * 关键词内容对应的示例 + */ + private String example; + /** + * 参数类型 + */ + private String rule; +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplatetitles.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplatetitles.java new file mode 100644 index 0000000..4eb82e8 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/util/weChat/model/PubTemplatetitles.java @@ -0,0 +1,26 @@ +package com.supersavedriving.user.modular.system.util.weChat.model; + +import lombok.Data; + +/** + * 功能模板 + */ +@Data +public class PubTemplatetitles { + /** + * 模版标题 id + */ + private Integer tid; + /** + * 模版标题 + */ + private String title; + /** + * 模版类型,2 为一次性订阅,3 为长期订阅 + */ + private Integer type; + /** + * 模版所属类目 id + */ + private Integer categoryId; +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/ResponseWarpper.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/ResponseWarpper.java new file mode 100644 index 0000000..7f7d595 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/ResponseWarpper.java @@ -0,0 +1,49 @@ +package com.supersavedriving.user.modular.system.warpper; + +import com.supersavedriving.user.modular.system.util.ResultUtil; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** +* 统一返回实体 +* @author pzb +* @Date 2023/2/3 10:46 +*/ +@Data +@ApiModel +public class ResponseWarpper<T> { + @ApiModelProperty(value = "返回状态码(200/300/400/500)", required = true, dataType = "int") + private Integer code; + @ApiModelProperty(value = "结果备注", required = true, dataType = "string") + private String message; + @ApiModelProperty(value = "结果集对象", required = false) + private ResultUtil<T> resultUtil; + + public ResponseWarpper() { + } + + public ResponseWarpper(Integer code, String message) { + this.code = code; + this.message = message; + this.resultUtil = ResultUtil.success(); + } + + public ResponseWarpper(Integer code, String message, ResultUtil<T> resultUtil) { + this.code = code; + this.message = message; + this.resultUtil = resultUtil; + } + + public static ResponseWarpper success() { + return new ResponseWarpper(200, "success", ResultUtil.success()); + } + + public static <T> ResponseWarpper<T> success(T data) { + return new ResponseWarpper(200, "success", ResultUtil.success(data)); + } + + public static <T> ResponseWarpper<T> success(ResultUtil<T> resultUtil) { + return new ResponseWarpper(200, "success", resultUtil); + } +} diff --git a/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/SignInToRegister.java b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/SignInToRegister.java new file mode 100644 index 0000000..1e4f337 --- /dev/null +++ b/user/guns-admin/src/main/java/com/supersavedriving/user/modular/system/warpper/SignInToRegister.java @@ -0,0 +1,23 @@ +package com.supersavedriving.user.modular.system.warpper; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 登录 + * @author 39373 + * @date 2023/2/26 21:21 + */ +@Data +public class SignInToRegister { + @ApiModelProperty(value = "微信jscode", required = true, dataType = "string") + private String jscode; + @ApiModelProperty(value = "手机加密数据", required = true, dataType = "string") + private String encryptedDataPhone; + @ApiModelProperty(value = "手机号码解码iv", required = true, dataType = "string") + private String ivPhone; + @ApiModelProperty(value = "邀约人类型(1=用户,2=司机)", required = false, dataType = "int") + private Integer inviterType; + @ApiModelProperty(value = "邀约人id", required = false, dataType = "int") + private Integer inviterId; +} -- Gitblit v1.7.1