com.healthy.common.security.social.weixinmp.connet.WeixinMpOAuth2Template Maven / Gradle / Ivy
package com.healthy.common.security.social.weixinmp.connet;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.social.oauth2.AccessGrant;
import org.springframework.social.oauth2.OAuth2Parameters;
import org.springframework.social.oauth2.OAuth2Template;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/**
* 完成微信公众号的OAuth2认证流程的模板类。国内厂商实现的OAuth2每个都不同, spring默认提供的OAuth2Template适应不了,只能针对每个厂商自己微调。
*/
@Slf4j
public class WeixinMpOAuth2Template extends OAuth2Template {
private static final String REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/refresh_token";
private final String clientId;
private final String clientSecret;
private final String accessTokenUrl;
public WeixinMpOAuth2Template(String clientId, String clientSecret, String authorizeUrl, String accessTokenUrl) {
super(clientId, clientSecret, authorizeUrl, accessTokenUrl);
setUseParametersForClientAuthentication(true);
this.clientId = clientId;
this.clientSecret = clientSecret;
this.accessTokenUrl = accessTokenUrl;
}
@Override
public AccessGrant exchangeForAccess(String authorizationCode, String redirectUri,
MultiValueMap parameters) {
StringBuilder accessTokenRequestUrl = new StringBuilder(accessTokenUrl);
accessTokenRequestUrl.append("?appid=" + clientId);
accessTokenRequestUrl.append("&secret=" + clientSecret);
accessTokenRequestUrl.append("&code=" + authorizationCode);
accessTokenRequestUrl.append("&grant_type=authorization_code");
accessTokenRequestUrl.append("&redirect_uri=" + redirectUri);
return getAccessToken(accessTokenRequestUrl);
}
@Override
public AccessGrant refreshAccess(String refreshToken, MultiValueMap additionalParameters) {
StringBuilder refreshTokenUrl = new StringBuilder(REFRESH_TOKEN_URL);
refreshTokenUrl.append("?appid=" + clientId);
refreshTokenUrl.append("&grant_type=refresh_token");
refreshTokenUrl.append("&refresh_token=" + refreshToken);
return getAccessToken(refreshTokenUrl);
}
private AccessGrant getAccessToken(StringBuilder accessTokenRequestUrl) {
log.debug("获取access_token, 请求URL: " + accessTokenRequestUrl.toString());
String response = getRestTemplate().getForObject(accessTokenRequestUrl.toString(), String.class);
log.debug("获取access_token, 响应内容: " + response);
Map result = null;
try {
result = new ObjectMapper().readValue(response, Map.class);
} catch (Exception e) {
e.printStackTrace();
}
//返回错误码时直接返回空
if (StrUtil.isNotBlank(MapUtil.getStr(result, "errcode"))) {
String errcode = MapUtil.getStr(result, "errcode");
String errmsg = MapUtil.getStr(result, "errmsg");
throw new RuntimeException("获取access token失败, errcode:" + errcode + ", errmsg:" + errmsg);
}
WeixinMpAccessGrant accessToken = new WeixinMpAccessGrant(
MapUtil.getStr(result, "access_token"),
MapUtil.getStr(result, "scope"),
MapUtil.getStr(result, "refresh_token"),
MapUtil.getLong(result, "expires_in"));
accessToken.setOpenId(MapUtil.getStr(result, "openid"));
return accessToken;
}
/**
* 构建获取授权码的请求。也就是引导用户跳转到微信的地址。
*/
@Override
public String buildAuthenticateUrl(OAuth2Parameters parameters) {
String url = super.buildAuthenticateUrl(parameters);
// url = url + "&appid=" + clientId + "&scope=snsapi_base";
url = url + "&appid=" + clientId + "&scope=snsapi_base,snsapi_userinfo,snsapi_login";
return url;
}
@Override
public String buildAuthorizeUrl(OAuth2Parameters parameters) {
return buildAuthenticateUrl(parameters);
}
/**
* 微信返回的contentType是html/text,添加相应的HttpMessageConverter来处理。
*/
@Override
protected RestTemplate createRestTemplate() {
RestTemplate restTemplate = super.createRestTemplate();
restTemplate.getMessageConverters().add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
return restTemplate;
}
}