微信公众号与小程序接口调试

  • 目录
    • 微信
      • 微信公众号
        • h5拦截器
      • 微信小程序
        • 小程序拦截器

目录

主要说明微信平台接口对接的记录文档与Java拦截器得使用。

微信

前置条件

微信公众平台获取唯一id:Appid,秘钥:secret,配置在application.yml文件中,并且加入所需jar包

    <dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk16</artifactId><version>1.46</version></dependency><dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.4</version></dependency><dependency><groupId>net.sf.json-lib</groupId><artifactId>json-lib</artifactId><version>2.2.3</version><classifier>jdk15</classifier></dependency>

接口调用器 RestTemplate


配置spring带的RestTemplate来调用Api

@Autowired
private RestTemplate restTemplate;

微信小程序获取jscode:
(1.uni-app框架搭建得小程序调用uni.login()方法获取code。(参考链接)
(2.微信小程序框架调用wx.login()方法获取code。(参考链接)

微信公众号获取jscode:
在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中的snsapi_base和snsapi_userinfo),引导关注者打开如下页面:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

若提示“该链接无法访问”,请检查参数是否填写错误,是否拥有scope参数对应的授权作用域权限。

尤其注意:由于授权操作安全等级较高,所以在发起授权请求时,微信会对授权链接做正则强匹配校验,如果链接的参数顺序不对,授权页面将无法正常访问

其中:
AppID- 公众号的唯一标识
REDIRECT_URI- 跳转url
SCOPE -值为snsapi_base(不弹出授权页面,直接跳转,只能获取用户openid) 或snsapi_userinfo (弹 出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息)
STATE- 开发者可以自定义填写a-zA-Z0-9的参数值

详情请访问微信官方文档


微信公众号

  1. 获取openId ,在获取到微信开发者平台的AppId,secret秘钥,与微信用户认证的jsCode;(参考链接)

    备注:jscode是由前端用户访问地址时带过来的

 //api调用工具类@Autowiredprivate RestTemplate restTemplate;/*** 微信小程序api接口调用** @param weChatDto 微信用户授权信息* @return 返回获取了session_key的weChat对象*/public WeChatDto appletsLogin(WeChatDto weChatDto) {String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={appid}&secret={secret}&code={code}&grant_type=authorization_code";Map<String, String> requestMap = new HashMap<>();requestMap.put("appid", weChatConfig.getAppid());requestMap.put("secret", weChatConfig.getSecret());requestMap.put("code", weChatDto.getJsCode());ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class, requestMap);JSONObject jsonObject = JSONObject.parseObject(responseEntity.getBody());String openId = jsonObject.getString("openid");if (StringUtils.isEmpty(openId)) {throw new CustomException(jsonObject.toString(), Integer.valueOf(jsonObject.getString("errcode")));}//String accessToken = jsonObject.getString("access_token");暂时不用//自定义方法用openId和session_key换取tokenWeChatDto weChatDto1 = new WeChatDto();weChatDto1.setOpenId(openId);weChatDto1.setAppId(weChatConfig.getAppid());return weChatDto1;}

通过appid,secret,code,grant_type获取到微信返回的数据后使用com.alibaba.fastjson.JSONObject.parseObject()解析成json,通过key/value形式读取数据并返回关键数据


  1. 获取access_token,token分为网页请求token与基础token,微信开发者平台需要将Ip和域名加入白名单;(参考链接)
public WeChatDto userInfo(String openId, String accessToken, String lang) {//初始化修改restTemplate编码StringHttpMessageConverter stringHttpMessageConver = new StringHttpMessageConverter(Charset.forName("UTF-8"));restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));//通过微信授权获取基础accessTokenString userInfoUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}";Map<String, String> userInfoRequestMap = new HashMap<>();userInfoRequestMap.put("appid", weChatConfig.getAppid());userInfoRequestMap.put("secret", weChatConfig.getSecret());ResponseEntity<String> userInfoResponseEntity = restTemplate.getForEntity(userInfoUrl, String.class, userInfoRequestMap);JSONObject userInfoJsonObject = JSONObject.parseObject(userInfoResponseEntity.getBody());String accessTokenNew = userInfoJsonObject.getString("access_token");if (StringUtils.isEmpty(accessTokenNew)) {throw new CustomException(userInfoJsonObject.toString(), Integer.valueOf(userInfoJsonObject.getString("errcode")));}log.error("===============================================================:" + userInfoJsonObject);

通过appid,secret,grant_type获取到微信返回的数据后,同样使用JSONObject来解析获取access_token并传值返回


  1. 第一个接口拿到openid,第二个接口拿到基础token,再调用获取用户信息的接口,就能获取到完整的用户信息(手机号除外);(参考链接)
     //使用用户唯一openid和基础accessToken获取完整用户信息String url = "https://api.weixin.qq.com/cgi-bin/user/info?openid={openid}&access_token={access_token}&lang={lang}";Map<String, String> requestMap = new HashMap<>();requestMap.put("openid", openId);requestMap.put("access_token", accessTokenNew);requestMap.put("lang", "zh_CN");ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class, requestMap);JSONObject jsonObject = JSONObject.parseObject(responseEntity.getBody());log.error("===============================================================:" + jsonObject);

通过获取到的openid和access_token获取到微信用户的信息,解析成json后通过key/value取值。

备注:以上功能,需要前端传值jscode,前端页面需要用户允许授权调用接口等配合,此处只说明后端!


h5拦截器

若依系统为例,业务需求是在不影响后台权限控制的情况下,重新配置一个公众号h5页面的权限控制,生产一个h5的token进行认证。需要使用redis(这里不多赘述redis的使用,感兴趣者可以去下载若依系统)。

# token配置
token:# 令牌自定义标识header: 123456# 令牌密钥secret: 123456# 令牌有效期(默认60分钟)expireTime: 60

先将若依系统的拦截器把h5相关接口开放出来不拦截,再加入到自定义的拦截器里进行拦截和开放

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** 微信H5端拦截器*/
@Configuration
public class WebAppConfig implements WebMvcConfigurer {@BeanShopUserActivityInterceptor getShopUserActivityInterceptor() {return new ShopUserActivityInterceptor();}@Overridepublic void addInterceptors(InterceptorRegistry registry) {//注册TestInterceptor拦截器InterceptorRegistration registration = registry.addInterceptor(getShopUserActivityInterceptor());registration.addPathPatterns("/user/h5List");                      //H5列表订单信息列表被拦截registration.excludePathPatterns(                         //添加不拦截路径"/user/getUser");}
}

拦截器

import com.ruoyi.business.domain.dto.LoginShopUserDto;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.WeCharConfig;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.concurrent.TimeUnit;/*** 微信用户活跃时间更新拦截器*/
@Component
public class ShopUserActivityInterceptor implements HandlerInterceptor {// 令牌自定义标识@Value("${token.header}")private String header;// 令牌秘钥@Value("${token.secret}")private String secret;// 令牌有效期(默认30分钟)@Value("${token.expireTime}")private int expireTime;protected static final long MILLIS_SECOND = 1000;protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;@Autowiredprivate RedisCache redisCache;@Autowiredprivate WeCharConfig weCharConfig;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//获取tokenString token = request.getHeader(header);LoginShopUserDto shopUserDto = weCharConfig.getLoginShopUser(token);//判断用户信息是否为空if (StringUtils.isNotNull(shopUserDto)) {//检验过期时间long expireTimes = shopUserDto.getExpireTime();//获取失效时间long currentTime = System.currentTimeMillis();//获取现在的时间//获取失效时间 - 获取现在的时间 <= 时间戳if (expireTimes - currentTime <= MILLIS_MINUTE_TEN) {//刷新token过期时间shopUserDto.setLoginTime(System.currentTimeMillis());shopUserDto.setExpireTime(shopUserDto.getLoginTime() + expireTime * MILLIS_MINUTE);// 根据uuid将loginUser缓存String shopUserKey = Constants.SHOP_TOKEN_KEY + shopUserDto.getToken();redisCache.setCacheObject(shopUserKey, shopUserDto, expireTime, TimeUnit.MINUTES);}//放行return true;}//返回错误信息response.setCharacterEncoding("UTF-8");response.setContentType("application/json; charset=utf-8");JSONObject res = new JSONObject();res.put("code", HttpStatus.UNAUTHORIZED);res.put("msg","未授权 unauthorized");PrintWriter out = null ;out = response.getWriter();out.write(res.toString());out.flush();out.close();return false;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}

获取redis中的用户信息工具类

import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.redis.RedisCache;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;@Component
public class WeCharConfig {// 令牌秘钥@Value("${token.secret}")private String secret;@Autowiredprivate RedisCache redisCache;public <T> T getLoginShopUser(String token){Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();// 解析对应的用户信息String uuid = (String) claims.get(Constants.SHOP_USER_KEY);String userKey = Constants.SHOP_TOKEN_KEY + uuid;return redisCache.getCacheObject(userKey);}
}

h5List Controller接口

    /*** h5-查询售后列表*/@GetMapping("/h5List")public TableDataInfo list1(User user, HttpServletRequest request) {//获取tokenString token = request.getHeader(header);log.info("=============================h5当前登录token:"+token);//解析token,获取redis中的用户信息LoginShopUserDto loginShopUser = weCharConfig.getLoginShopUser(token);//根据用户信息Id获取对象user.setUid(loginShopUser.getShopUser().getId());startPage();List<User> list = userService.selectUserListH5(user);return getDataTable(list);}

总结:主要目的是在同一套系统中做不同的路径拦截和token认证,把h5登录的用户信息记录到redis中。前端传来jscode,通过jscode获取到用户信息,存入redis,生成token传给前端,进行活跃时间校验和后面的增删改查功能的使用。


微信小程序

校验该用户是否授权,进入小程序时会有授权窗口弹出,检查该用户是否全部授权信息,记录用户得openid到数据库进行校验。

(1.微信用户信息授权即昵称、头像、地区、性别等,(参考连接)

通过配置文件中得appid,secret和前端通过授权弹窗后微信放回得jscode为参数获取用户信息

/*** 微信小程序api接口调用* @param weChatModel 微信用户授权信息* @return 返回获取了session_key的weChat对象*/public WeChatModel appletsLogin(WeChatModel weChatModel) {String url = "https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}&js_code={code}&grant_type=authorization_code";Map<String, String> requestMap = new HashMap<>();requestMap.put("appid", weChatConfig.getAppid());requestMap.put("secret", weChatConfig.getSecret());requestMap.put("code", weChatModel.getJsCode());ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class, requestMap);JSONObject jsonObject = JSONObject.parseObject(responseEntity.getBody());String openId = jsonObject.getString("openid");String session_key = jsonObject.getString("session_key");//自定义方法用openId和session_key换取tokenWeChatModel weChatModel1 = new WeChatModel();weChatModel1.setOpenId(openId);weChatModel1.setSessionKey(session_key);weChatModel1.setAppId(weChatConfig.getAppid());weChatModel1.setCity(weChatModel.getCity());weChatModel1.setProvince(weChatModel.getProvince());weChatModel1.setCountry(weChatModel.getCountry());weChatModel1.setGender(weChatModel.getGender());weChatModel1.setNickName(weChatModel.getNickName());weChatModel1.setIv(weChatModel.getIv());weChatModel1.setEncryptedData(weChatModel.getEncryptedData());return weChatModel1;}

获取数据:
openid string 用户唯一标识
session_key string 会话密钥
unionid string 用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台帐号下会返回,详见 UnionID 机制说明

备注:

微信用户数据是由前端调用微信的getUserProfile()方法(参考链接)来获取微信用户信息传给后端。

(2.手机号码授权即微信绑定的手机号。(参考文档)

微信用户点击授权手机号码时,前端获取到jscode,iv,encryptedData传到后台,后台获取到最新jscode后拿到最新session_key,通过appid,iv,encryptedData,session_key获取到手机号,并进行解密。

public void addUser(WeChatModel weChatModel){User user = new User();try {//获取session_keyWeChatModel weChatModel1 = this.appletsLogin(weChatModel);//解密客户手机号String result = wxCore.decrypt(weChatModel1.getAppId(), weChatModel1.getEncryptedData(), weChatModel1.getSessionKey(), weChatModel1.getIv());ObjectMapper mapper = new ObjectMapper();Map<String,String> map = mapper.readValue(result, Map.class);//手机号是否存在User userNew = userMapper.selectByPrimaryKey(map.get("phoneNumber"));if (shopUserNew == null){//传入User对象,插入数据库user.setUserName(weChatModel1.getNickName());user.setUserPhone(map.get("phoneNumber"));user.setCountry(weChatModel1.getCountry());user.setProvince(weChatModel1.getProvince());user.setCity(weChatModel1.getCity());user.setSex(Integer.valueOf(weChatModel1.getGender()));user.setOpenId(weChatModel1.getOpenId());user.setAvatarUrl(weChatModel.getAvatarUrl());insert(user);}} catch (JsonProcessingException e) {e.printStackTrace();}}

微信用户手机号解密的工具类WXCore,AES,WxPKCS7Encoder

import net.sf.json.JSONObject;
import org.apache.commons.codec.binary.Base64;
import org.springframework.context.annotation.Configuration;/*** 解密工具类-封装对外访问方法* @author Steven**/
@Configuration
public class WXCore {private static final String WATERMARK = "watermark";private static final String APPID = "appid";/*** 解密小程序数据* @author Steven* @param appId 小程序AppId* @param encryptedData 加密的数据* @param sessionKey sessionKey* @param iv 加密算法的初始向量* @note 官方文档参考地址:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html* @return 解密后数据*/public String decrypt(String appId, 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));JSONObject jsonObject = JSONObject.fromObject(result);String decryptAppid = jsonObject.getJSONObject(WATERMARK).getString(APPID);if (!appId.equals(decryptAppid)) {result = "";}}} catch (Exception e) {result = "";e.printStackTrace();}return result;}
}
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 liuyazhuang**/
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 blocke.printStackTrace();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}return null;}public static void initialize() {if (initialized)return;Security.addProvider(new BouncyCastleProvider());initialized = true;}// 生成ivpublic static AlgorithmParameters generateIV(byte[] iv) throws Exception {AlgorithmParameters params = AlgorithmParameters.getInstance("AES");params.init(new IvParameterSpec(iv));return params;}}
import java.nio.charset.Charset;
import java.util.Arrays;/*** 微信小程序加解密* @author liuyazhuang**/
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;}
}

小程序拦截器

业务需求为搭建一个获取用户信息的小程序,自建一个简单并不复杂的请求拦截器与权限认证,后台系统与前端配合进行

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;/*** 拦截器* @version 1.0*/
@Configuration
public class WebAppConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {//注册TestInterceptor拦截器InterceptorRegistration registration = registry.addInterceptor(new ShopUserActivityInterceptor());registration.addPathPatterns("/**");                      //所有路径都被拦截registration.excludePathPatterns(                         //添加不拦截路径"/static/**","/**/*.html",            //html静态资源"/**/*.js",              //js静态资源"/**/*.css",             //css静态资源"/**/*.woff","/**/*.ttf");}
}

拦截器:每次请求接口都会进入拦截器更新管理员用户的活跃时间,该用户的信息已被存入cookie中

import com.test.shop.web.model.ShopUser;
import com.test.shop.web.utils.UserConstants;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Date;/*** 用户活跃时间更新拦截器* @version 1.0*/
@Component
public class ShopUserActivityInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//        HttpSession session = request.getSession();
//        String userPhone = (String) session.getAttribute("userPhone");Cookie[] cookies = request.getCookies();if(cookies != null){for (Cookie cookie : cookies){if("hitCounter".equals(cookie.getName())){String userPhone = cookie.getValue();if (userPhone != null && userPhone.length() > 0) {ShopUser shopUser = (ShopUser) UserConstants.concurrentHashMap.get(userPhone);if (shopUser != null && shopUser.getType() == 1 && shopUser.getAuditStatus() == 1) {//更新用户活跃时间shopUser.setActivityTime(new Date());UserConstants.concurrentHashMap.put(shopUser.getUserPhone(), shopUser);}}}}}//放行return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}

常量工具类:本地缓存的用户活跃时间

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** 常量工具类* @version 1.0* @description com.sbwg.shop.web.utils* @date 2021/7/22*/
public class UserConstants {public static final Map<String, Object> concurrentHashMap=new ConcurrentHashMap<String,Object>();
}

定时器任务:定时去校验用户的活跃时间是否过期,过期就清除

import com.test.shop.web.model.ShopUser;
import com.test.shop.web.utils.UserConstants;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.Date;/*** 定时器任务* <p>* “0 0 12 * * ?”         每天中午十二点触发* “0 15 10 ? * *”          每天早上10:15触发* “0 15 10 * * ?”         每天早上10:15触发* “0 15 10 * * ? *”       每天早上10:15触发* “0 15 10 * * ? 2005”        2005年的每天早上10:15触发* “0 * 14 * * ?”            每天从下午2点开始到2点59分每分钟一次触发* “0 0/5 14 * * ?”            每天从下午2点开始到2:55分结束每5分钟一次触发* “0 0/5 14,18 * * ?”       每天的下午2点至2:55和6点至6点55分两个时间段内每5分钟一次触发* “0 0-5 14 * * ?”            每天14:00至14:05每分钟一次触发* “0 10,44 14 ? 3 WED”      三月的每周三的14:10和14:44触发* “0 15 10 ? * MON-FRI”    每个周一、周二、周三、周四、周五的10:15触发** @version 1.0*/
@Configuration      //1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling   // 2.开启定时任务
public class UserConstantsTask {@Scheduled(cron = "0 0/1 * * * ?")public void task() {System.out.println("检查活跃用户....");Map<String, Object> map = UserConstants.concurrentHashMap;for (String phone : map.keySet()) {ShopUser shopUser = (ShopUser) map.get(phone);Date activityTime = shopUser.getActivityTime();Date date = new Date();long nd = 1000 * 24 * 60 * 60;long nh = 1000 * 60 * 60;long nm = 1000 * 60;// 获得两个时间的毫秒时间差异long diff = date.getTime() - activityTime.getTime();// 计算差多少天long day = diff / nd;// 计算差多少小时long hour = diff % nd / nh;// 计算差多少分钟long min = diff % nd % nh / nm;//System.out.println("活跃时间"+activityTime);//System.out.println("当前时间"+date);//System.out.println("计算相差分钟:"+min);if (min > 29) {map.remove(shopUser.getUserPhone());}}//System.out.println("map:"+map);}}

Controller接口:当请求是否授权接口时(查询列表或新增一致)都会经过拦截器,更新或生产一个该用户的活跃时间,以手机号为唯一标识

/*** 查询客户是否已经授权录入信息** @return 返回客户列表*/@RequestMapping(value = "/getShopUser", method = RequestMethod.GET)@ResponseBody@CrossOriginpublic ResponseEntity<ShopUser> getShopUser(@RequestParam(required = false) String jsCode,HttpSession session,HttpServletResponse response) {ShopUser u = shopUserService.getShopUser(jsCode);if (u != null) {if (u.getType() == 1 && u.getAuditStatus() == 1) {//把手机号缓存到cookie,主要给后续拦截器使用Cookie hit=new Cookie("hitCounter",u.getUserPhone());hit.setHttpOnly(true);//如果设置了"HttpOnly"属性,那么通过程序(JS脚本、Applet等)将无法访问该Cookiehit.setMaxAge(60*30);//设置生存期response.addCookie(hit);//把手机号缓存到session,主要给后续拦截器使用
//                session.setAttribute("userPhone", u.getUserPhone());
//                //把用户信息缓存到本地u.setActivityTime(new Date());UserConstants.concurrentHashMap.put(u.getUserPhone(), u);}return ResponseEntity.status(HttpStatus.OK).body(u);} else {return ResponseEntity.status(HttpStatus.OK).body(null);}

总结:主要目的为自定义一个拦截器和活跃时间,模仿token登录等功能的需求,获取小程序授权用户的信息,并且给管理员开放一个小程序入口可以导出用户信息(导出功能需要判断管理员活跃时间是否过期)。
1.先是将请求进行拦截,判断cookie是否有值并且是管理员用户,如果“是”就更新活跃时间
2.用户点击昵称、头像信息授权前端拿到微信传来的jscode穿给后端获取openid、session_key并返回
3.用户再授权手机号码后,前端获取新的jscode和iv、encryptedData给后端,后端用jscode获取新的session_key,同时拿到openid检索数据库,查无此用户后,将参数传到微信获取手机加密信息进行解密,进行新增
4.管理员导出用户数据判断管理员活跃时间是否过期,cookie中是否有值,判断身份是否合格

微信公众号与小程序对接文档相关推荐

  1. 微信公众号及小程序对接H5分享

    微信公众号分享 首先按照官方的介绍流程 公众号官方说明文档 js-sdk说明文档 微信公众号平台 微信 JS 接口签名校验工具 一般的报错微信官方文档的常见错误里面都有描述及分析,本文就不在赘叙: s ...

  2. Bwsaas框架基于Thinkphp6.x开发的微信公众号,小程序,app,H5等多端打通的框架

    完善bwsaas框架并开源原生小程序商业版本前端应用 新增 1插件安装升级,安装平台系统插件(type=admin_system)时可同时添加多个角色组(平台系统功能+租户系统功能),需要在group ...

  3. 小程序 php wecahtpay,PHP 微信公众号,小程序获取支付参数。微信支付

    PHP 微信公众号,小程序获取支付参数.微信支付 发布时间:2018-09-26 11:19, 浏览次数:278 , 标签: PHP 首先下载微信官方demo放入项目中 地址:https://gith ...

  4. Appium 解决微信公众号、小程序切换 webview 后无法定位元素的问题

    Appium 解决微信公众号.小程序切换 webview 后无法定位元素的问题 参考文章: (1)Appium 解决微信公众号.小程序切换 webview 后无法定位元素的问题 (2)https:// ...

  5. 微信公众号及小程序开发入门(二)

    开发过程中一些对微信公众号和小程序的认识. 一.服务号 在公众号开发入门一中提到,微信公众号分为订阅号和服务号,其中服务号功能最强,只准企业申请,并且要每年交300元认证费.如果企业想拥有自己的公众号 ...

  6. ngrok内网穿透工具搭建,方便微信公众号,小程序、钉钉等开发【已投入使用】

    前言:微信公众号,小程序.钉钉等开发需要内网穿透工具. 1.经过不断的摸索和调试,完成ngrok内网穿透的工具的搭建,对于微信公众号,小程序.钉钉等开发提供便利性. 2.搭建环境Linux cento ...

  7. 企业是先做网站,还是做微信公众号或小程序 ?

    很多企业想开发线上业务,就开始做网站.做微信公众号.做小程序.表面看起来开发线上业务的工具,该有的都有了,结果却不是很理想.因此,有的企业就觉得网上开发客户效果不好,但还是有的企业老板希望能把线上开发 ...

  8. 【教程】微信公众号与小程序主体变更看这篇就够了

    一.前言 不久后公司主体将要变更,为了公司现有的公众号以及小程序能够实现丝滑切换,花了半天时间仔细研究了一下微信主体变更的操作章程.现将其中的要点以及注意点记录整理如下,可供后续有需要做微信公众号或小 ...

  9. 【微信小程序控制硬件 第14篇】分享一下我在微信公众号和小程序上实现的几个颜色采集器,轻松集成到您项目实现调光。

    [微信小程序控制硬件第1篇 ] 全网首发,借助 emq 消息服务器带你如何搭建微信小程序的mqtt服务器,轻松控制智能硬件! [微信小程序控制硬件第2篇 ] 开始微信小程序之旅,导入小程序Mqtt客户 ...

  10. 私域流量对比:微信公众号、小程序、APP,谁更有价值?

    在数字化时代,流量已经成为了互联网企业最重要的资源之一.而对于企业来说,获取到流量只是第一步,如何将流量转化为价值才是最终目的.对于私域流量的获取和转化,微信公众号.小程序和APP是目前最常见的三种方 ...

最新文章

  1. 解决Mysql:unrecognized service错误的方法(CentOS)附:修改用户名密码
  2. 集群瓶颈为什么是磁盘io
  3. SpringCloud学习(七)高可用的分布式配置中心(Spring Cloud Config)(Finchley版本)
  4. Android GooglePay 填坑录
  5. vi/vim使用教程
  6. assert函数_PHP 之 assert()函数
  7. mysql导入查询失败_mysql数据库中的查询失败
  8. MySQL Partition扫盲
  9. 网络营销教程—SEO 第五章 单面页最佳优化
  10. 8种常被忽视的SQL错误用法
  11. ExtJs xtype一览(转存)
  12. 上网账号口令怎么获取_如何获取自己路由器的上网账号和上网口令
  13. adb shell wm 命令使用
  14. 解决 error: Raw kernel process exited code: 3221226505
  15. php爬虫严选,用 Python 爬取网易严选妹子内衣信息,探究妹纸们的偏好|python爬虫|python入门|python教程...
  16. 华为k662c的虚拟服务器,华为k662c路由器怎么设置 | 华为k662c路由器设置_什么值得买...
  17. 细说;(function ($, undefined){ })(jQuery); 的使用
  18. 安装taro踩到小坑
  19. 压缩/解压(ICSharpCode.SharpZipLib 类库)
  20. 根据在同一时间使用计算机用户的多少,2016年职称计算机WindowsXP考前冲刺试题1...

热门文章

  1. 涉及到的非线性滤波算法 -- 理解
  2. 【pandas】结合泰坦尼克生还分析讲讲pandas常用基础操作
  3. 12306android端接口,12306官方火车票Api接口
  4. 电脑在使用b站的时候插入耳机,耳机没有声音,只能外放,其他软件可以正常使用。
  5. 税务信息化与计算机技术应用开发,税务信息化与计算机技术应用开发岗位辞职报告范文...
  6. android 分享到YouTube失败 403
  7. 百度产品经理探秘:需求把握和正确决策
  8. 2015年2月8日工作日志------赵鑫
  9. 昔我往矣,杨柳依依,今我来思,雨雪霏霏
  10. UE编辑器格式化java代码