概述
如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。拉取用户信息所需步骤如下:
- 引导用户进入授权同意页面同意授权,获取code。
- 通过code换取网页授权access_token。
- 通过网页授权access_token和openid获取用户基本信息。
第一步:用户同意授权
- 引导用户使用微信手机端访问规定的url:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID
&redirect_uri=REDIRECT_URI
&response_type=code&scope=SCOPE
&state=STATE
#wechat_redirect
参数说明:
APPID - 微信提供的APPID
REDIRECT_URI - 需要跳转的url,并且需要进行编码
SCOPE - 这里填写snsapi_userinfo,用以获取用户信息
STATE - 状态,重定向时会携带state
第二步:获取code
微信进行重定向时会携带code和state,这个时候我们需要进行获取。
第三步:获取access_token
利用获得到的code获取access_token,代码如下:
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?";
Map<String, String> map = new HashMap<>();
map.put("appid", WxConstant.APP_ID);
map.put("secret", WxConstant.SECRET);
map.put("grant_type", "authorization_code");
map.put("code", code);
String res = HttpClientUtils.doGet(url, map);
第四步:通过access_token拉取用户信息
再次发出请求,请求用户信息。
String sUrl = "https://api.weixin.qq.com/sns/userinfo?";
Map<String, String> sMap = new HashMap<>();
sMap.put("access_token", obj.getString("access_token"));
sMap.put("openid", obj.getString("openid"));
sMap.put("lang", "zh_CN");
JSONObject u = JSONObject.parseObject(HttpClientUtils.doGet(sUrl, sMap));
完整实现代码
需要引入的jar包和所需的工具类
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.6</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
简化了网络请求操作的工具类,使用了Apache的HttpClient。
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
public class HttpClientUtils {
public static String doGet(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpclient = HttpClients.createDefault();
String resultString = "";
CloseableHttpResponse response = null;
try {
// 创建uri
URIBuilder builder = new URIBuilder(url);
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
// 创建http GET请求
HttpGet httpGet = new HttpGet(uri);
// 执行请求
response = httpclient.execute(httpGet);
// 判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doGet(String url) {
return doGet(url, null);
}
public static String doPost(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建参数列表
if (param != null) {
List<NameValuePair> paramList = new ArrayList<>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList,"utf-8");
httpPost.setEntity(entity);
}
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return resultString;
}
public static String doPost(String url) {
return doPost(url, null);
}
public static String doPostJson(String url, String json) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建请求内容
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return resultString;
}
}
以下是核心代码,顺序依次是Controller->Service->Dao->Entity
import com.lanou.wechat.service.WxService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@RestController
@RequestMapping("/wx")
public class WxController {
@Autowired
private WxService wxService;
@GetMapping("/redirect")
public String redirect(String code, String state){
wxService.getUserInfo(code, state);
return "数据抓取成功";
}
}
import com.alibaba.fastjson.JSONObject;
import com.lanou.wechat.Constant.WxConstant;
import com.lanou.wechat.dao.UserInfoDao;
import com.lanou.wechat.entity.UserInfo;
import com.lanou.wechat.utils.HttpClientUtils;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeSet;
@Service
public class WxService {
@Resource
private UserInfoDao userInfoDao;
public void getUserInfo(String code, String state){
//通过code获取网页授权access_token
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?";
Map<String, String> map = new HashMap<>();
map.put("appid", WxConstant.APP_ID);
map.put("secret", WxConstant.SECRET);
map.put("grant_type", "authorization_code");
map.put("code", code);
String res = HttpClientUtils.doGet(url, map);
JSONObject obj = JSONObject.parseObject(res);
//拉取用户信息
String sUrl = "https://api.weixin.qq.com/sns/userinfo?";
Map<String, String> sMap = new HashMap<>();
sMap.put("access_token", obj.getString("access_token"));
sMap.put("openid", obj.getString("openid"));
sMap.put("lang", "zh_CN");
JSONObject u = JSONObject.parseObject(HttpClientUtils.doGet(sUrl, sMap));
//创建UserInfo对象
UserInfo info = new UserInfo();
info.setAccessToken(obj.getString("access_token"));
info.setRefreshToken(obj.getString("refresh_token"));
info.setOpenId(u.getString("openid"));
info.setNickName(u.getString("nickname"));
info.setSex(u.getString("sex"));
info.setCountry(u.getString("country"));
info.setProvince(u.getString("province"));
info.setCity(u.getString("city"));
info.setHeadImgUrl(u.getString("headimgurl"));
//将UserInfo对象写进数据库
try{
userInfoDao.insert(info);
}catch (Exception e){
e.printStackTrace();
}
}
}
//UserInfoDao
import com.lanou.wechat.entity.UserInfo;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserInfoDao extends BaseDao<UserInfo> {
}
//BaseDao
public interface BaseDao<T> {
void insert(T t) throws Exception;
}
public class UserInfo {
//省略 getter setter
private int id;
private String openId;
private String nickName;
private String sex;
private String province;
private String city;
private String country;
private String headImgUrl;
private String accessToken;
private String refreshToken;
}
常量定义
public class WxConstant {
public static final String APP_ID = "你的APP_ID";
public static final String SECRET = "你的SECRET";
}
所要用到的用户信息插入操作
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lanou.wechat.dao.UserInfoDao">
<insert id="insert" parameterType="com.lanou.wechat.entity.UserInfo">
insert into tb_userinfo
(nickName,gender,headImgUrl,city,province,country,accessToken,refreshToken,openId)
values
(#{nickName}, #{sex}, #{headImgUrl}, #{city}, #{province}, #{country}, #{accessToken}, #{refreshToken}, #{openId})
</insert>
</mapper>
总结
需要注意的是配置文件端口号必须是80,以确保安全。最终将项目打包上传就可以获取到微信用户的信息了。最终获取到的信息如图所示: