手把手教你使用java对接微信公众号-获取地理位置信息
发表时间:2020-10-19
发布人:葵宇科技
浏览次数:82
公众号需要定位当前用户的地址位置,需要通过签名后返回的数据,前端将参数回调到微信服务器获取地理位置信息,官方文档说明(https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html)。
这里会告诉你对接JSSDK使用步骤。
可以看出调用JSSDK需要后端提供四个参数:
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名
1、编写后端的签名接口SysWxUserController
前端的url需要encodeURIComponent),因为页面一旦分享,微信客户端会在你的链接末尾加入其它参数,如果不是动态获取当前链接,将导致分享后的页面签名失败。
package com.sx.local.govern.controller;
import com.sx.common.result.RestResponse;
import com.sx.common.result.ResultGenerator;
import com.sx.local.govern.service.SysWxUserService;
import io.swagger.annotations.Api;
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.*;
/**
* <p>
* 微信用户信息表 前端控制器
* </p>
*
* @author lst
* @since 2020-08-29
*/
@RestController
@RequestMapping("/wx-user")
@Api(value = "SysWxUserController", tags = "微信用户")
public class SysWxUserController {
@Autowired
private SysWxUserService sysWxUserService;
/**
* @Description 获取微信地理位置需要的签名信息
* @author lst
* @date 2020-9-25 17:34
* @param url 当前网页的URL,不包含#及其后面部分
* @return com.sx.common.result.RestResponse
*/
@GetMapping(value = "/get-config", produces = "application/json; charset=utf-8")
@ApiOperation(value = "获取微信地理位置需要的签名信息", notes = "获取微信地理位置需要的签名信息", code = 200, produces = "application/json")
@ApiImplicitParam(paramType = "query", dataType = "string", name = "url",required = true ,value = "当前网页的URL,不包含#及其后面部分")
public RestResponse getConfig(@RequestParam("url") String url) {
return ResultGenerator.genSuccessResult(sysWxUserService.getConfig(url));
}
}
2、SysWxUserServiceImpl
package com.sx.local.govern.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sx.common.config.GlobalConfig;
import com.sx.common.constants.Constant;
import com.sx.common.constants.SymbolConstants;
import com.sx.common.exception.SxException;
import com.sx.common.util.HttpClientUtil;
import com.sx.common.util.RedisUtil;
import com.sx.common.util.StringUtil;
import com.sx.local.govern.constants.WxCommonConstants;
import com.sx.local.govern.entity.SysWxUser;
import com.sx.local.govern.exceptionhandler.GovernExceptionEnum;
import com.sx.local.govern.form.WxUserForm;
import com.sx.local.govern.mapper.SysWxUserMapper;
import com.sx.local.govern.service.SysWxUserService;
import com.sx.local.govern.utils.ShaUtil;
import com.sx.system.constants.SystemCommonConstants;
import com.sx.system.entity.SysArea;
import com.sx.system.exceptionhandler.SysExceptionEnum;
import com.sx.system.mapper.SysAreaMapper;
import com.sx.system.shiro.jwt.JwtUtil;
import com.sx.system.vo.WxUserVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* <p>
* 微信用户信息表 服务实现类
* </p>
*
* @author lst
* @since 2020-08-29
*/
@Service
@Slf4j
public class SysWxUserServiceImpl extends ServiceImpl<SysWxUserMapper, SysWxUser> implements SysWxUserService {
@Autowired
private RedisUtil redisUtil;
/**
* @Description 获取微信地理位置需要的签名信息
* 微信 JS 接口签名校验工具: http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign
* @author lst
* @date 2020-9-25 17:29
* @param url 当前网页的URL,不包含#及其后面部分
* @return java.util.Map<java.lang.String,java.lang.Object>
*/
@Override
public Map<String,Object> getConfig(String url){
Map<String,Object> resultMap = new HashMap<String,Object>();
String timeStamp = String.valueOf(ShaUtil.getCurrentTimestamp());
String nonceStr = ShaUtil.generateNonceStr();
String jsapiTicket = getTicket();
String signature = getSign(timeStamp,nonceStr,jsapiTicket,url);
resultMap.put("appId",GlobalConfig.getConfig("wx.appid"));
resultMap.put("timestamp",timeStamp);
resultMap.put("nonceStr",nonceStr);
resultMap.put("signature",signature);
return resultMap;
}
/**
* @Description 获取签名
* @author lst
* @date 2020-9-25 17:22
* @param timeStamp 当前时间戳,单位毫秒
* @param nonceStr 获取随机字符串
* @param jsapiTicket ticket
* @param url 当前网页的URL,不包含#及其后面部分
* @return java.lang.String
*/
private static String getSign(String timeStamp,String nonceStr,String jsapiTicket,String url){
String arr[] =new String[] {"jsapi_ticket="+jsapiTicket,"noncestr="+nonceStr,"timestamp="+timeStamp,"url="+url};
//字典序排序
Arrays.sort(arr);
String str = "";
str = arr[0]+"&"+arr[1]+"&"+arr[2]+"&"+arr[3];
log.info("str:{}",str);
String mParms = null;//sha1加密
MessageDigest digest = null;
try {
digest = java.security.MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException e) {
log.error("错误信息:{}",e);
throw new SxException(GovernExceptionEnum.PARAM_NOT_EXIST.getCode(),e.getMessage());
}
digest.update(str.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < messageDigest.length; i++) {
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
mParms = hexString.toString();
log.info("签名:{}",mParms);
return mParms;
}
/**
* @Description 通过access_token获取 ticket
* @author lst
* @date 2020-9-25 17:22
* @return java.lang.String
*/
private String getTicket(){
JSONObject jsonObject = (JSONObject) redisUtil.getValue(WxCommonConstants.WX_ACCESS_TOKEN);
Map<String,Object> resultMap = new HashMap<String,Object>();
//access_token 获取
if(null == jsonObject){
Map<String,Object> params = new HashMap<String,Object>();
String url = WxCommonConstants.GET_ACCESS_TOKEN_URL_BY_BACKSTAGE;
String appId = GlobalConfig.getConfig("wx.appid");
String appSecret = GlobalConfig.getConfig("wx.appsecret");
params.put("appid",appId);
params.put("secret",appSecret);
params.put("grant_type","client_credential");
jsonObject = JSONObject.parseObject(HttpClientUtil.doHttpsGet(url,params));
if(StringUtil.isEmpty(jsonObject.get("access_token"))){
throw new SxException(GovernExceptionEnum.WXUSER_OPENID_NOT_EXIST);
}
Integer expiresIn = (Integer) jsonObject.get("expires_in");
redisUtil.setValue(WxCommonConstants.WX_ACCESS_TOKEN,jsonObject, Long.valueOf(expiresIn));
}
log.info("通过code换取access_token、openID:{}",jsonObject);
//ticket 获取
String accessToken = (String) jsonObject.get("access_token");
JSONObject jsonTicket = (JSONObject) redisUtil.getValue(WxCommonConstants.GET_TICKET);
if(null == jsonTicket){
Map<String,Object> params = new HashMap<String,Object>();
String url = WxCommonConstants.GET_TICKET_URL;
params.put("access_token",accessToken);
params.put("type","jsapi");
jsonTicket = JSONObject.parseObject(HttpClientUtil.doHttpsGet(url,params));
if(StringUtil.isEmpty(jsonTicket.get("ticket"))){
throw new SxException(GovernExceptionEnum.WXUSER_OPENID_NOT_EXIST);
}
Integer expiresIn = (Integer) jsonTicket.get("expires_in");
redisUtil.setValue(WxCommonConstants.GET_TICKET,jsonTicket,Long.valueOf(expiresIn));
}
log.info("获取jsonTicket:{}",jsonTicket);
String ticket = (String) jsonTicket.get("ticket");
return ticket;
}
}
3、ShaUtil工具类
package com.sx.local.govern.utils;
import lombok.extern.slf4j.Slf4j;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.UUID;
/**
* @Description: 用SHA1算法验证Token
* @Author: lst
* @Date 2020-08-18
*/
@Slf4j
public class ShaUtil {
/**
* @Description 用SHA1算法验证Token
* @author lst
* @date 2020-8-20 11:30
* @param token url相关的token
* @param timestamp 时间戳
* @param nonce 随机数
* @return java.lang.String
*/
public static String getSHA1(String token, String timestamp, String nonce){
String[] arr = new String[] { token, timestamp, nonce };
Arrays.sort(arr);
//TODO 2. 将三个参数字符串拼接成一个字符串进行sha1加密
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
}
MessageDigest md = null;
String tmpStr = null;
try {
md = MessageDigest.getInstance("SHA-1");
// 将三个参数字符串拼接成一个字符串进行sha1加密
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
log.info("错误信息:{}",e.getMessage());
}
return tmpStr;
}
/**
* @Description 将字节数组转换为十六进制字符串
* @author lst
* @date 2020-8-18 11:56
* @param byteArray
* @return java.lang.String
*/
public static String byteToStr(byte[] byteArray) {
StringBuilder strDigest = new StringBuilder();
for (int i = 0; i < byteArray.length; i++) {
strDigest.append(byteToHexStr(byteArray[i]));
}
return strDigest.toString();
}
/**
* @Description 将字节转换为十六进制字符串
* @author lst
* @date 2020-8-18 11:57
* @param mByte
* @return java.lang.String
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',
'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
/**
* @Description 获取当前时间戳,单位秒
* @author lst
* @date 2020-9-25 16:35
* @return long
*/
public static long getCurrentTimestamp() {
return System.currentTimeMillis()/1000;
}
/**
* @Description 获取随机字符串 Nonce Str
* @author lst
* @date 2020-9-25 16:35
* @return java.lang.String
*/
public static String generateNonceStr() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
}
4、测试
5、通过微信 JS 接口签名校验工具验证(http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign)