在微信客户端中访问第三方网页(自己开发的),需要先通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。

授权获取openId和用户信息

开发步骤:网页授权 | 微信开放文档

首先要根据前端传来的 code 去获取 openId

  1. /**
  2. * 根据code获取openId&access_token
  3. */
  4. public GetWxOpenId getOpenIdByCode(String code) {
  5. try {
  6. String getUrlUri = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type=authorization_code";
  7. String url = MessageFormat.format(getUrlUri, projectAppVo.getAppId(), projectAppVo.getAppSecret(), code);
  8. JSONObject json = RequestUtil.get(url);
  9. if (ObjectUtil.isNotEmpty(json.get("openid"))) {
  10. GetWxOpenId wxOpenId = JSON.to(GetWxOpenId.class, json);
  11. return wxOpenId;
  12. }
  13. } catch (Exception e) {
  14. e.printStackTrace();
  15. }
  16. return null;
  17. }
  18. @Data
  19. public class GetWxOpenId {
  20. @ApiModelProperty(name = "access_token", value = "token")
  21. @JsonProperty("access_token")
  22. private String accessToken;
  23. @ApiModelProperty(name = "expires_in", value = "过期时间")
  24. @JsonProperty("expires_in")
  25. private Integer expiresIn;
  26. @ApiModelProperty(name = "openid", value = "用户openId")
  27. private String openid;
  28. }

拿到了 openId 后,可以根据 openId 去获取改用户的信息

  1. /**
  2. * 获取微信用户信息
  3. */
  4. public Map<String, Object> wxUserInfo(String openId) {
  5. Map<String, Object> params = new HashMap<>(2);
  6. try {
  7. String getUrlUri = "https://api.weixin.qq.com/sns/userinfo?access_token={0}&openid={1}&lang=zh_CN";
  8. String url = MessageFormat.format(getUrlUri, token, openId);
  9. JSONObject json = RequestUtil.get(url);
  10. if (ObjectUtil.isNotEmpty(json.get("nickname")) || ObjectUtil.isNotEmpty(json.get("headimgurl"))) {
  11. params.put("nickName", json.get("nickname"));
  12. params.put("headImgUrl", json.get("headimgurl"));
  13. return params;
  14. }
  15. } catch (Exception e) {
  16. e.printStackTrace();
  17. }
  18. return null;
  19. }

JS-SDK使用

微信JS-SDK是微信公众平台,面向网页开发者提供的基于微信内的网页开发工具包。

借助这个工具包,开发这可以快速方便调用微信上的一些功能,例如拍照,选图,扫一扫等……

前端参考:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#1

后端参考:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62

前端要想使用 JS-SDK,第一步要先验签,验签因为需要用到 appsecret,为了安全要放在服务器上去计算签名,通过接口返回给到前端。

微信公众号网页开发 - 图1

验签步骤

  1. /**
  2. * 获取JS调用凭证刷新Ticket
  3. */
  4. public String getTicket(String appId, String appSecret) {
  5. Long defaultExpire = 600L;
  6. String accessToken = this.getAccessToken(appId, appSecret);
  7. String ticketKey = "wx:ticket" ;
  8. String val = redisUtil.get(ticketKey);
  9. Long expire = redisUtil.getExpire(ticketKey);
  10. if (Objects.nonNull(val) && expire > defaultExpire) {
  11. return val;
  12. }
  13. return this.refreshTicket(ticketKey, accessToken);
  14. }
  15. /**
  16. * 刷新Ticket
  17. */
  18. public String refreshTicket(String ticketKey, String accessToken) {
  19. String ticketUri = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=jsapi";
  20. try {
  21. String url = MessageFormat.format(ticketUri, accessToken);
  22. JSONObject ticket = RequestUtil.get(url);
  23. WeChatTicket weChatTicket = JSON.to(WeChatTicket.class, ticket);
  24. if (Objects.nonNull(weChatTicket) && Objects.equals(weChatTicket.getErrcode(), 0)) {
  25. redisUtil.set(ticketKey, weChatTicket.getTicket(), weChatTicket.getExpiresIn());
  26. return weChatTicket.getTicket();
  27. }
  28. } catch (Exception e) {
  29. e.printStackTrace();
  30. }
  31. return null;
  32. }
  33. @Data
  34. public class WeChatTicket {
  35. @ApiModelProperty(name = "ticket", value = "js凭证")
  36. private String ticket;
  37. @ApiModelProperty(name = "expires_in", value = "过期时间")
  38. @JsonProperty("expires_in")
  39. private Integer expiresIn;
  40. @ApiModelProperty(name = "errcode", value = "状态码")
  41. private int errcode;
  42. }
  1. //最终获取到的签名方法
  2. public JsSignVo wxSignature(String url) {
  3. if (StringUtils.isEmpty(url)) {
  4. return Result.error("url不能为空");
  5. }
  6. String ticket = weChatUtil.getTicket(appId, appSecret);
  7. if (StringUtils.isNotBlank(ticket)) {
  8. try {
  9. //前端传的地址编码了,所以需要先解码
  10. url = URLDecoder.decode(url, "UTF-8");
  11. } catch (UnsupportedEncodingException e) {
  12. e.printStackTrace();
  13. }
  14. String nonceStr = UUID.randomUUID().toString();
  15. String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
  16. String content = MessageFormat.format("jsapi_ticket={1}&noncestr={0}&timestamp={2}&url={3}", nonceStr, ticket, timestamp, url);
  17. String signature = sha1(content);
  18. if (StringUtils.isNotBlank(signature)) {
  19. JsSignVo jsSignVo = new JsSignVo();
  20. jsSignVo.setNonceStr(nonceStr);
  21. jsSignVo.setTimestamp(timestamp);
  22. jsSignVo.setSignature(signature);
  23. return jsSignVo;
  24. }
  25. }
  26. return null;
  27. }
  28. @Data
  29. public class JsSignVo {
  30. @ApiModelProperty(name = "nonceStr", value = "随机字符串")
  31. private String nonceStr;
  32. @ApiModelProperty(name = "timestamp", value = "时间戳")
  33. private String timestamp;
  34. @ApiModelProperty(name = "signature", value = "签名")
  35. private String signature;
  36. }
  37. public static String sha1(String str) {
  38. try {
  39. MessageDigest md = MessageDigest.getInstance("SHA-1");
  40. md.update(str.getBytes());
  41. byte[] digest = md.digest();
  42. StringBuilder sb = new StringBuilder();
  43. for (byte b : digest) {
  44. sb.append(String.format("%02x", b));
  45. }
  46. return sb.toString();
  47. } catch (NoSuchAlgorithmException e) {
  48. throw new RuntimeException(e);
  49. }
  50. }

签名后可以通过官方提供的工具去生成比对,https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign

签名比对一致后,前端就可以正常去使用 JS-SDK 工具包中的功能。