| | |
| | | </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> |
| | |
| | | <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> |
| | |
| | | <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> |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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 ""; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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()); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | 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()); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | 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> { |
| | | } |
New file |
| | |
| | | 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> { |
| | | } |
New file |
| | |
| | | <?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> |
New file |
| | |
| | | <?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> |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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> { |
| | | } |
New file |
| | |
| | | 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); |
| | | } |
| | | } |
New file |
| | |
| | | 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 { |
| | | } |
| | |
| | | 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.*; |
| | | |
| | | |
| | | /** |
| | |
| | | public class RedisUtil { |
| | | |
| | | @Autowired |
| | | private RestTemplate internalRestTemplate; |
| | | private JedisPool jedisPool; |
| | | |
| | | private Timer timer; |
| | | |
| | | |
| | | /** |
| | |
| | | * @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); |
| | | } |
| | | |
| | | } |
| | | |
| | | |
| | |
| | | * @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); |
| | | } |
| | | } |
| | | |
| | |
| | | */ |
| | | 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"); |
| | | } |
| | | } |
| | |
| | | package com.supersavedriving.user.modular.system.util; |
| | | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | |
| | |
| | | @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 = "返回结果说明") |
| | |
| | | * @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); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @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()); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @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()); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @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()); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @return |
| | | */ |
| | | public static ResultUtil runErr(){ |
| | | return ResultUtil.getResult(ResultUtil.RUNTIME_ERROR, "SYSTEM_RUN_ERROR", new JSONObject()); |
| | | return ResultUtil.getResult(ResultUtil.RUNTIME_ERROR, "系统运行异常", new Object()); |
| | | } |
| | | |
| | | |
| | |
| | | * 运行异常 |
| | | * @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); |
| | | } |
| | | |
| | |
| | | * @return |
| | | */ |
| | | public static ResultUtil success(){ |
| | | return ResultUtil.getResult(ResultUtil.SUCCESS, "SUCCESS", new JSONObject()); |
| | | return ResultUtil.getResult(ResultUtil.SUCCESS, "成功", new Object()); |
| | | } |
| | | |
| | | |
| | |
| | | * @return |
| | | */ |
| | | public static <T> ResultUtil<T> success(T data){ |
| | | return ResultUtil.getResult(ResultUtil.SUCCESS, "SUCCESS", data); |
| | | return ResultUtil.getResult(ResultUtil.SUCCESS, "成功", data); |
| | | } |
| | | |
| | | /** |
New file |
| | |
| | | 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(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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(); |
| | | } |
| | | } |
New file |
| | |
| | | /** |
| | | * 对公众平台发送给公众账号的消息加解密示例代码. |
| | | * |
| | | * @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; |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | /** |
| | | * 对公众平台发送给公众账号的消息加解密示例代码. |
| | | * |
| | | * @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); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | 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(); |
| | | } |
| | | } |
| | | |
| | | |
| | | } |
New file |
| | |
| | | /** |
| | | * 对公众平台发送给公众账号的消息加解密示例代码. |
| | | * |
| | | * @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; |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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); |
| | | // 只要不抛出异常就好 |
| | | } |
| | | } |
New file |
| | |
| | | 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)); |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | /** |
| | | * 对公众平台发送给公众账号的消息加解密示例代码. |
| | | * |
| | | * @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); |
| | | |
| | | } |
| | | } |
New file |
| | |
| | | package com.supersavedriving.user.modular.system.util.weChat.model; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 订阅消息类目 |
| | | */ |
| | | @Data |
| | | public class Category { |
| | | /** |
| | | * 类目id |
| | | */ |
| | | private String id; |
| | | /** |
| | | * 类目名称 |
| | | */ |
| | | private String name; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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); |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |