微信网页授权后获取用户信息并重定向
微信公众号开发还是比较简单的,话不多说,直接开始演示微信网页如何授权获取用户信息。
微信开发文档:
https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
开发思路:
1、提供一个链接,带有一个redirect_url参数,代表在获取用户信息后要返回的页面。
2、在上一个链接中跳转进行微信网页授权。
3、授权成功后获取code。
4、通过code调用微信接口获取access_token。
5、通过access_token获取用户基本信息。
6、将用户信息拼在之前定义的redirect_url中,并重定向到redirect_url。
Java代码
controller层
import com.aaa.project.system.emplikes.domain.Emplikes;
import com.aaa.project.system.emplikes.service.IEmplikesService;
import com.aaa.project.system.friendinfo.domain.Friendinfo;
import com.aaa.project.system.friendinfo.service.IFriendinfoService;
import com.aaa.project.system.wxuserinfo.domain.Wxuserinfo;
import com.aaa.project.system.wxuserinfo.service.IWxuserinfoService;
import com.aaa.project.tool.CORS.Cors;
import com.aaa.project.tool.WeChatAccredit.service.WXAccreditService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
@RequestMapping("/wxaccredit")
public class WXAccreditController {
//微信授权后返回的地址
private String getCodeUrl = "https://www.xxxx.cn/wxaccredit/redirect/code?redirect_url=";
//微信网页授权地址
private String getWechatCodeUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?" +
"appid=[APPID]&" + //微信appid
"redirect_uri=[REDIRECT_URI]&" + //获取网页授权后,微信重定向地址
"response_type=code&" +
"scope=snsapi_userinfo&" + //授权方式
"state=STATE#wechat_redirect";
/**
* 微信点击授权重定向
* @param redirectUrl 项目地址
* @return 重定向获取信息
* @throws UnsupportedEncodingException
*/
@RequestMapping("/redirect")
public String redirect( @RequestParam(name = "redirect_url", defaultValue = "", required = false) String redirectUrl ) throws UnsupportedEncodingException {
//一次编码
redirectUrl = URLEncoder.encode(redirectUrl, "utf-8");
//二次编码
redirectUrl = URLEncoder.encode(getCodeUrl + redirectUrl, "utf-8");
//最终得到的微信获取微信授权地址
String url = getWechatCodeUrl
.replace("[APPID]", "xxxxxxxxxxxxxxxxx")
.replace("[REDIRECT_URI]", redirectUrl);
return "redirect:" + url;
}
/**
* 这里的地址由微信重定向跳转,携带code参数。
*/
@RequestMapping("/redirect/code")
public String getCode(
@RequestParam(name = "redirect_url", defaultValue = "", required = false) String redirect_url,
@RequestParam(name = "code", defaultValue = "", required = false) String code) throws Exception {
if (StringUtils.isBlank(code)) {
return "redirect:" + redirect_url + "?error=code-is-null";
}
//解码重定向地址
redirect_url = URLDecoder.decode(redirect_url, "utf-8");
//根据code获取微信相关信息
Map<String, Object> accesstokenInfo = wxAccreditService.getACCESSTOKEN(code);
String errcode = (String)accesstokenInfo.get("errcode");
String access_token = (String)accesstokenInfo.get("access_token");
String refresh_token = (String)accesstokenInfo.get("refresh_token");
String openid = (String)accesstokenInfo.get("openid");
if(StringUtils.isEmpty(errcode)){
//第四步:拉取用户信息(需scope为 snsapi_userinfo)
Map<String, Object> weiXinUserInfo = wxAccreditService.getWeiXinUserInfo(accesstokenInfo);
Map<String, Object> userInfo = createUserInfoHtml(weiXinUserInfo);
Object openids = userInfo.get("openid");
Object nickname = userInfo.get("nickname");
Object headimgurl = userInfo.get("headimgurl");
Object sexString = userInfo.get("sexString");
Object province = userInfo.get("province");
Object city = userInfo.get("city");
Object country = userInfo.get("country");
//----- 这里可以根据需要进行数据库操作,将数据添加到数据库中 -----
//最后重定向到原来的页面中
return "redirect:" + urls ;
}
return "";
}
/**
* 生成微信用户信息
* @param weiXinUserInfo
* @return
*/
private Map<String,Object> createUserInfoHtml(Map<String, Object> weiXinUserInfo) {
String openid = (String)weiXinUserInfo.get("openid");
String nickname = (String)weiXinUserInfo.get("nickname");
Double sex = (Double)weiXinUserInfo.get("sex");
String headimgurl = (String)weiXinUserInfo.get("headimgurl");
String sexString = "";
if(sex == 1.0d){
sexString = "男";
}else{
sexString = "女";
}
String province = (String)weiXinUserInfo.get("province");
String city = (String)weiXinUserInfo.get("city");
String country = (String)weiXinUserInfo.get("country");
Map<String,Object> map=new HashMap<>();
map.put("openid",openid);
map.put("nickname",nickname);
map.put("sexString",sexString);
map.put("headimgurl",headimgurl);
map.put("province",province);
map.put("city",city);
map.put("country",country);
return map;
}
}
service层
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
import org.springframework.http.ResponseEntity;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.springframework.web.client.RestTemplate;
@Service
public class WXAccreditService {
// 微信appid---微信公众平台
public static final String appIdWx = "xxxxxxxxxxxx";
// 微信AppSecret---微信公众平台
public static final String appSecretWx = "xxxxxxxxxxxxxxxxx";
@Autowired
private RestTemplate restTemplate;
private static final String GETWEIXINUSERINFO_AUTHORIZE="https://open.weixin.qq.com/connect/oauth2/authorize";
private static final String GETWEIXINUSERINFO_GETACCESSTOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token";
private static final String GETWEIXINUSERINFO_GETUSERINFO = "https://api.weixin.qq.com/sns/userinfo";
public String buildAuthorizeURL(String url){
return concatAuthorizeURL(url);
}
/**
* 拼接引导用户授权重定向的URL
* snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid)
* snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
* @param url
* @return
*/
private String concatAuthorizeURL(String url) {
StringBuilder authorizeUrl = new StringBuilder(GETWEIXINUSERINFO_AUTHORIZE);
authorizeUrl.append("?appid=").append(appIdWx);
authorizeUrl.append("&redirect_uri=").append(url);
authorizeUrl.append("&response_type=code");
authorizeUrl.append("&scope=snsapi_userinfo");
authorizeUrl.append("&state=").append("STATE");
authorizeUrl.append("#wechat_redirect");
return authorizeUrl.toString();
}
/**
* 获取微信用户的 accesstoken 和 openid
* @param code
* @return
*/
public Map<String,Object> getACCESSTOKEN(String code){
String getAccessTokenUrl = concatGetAccessTokenInfoURL(code);
String json = postRequestForWeiXinService(getAccessTokenUrl);
Map<String,Object> map = jsonToMap(json);
return map;
}
/**
* 拼接调用微信服务获取 accesstoken 和 openid URL
* @param code
* @return
*/
private String concatGetAccessTokenInfoURL(String code) {
StringBuilder getAccessTokenUrl = new StringBuilder(GETWEIXINUSERINFO_GETACCESSTOKEN);
getAccessTokenUrl.append("?appid=").append(appIdWx);
getAccessTokenUrl.append("&secret=").append(appSecretWx);
getAccessTokenUrl.append("&code=").append(code);
getAccessTokenUrl.append("&grant_type=authorization_code");
return getAccessTokenUrl.toString();
}
/**
* 获取微信用户信息
* @param map
* @return
*/
public Map getWeiXinUserInfo(Map<String, Object> map) {
String getUserInfoUrl = concatGetWeiXinUserInfoURL(map);
String json = getRequestForWeiXinService(getUserInfoUrl);
Map userInfoMap = jsonToMap(json);
return userInfoMap;
}
/**
* 拼接调用微信服务获取用户信息的URL
* @param map
* @return
*/
private String concatGetWeiXinUserInfoURL(Map<String, Object> map) {
String openId = (String) map.get("openid");
String access_token = (String) map.get("access_token");
// 检验授权凭证(access_token)是否有效
StringBuilder getUserInfoUrl = new StringBuilder(GETWEIXINUSERINFO_GETUSERINFO);
getUserInfoUrl.append("?access_token=").append(access_token);
getUserInfoUrl.append("&openId=").append(openId);
getUserInfoUrl.append("&lang=zh_CN");
return getUserInfoUrl.toString();
}
public String mapToJson(Map map){
Gson gson = new Gson();
String json = gson.toJson(map);
return json;
}
private Map jsonToMap(String json) {
Gson gons = new Gson();
Map map = gons.fromJson(json, new TypeToken<Map>(){}.getType());
return map;
}
private String postRequestForWeiXinService(String getAccessTokenUrl) {
ResponseEntity<String> postForEntity = restTemplate.postForEntity(getAccessTokenUrl, null, String.class);
String json = postForEntity.getBody();
return json;
}
private String getRequestForWeiXinService(String getUserInfoUrl) {
ResponseEntity<String> postForEntity = restTemplate.getForEntity(getUserInfoUrl.toString(), String.class);
String json = postForEntity.getBody();
return json;
}
}
config层
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import com.aaa.project.tool.WeChatAccredit.support.CustomConnectionKeepAliveStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
//return builder.build();
return builder
.setConnectTimeout(Duration.ofMillis(100))
.setReadTimeout(Duration.ofMillis(500))
.requestFactory(this::requestFactory)
.build();
}
@Bean
public HttpComponentsClientHttpRequestFactory requestFactory() {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
connectionManager.setMaxTotal(200);
connectionManager.setDefaultMaxPerRoute(20);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.evictIdleConnections(30, TimeUnit.SECONDS)
.disableAutomaticRetries()
// 有 Keep-Alive 认里面的值,没有的话永久有效
//.setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
// 换成自定义的
.setKeepAliveStrategy(new CustomConnectionKeepAliveStrategy())
.build();
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory(httpClient);
return requestFactory;
}
}
support层
import java.util.Arrays;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.http.HttpResponse;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
public class CustomConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy {
private final long DEFAULT_SECONDS = 30;
@Override
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
return Arrays.asList(response.getHeaders(HTTP.CONN_KEEP_ALIVE))
.stream()
.filter(h -> StringUtils.equalsIgnoreCase(h.getName(), "timeout")
&& StringUtils.isNumeric(h.getValue()))
.findFirst()
.map(h -> NumberUtils.toLong(h.getValue(), DEFAULT_SECONDS))
.orElse(DEFAULT_SECONDS) * 1000;
}
}