微信公众号授权

首次学习参考网址:https://blog.csdn.net/kelly921011/article/details/97776226

微信官方文档:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html

我们需要有一个测试账号,可以用微信提供的测试账号

 1、点击此链接,用微信扫描二维码,申请测试账号

 这样就获取到了测试账号的appId、appsecret

2、添加用户

扫描此二维码,添加用户

3、配置重定向url地址

 4、配置js接口安全域名

 公众号基本信息配置好以后,下面就是按照要求,写代码了

 配置文件application.properties

 wechat.redirect.url=http://192.168.7.101:8080      (此处与公众号里配置的网页授权url地址一致)

1、用户同意授权获取code

controller层:

@RequestMapping(value = "/get-url", method = RequestMethod.GET)
public JSONObject getUrl(WechatDTO wechatDTO) {
    return weChatService.getUrl(wechatDTO);
}

service层 WeChatServiceImpl
@Value("${wechat.appid}")
private String wechatAppId;

@Value("${wechat.secret}")
private String wechatAppSecret;

@Value("${wechat.redirect.host}")
private String redirectHost;

@Value("${wechat.user.html}")
private String userHtml;

@Value("${wechat.engineer.html}")
private String engineerHtml;


/*** 获取授权登录重定向url
* @return JSONObject
* @throws BusinessException
*/
public JSONObject getUrl(WechatDTO wechatDTO) throws BusinessException {
    logger.debug("进入了getUrl方法");
try {
    String html = "";
    if ("1".equals(type)) {
     html = userHtml;
    } else if ("2".equals(type)) {
     html = engineerHtml;
    }
    redirectHost = redirectHost + html;
        String redirectUri = URLEncoder.encode(redirectUrl, "UTF-8");
String url = String.format("https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_userinfo#wechat_redirect", wechatAppId, redirectUri, wechatAppSecret);
JSONObject result = new JSONObject();
result.put("url", url);
return result;
} catch (Exception ex) {
if (ex instanceof BusinessException) {
throw new BusinessException(ex);
} else {
logger.error(ex.getMessage());
throw new BusinessException("数据异常:" + ex.getMessage(), 500, null);
}
}
}

postman测试此接口,并返回url

2、通过code获取网页授权token        (实际项目中wechat.engineer.html里写此接口,通过这个页面调用此接口)

由于是自己后台测试,我用的方法是,把获取的这个url,发到我自己的微信号,之后点此链接,获取token

controller层代码

/**
* @api {get} /wechat/get-access-token-by-code 根据code获取token
* @apiPermission none
* @apiName get-access-token-by-code
* @apiGroup wechat
* @apiDescription 根据code获取token
* @apiParam {String} code code
* @apiParam {String} [param2] 参数2
* @apiParamExample {json} 参数示例:
* {
* <p>
* }
* @apiSuccess {String} access_token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
* @apiSuccess {String} expires_in access_token接口调用凭证超时时间,单位(秒)
* @apiSuccess {String} refresh_token 用户刷新access_token
* @apiSuccess {String} openid 用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID
* @apiSuccess {String} scope 用户授权的作用域,使用逗号(,)分隔
* @apiSuccessExample {json} 返回示例:
* HTTP/1.1 200 OK
* {
* "access_token":"ACCESS_TOKEN",
* "expires_in":7200,
* "refresh_token":"REFRESH_TOKEN",
* "openid":"OPENID",
* "scope":"SCOPE"
* }
*/
@RequestMapping(value = "/get-access-token-by-code", method = RequestMethod.GET)
public JSONObject getAccessTokenByCode(WechatDTO wechatDTO) {
return weChatService.getAccessTokenByCode(wechatDTO);
}

service层
/*** 通过code换取网页授权access_token
* @return JSONObject
* @throws BusinessException
*/
public JSONObject getAccessTokenByCode(WechatDTO wechatDTO) throws BusinessException {
    logger.debug("进入了getAccessTokenByCode方法");
try {
    String code = wechatDTO.getCode();
    String access_token = wechatDTO.getAccess_token();
    String openid = wechatDTO.getOpenid();
    String refresh_token = wechatDTO.getRefresh_token();
    if (StringUtils.isBlank(code)) {
    throw new BusinessException("code不能为空", 400, null);
    }
    if (StringUtils.isNotBlank(access_token) && StringUtils.isNotBlank(openid) && StringUtils.isNotBlank(refresh_token)) {
      // 校验token是否过期
    JSONObject validTokenJson = this.validAccessToken(wechatDTO);
    if (!"0".equals(validTokenJson.getString("errcode"))) {
      // 刷新token
   return this.getAccessTokenByRefreshToken(wechatDTO);
   }
    }
    // 第一次授权登录
    JSONObject tokenObject = new JSONObject();
    String result = HttpClientUtils.executeByGET(String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code", wechatAppId, wechatAppSecret, code));
    if (StringUtils.isNotBlank(result)) {
    tokenObject = JSONObject.parseObject(result);
    }
    return tokenObject;
    } catch (Exception ex) {
if (ex instanceof BusinessException) {
throw new BusinessException(ex);
} else {
logger.error(ex.getMessage());
throw new BusinessException("数据异常:" + ex.getMessage(), 500, null);
}
}
}
/*** 检验授权凭证(access_token)是否有效
* @param wechatDto 微信dto
* @return JSONObject
* @throws BusinessException
*/
public JSONObject validAccessToken(WechatDTO wechatDto) throws BusinessException {
logger.debug("进入了validAccessToken方法");
try {
String access_token = wechatDto.getAccess_token();
String openid = wechatDto.getOpenid();
String url = String.format("https://api.weixin.qq.com/sns/auth?access_token=%s&openid=%s", access_token, openid);
String result = HttpClientUtils.executeByGET(url);
JSONObject userinfo = new JSONObject();
if (StringUtils.isNotBlank(result)) {
userinfo = JSONObject.parseObject(result);
} else {
throw new BusinessException("检验授权凭证(access_token)是否有效失败", 400, null);
}
return userinfo;
} catch (Exception ex) {
if (ex instanceof BusinessException) {
throw new BusinessException(ex);
} else {
logger.error(ex.getMessage());
throw new BusinessException("数据异常:" + ex.getMessage(), 500, null);
}
}
}
/*** 根据refresh_token刷新access_token
* @param wechatDTO wechatDTO
* @return JSONObject
* @throws BusinessException
*/
public JSONObject getAccessTokenByRefreshToken(WechatDTO wechatDTO) throws BusinessException {
logger.debug("进入了getAccessTokenByRefreshToken方法");
try {
String refreshToken = wechatDTO.getRefresh_token();
if (StringUtils.isBlank(refreshToken)) {
throw new BusinessException("refresh_token不能为空", 400, null);
}

JSONObject tokenObject = new JSONObject();
String result = HttpClientUtils.executeByGET(String.format("https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s", wechatAppId, refreshToken));
if (StringUtils.isNotBlank(result)) {
tokenObject = JSONObject.parseObject(result);
} else {
throw new BusinessException("刷新access_token失败", 400, null);
}
return tokenObject;
} catch (Exception ex) {
if (ex instanceof BusinessException) {
throw new BusinessException(ex);
} else {
logger.error(ex.getMessage());
throw new BusinessException("数据异常:" + ex.getMessage(), 500, null);
}
}
}

 5、拉取用户信息(需scope为 snsapi_userinfo)

controller层

/**
* @api {get} /wechat/get-user-info 获取用户信息
* @apiPermission none
* @apiName get-user-info
* @apiGroup wechat
* @apiDescription 获取用户信息
* @apiParam {String} access_token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
* @apiParam {String} openid 用户的唯一标识
* @apiParam {String} [lang] 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语
* @apiParamExample {json} 参数示例:
* {
* <p>
* }
* @apiSuccess {String} openid 用户的唯一标识
* @apiSuccess {String} nickname 用户昵称
* @apiSuccess {String} sex 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
* @apiSuccess {String} province 用户个人资料填写的省份
* @apiSuccess {String} city 普通用户个人资料填写的城市
* @apiSuccess {String} country 国家,如中国为CN
* @apiSuccess {String} headimgurl 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。
* @apiSuccess {String} privilege 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)
* @apiSuccess {String} [unionid] 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。
* @apiSuccessExample {json} 返回示例:
* HTTP/1.1 200 OK
* {
* "openid":" OPENID",
* "nickname": NICKNAME,
* "sex":"1",
* "province":"PROVINCE",
* "city":"CITY",
* "country":"COUNTRY",
* "headimgurl":"https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
* "privilege":[ "PRIVILEGE1" "PRIVILEGE2" ],
* "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
* }
*/
@RequestMapping(value = "/get-user-info", method = RequestMethod.GET)
public JSONObject getUserInfo(WechatDTO wechatDTO) {
return weChatService.getUserInfo(wechatDTO);
}

service层
/*** 获取用户信息
* @param wechatDto 微信dto
* @return JSONObject
* @throws BusinessException
*/
public JSONObject getUserInfo(WechatDTO wechatDto) throws BusinessException {
logger.debug("进入了getUserInfo方法");
try {
this.valid(wechatDto).valid("access_token", "access_token", ValidationType.BLANK)
.valid("openid", "openid", ValidationType.BLANK);

String access_token = wechatDto.getAccess_token();
String openid = wechatDto.getOpenid();
String lang = wechatDto.getLang();
if (StringUtils.isBlank(lang)) {
lang = "zh_CN";
}
String url = String.format("https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=%s", access_token, openid, lang);
String result = HttpClientUtils.executeByGET(url);
     // 解决中文乱码问题
result = new String(result.getBytes("ISO-8859-1"), "UTF-8");
JSONObject userinfo = new JSONObject();
if (StringUtils.isNotBlank(result)) {
userinfo = JSONObject.parseObject(result);

      // 处理emoji表情方式有如下三种:1、把表情替换成(表情) 2、把表情转换URLEncoder.encode(userinfo .getString("nickname"), "UTF-8") ,读取的时候在转换回来URLDecoder.decode(userinfo .getString("nickname"), "UTF-8")
       3、设置数据库,数据库表设置
       
       // emoji表情处理 
       Pattern emoji = Pattern.compile("[ud83cudc00-ud83cudfff]|[ud83dudc00-ud83dudfff]|[u2600-u27ff]",
         Pattern.UNICODE_CASE | Pattern.CASE_INSENSITIVE);
      Matcher emojiMatcher = emoji.matcher(
userinfo .getString("nickname"));
      if (emojiMatcher.find()) {
       //将所获取的表情转换为表情
       userDTO.setUserName(emojiMatcher.replaceAll("(表情)"));
      }
        } else {
throw new BusinessException("获取微信用户个人信息失败", 400, null);
}
return userinfo;
} catch (Exception ex) {
if (ex instanceof BusinessException) {
throw new BusinessException(ex);
} else {
logger.error(ex.getMessage());
throw new BusinessException("数据异常:" + ex.getMessage(), 500, null);
}
}
}

private UserDTO userinfo(JSONObject info) {
UserDTO userDTO = new UserDTO();
userDTO.setWxOpenid(info.getString("openid"));
userDTO.setUserName(info.getString("nickname"));
return userDTO;
}

postmant测试

以上就是微信公众号授权代码

获取jsapi_ticket

controller层

/**
* @api {get} /wechat/get-jsapi-ticket 获取jsapi_ticket
* @apiPermission none
* @apiName get-jsapi-ticket
* @apiGroup wechat
* @apiDescription 获取jsapi_ticket
* @apiParam {String} url url(当前网页的URL)
* @apiParamExample {json} 参数示例:
* {
* <p>
* }
* @apiSuccessExample {json} 返回示例:
* HTTP/1.1 200 OK
* {
* "signature": "5714493857bae8c0a10bf042760147a16426bed1",
* "appId": "wxd7cd6846d8f5c913",
* "nonceStr": "2acb0ab89cc646c1bb9c0684a83f03d4",
* "timestamp": 1608167304690
* }
*/
@RequestMapping(value = "/get-jsapi-ticket", method = RequestMethod.GET)
public JSONObject getJsapiTicket(WechatDTO wechatDTO) {
return weChatService.getJsapiTicket(wechatDTO);
}

service层

/*** 获取jsapi_ticket
* @return void
* @throws BusinessException
*/
public JSONObject getJsapiTicket(WechatDTO wechatDTO) throws BusinessException {
logger.debug("进入了getJsapiTicket方法");
try {
String url = wechatDTO.getUrl();
if (StringUtils.isBlank(url)) {
throw new BusinessException("url不能为空", 400, null);
}
     // 由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket (此处放到了redis缓存里)
String cacheTicket = (String) this.getCacheService().get("WECHAT:JSAPI:TICKET");
if (StringUtils.isBlank(cacheTicket)) {
        // 获取access_token
String tokenString = HttpClientUtils.executeByGET(String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", wechatAppId, wechatAppSecret));
if (StringUtils.isNotBlank(tokenString)) {
String token = JSONObject.parseObject(tokenString).getString("access_token");
String ticketString = HttpClientUtils.executeByGET(String.format("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi", token));
if (StringUtils.isNotBlank(ticketString)) {
cacheTicket = JSONObject.parseObject(ticketString).getString("ticket");
// 此ticket在微信中有效期7200秒
this.getCacheService().set("WECHAT:JSAPI:TICKET", cacheTicket, 7195 * 1000);
}
}
}
return this.getSign(cacheTicket, url);
} catch (Exception ex) {
if (ex instanceof BusinessException) {
throw new BusinessException(ex);
} else {
logger.error(ex.getMessage());
throw new BusinessException("数据异常:" + ex.getMessage(), 500, null);
}
}
}

/**
* 获取签名
* @param ticket
* @param url
* @return
*/
private JSONObject getSign(String ticket, String url) {
String nonce = UUIDUtils.createId_32();
Long timestamp = System.currentTimeMillis();
String temp = String.format("jsapi_ticket=%s&noncestr=%s&timestamp=%s&url=%s", ticket, nonce, timestamp, url);
String signature = DigestUtils.sha1Hex(temp);
JSONObject result = new JSONObject();
result.put("appId", wechatAppId);
result.put("timestamp", timestamp);
result.put("nonceStr", nonce);
result.put("signature", signature);
return result;
}

******url为完成的路径

 

jar包依赖

HttpClientUtils.executeByGET这个方法代码如下:
HttpClient httpclient = getHttpClient();
HttpGet get = new HttpGet(url);

String responseJson = (String)httpclient.execute(get, responseHandler);
private static HttpClient httpClient = null;
public static HttpClient getHttpClient() {
if (httpClient == null) {
httpClient = new DefaultHttpClient(new ThreadSafeClientConnManager());
}

return httpClient;
}
原文地址:https://www.cnblogs.com/ssk913/p/14173185.html