利用 HttpClient 实现小程序微信登录功能
1、HttpClient
1)Why HttpClient?
微信登录实现流程
要为我们的小程序实现微信登录功能,可以参考微信官方提供的官方文档来实现。开放能力 / 用户信息 / 小程序登录 (qq.com)
下面来看一下官方文档中小程序实现登录功能的流程:
分析
- 通过 wx.login() 接口来获得登录凭证(code)。
- 将登录凭证传递给后端服务器
- 后端服务器通过向微信接口服务发送请求,请求中包含
属性 | 类型 | 必填 | 说明 |
---|---|---|---|
appid | string | 是 | 小程序 appId |
secret | string | 是 | 小程序 appSecret |
js_code | string | 是 | 登录时获取的 code,可通过wx.login获取 |
grant_type | string | 是 | 授权类型,此处只需填写 authorization_code |
- 通过这个请求,我们就能换取 用户唯一标识 OpenID 、 用户在微信开放平台账号下的唯一标识UnionID(若当前小程序已绑定到微信开放平台账号) 和 会话密钥 session_key。
- 这时候我们就能对用户信息进行处理,比如将用户存入数据库或者利用这些信息来构建 token 返回给前端等
- 用户继续使用服务
什么是 openID 和 unionID
openID:
在微信小程序
的开发过程中,往往需要将用户信息录入自己的数据库中,就得有一个唯一标记区分用户,这个标记就是openid。当一个用户使用了某个微信小程序,微信后台会产生一个openid,并且是唯一的,所以可以通过这个来区分用户。
unionID:
如果开发者拥有多个
移动应用、网站应用和公众帐号,可通过获取用户基本信息中的unionid来区分用户的唯一性
,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号,用户的unionid是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid是相同的。
HttpClient
了解了上面的登录流程,了解到我们需要想想微信提供的服务接口发送请求来获取用户的标识,这时候就需要通过 Java 来向 code2Session 接口发送符合规范的请求,尽管 Java 原生的方式(如使用 HttpURLConnection
)也可以执行 HTTP 请求,但通常情况下,HttpClient
更适合需要更高级功能和更好性能的应用程序。在大多数情况下,使用 HttpClient
可以更快速地构建可维护和高性能的 HTTP 客户端。
2)Quick Start
先来看看官方为我们提供的示例文档中的方法
// 构建 HttpClient 对象
CloseableHttpClient httpclient = HttpClients.createDefault();
// 实现 get 请求
ClassicHttpRequest httpGet = ClassicRequestBuilder.get("http://httpbin.org/get")
httpclient.execute(httpGet, response -> {
final HttpEntity entity1 = response.getEntity();
EntityUtils.consume(entity1);
return null;
}
// 实现 post 请求
ClassicHttpRequest httpPost = ClassicRequestBuilder.post("http://httpbin.org/post")
.setEntity(new UrlEncodedFormEntity(Arrays.asList(
new BasicNameValuePair("username", "vip"),
new BasicNameValuePair("password", "secret"))))
.build();
httpclient.execute(httpPost, response -> {
System.out.println(response.getCode() + " " + response.getReasonPhrase());
final HttpEntity entity2 = response.getEntity();
EntityUtils.consume(entity2);
return null;
}
上述的操作为我们演示了如何实现 get 和 post 请求的发送
使用步骤
根据上面的示例,我们来总结一下这个 Java 库的基础的使用步骤:
-
引入依赖
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency>
-
创建 HttpClient 实例
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; CloseableHttpClient httpClient = HttpClients.createDefault();
-
创建一个 HTTP 请求对象,根据自己的需求来创建一个 GET 或者 POST 对象
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; HttpGet httpGet = new HttpGet("https://www.example.com"); HttpPost httpPost = new HttpPost("https://www.example.com");
-
设置请求参数:可以设置请求头、请求体、超时时间等参数
httpGet.addHeader("User-Agent", "MyHttpClient"); httpPost.addHeader("Content-Type", "application/json"); // 设置请求体 httpPost.setEntity(new StringEntity("{'key': 'value'}"));
-
执行请求:使用 HttpClient 执行请求,得到响应对象
CloseableHttpResponse response = httpClient.execute(httpGet);
-
处理响应
int statusCode = response.getStatusLine().getStatusCode; HttpEntity entity = response.getEntity; String responseContent = EntityUtils.toString(entity);
-
释放资源:最后,不要忘记释放 HttpClient 和响应对象的资源,以防止资源泄漏:
response.close(); httpClient.close();
2、实现微信登录功能
当我们向这个接口发送请求的时候,接口会向我们返回如下的结果:
属性 | 类型 | 说明 |
---|---|---|
session_key | string | 会话密钥 |
unionid | string | 用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台账号下会返回,详见 UnionID 机制说明。 |
errmsg | string | 错误信息 |
openid | string | 用户唯一标识 |
errcode | int32 | 错误码 |
为了避免对 HttpClient 代码的反复调用,我们可以封装一个工具类来完成请求的发送和结果的接受,我们只需要获得需要的参数即可,将实现微信登录分为以下的步骤:
-
构建一个工具类来发送请求和接收结果,这个工具类中我们通过向里面传入一个请求地址,和参数图来实现请求的发送。
/** * Http工具类 */ public class HttpClientUtil { static final int TIMEOUT_MSEC = 5 * 1000; /** * 发送GET方式请求 * @param url * @param paramMap * @return */ public static String doGet(String url,Map<String,String> paramMap){ // 创建Httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); String result = ""; CloseableHttpResponse response = null; try{ URIBuilder builder = new URIBuilder(url); if(paramMap != null){ for (String key : paramMap.keySet()) { builder.addParameter(key,paramMap.get(key)); } } URI uri = builder.build(); //创建GET请求 HttpGet httpGet = new HttpGet(uri); //发送请求 response = httpClient.execute(httpGet); //判断响应状态 if(response.getStatusLine().getStatusCode() == 200){ result = EntityUtils.toString(response.getEntity(),"UTF-8"); } }catch (Exception e){ e.printStackTrace(); }finally { try { response.close(); httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } return result; } }
-
利用工具类来发送请求
// 微信用于接收传输来数据的接口,作为 GET 请求的 url 使用 public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session"; /** * 微信登录 * @param userLoginDTO 接受微信登录的信息的对象 * @return UserLoginVO */ @PostMapping("/login") @ApiOperation("微信用户登录的实现") public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO) { log.info("微信用户登录{}", userLoginDTO); // 微信登录 User user = userService.wxLogin(userLoginDTO); //为微信用户生成登录的令牌 Map<String, Object> claims = new HashMap<>(); claims.put(JwtClaimsConstant.USER_ID, user.getId()); String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), claims); UserLoginVO userLoginVO = UserLoginVO.builder() .id(user.getId()) .openid(user.getOpenid()) .token(token) .build(); return Result.success(userLoginVO); }
这里使用 JWT 来实现对用户的验证,不了解的可以看一下我的这篇文章JWT 详解与使用方法-CSDN博客
public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session"; @Override public User wxLogin(UserLoginDTO userLoginDTO) { // 调用接口服务获得 openid,具体方法实现在下方 String openid = getOpenid(userLoginDTO.getCode()); // 判断 openid 是否为空,为空则登录失败,抛出异常 if (openid == null) { throw new LoginFailedException(MessageConstant.LOGIN_FAILED); } // 判断用户是否是新用户 User user = userMapper.getByOpenId(openid); if (user == null) { user = User.builder() .openid(openid) .createTime(LocalDateTime.now()) .build(); userMapper.insert(user); } // 如果是新用户则自动完成注册,向用户数据库中插入对象 return user; }
/** * 得到 openid 的方法 * @param code * @return */ private String getOpenid(String code) { Map<String, String> map = new HashMap<>(); map.put("appid", weChatProperties.getAppid()); map.put("secret", weChatProperties.getSecret()); map.put("js_code", code); map.put("grant_type", "authorization_code"); String json = HttpClientUtil.doGet(WX_LOGIN, map); JSONObject jsonObject = JSON.parseObject(json); return jsonObject.getString("openid"); }
根据微信官方提供的需要的参数的文档,我们来为 Map 对象中注入 k-v 对象,这个对象在传入工具类后会通过解析然后作为构建 uri 的元素。
这个工具类会将 JSON 字符串转为 Sting 字符串来返回,这时候我们为了可以得到其中的元素就需要将其转为然后通过 fastjson 提供的方法来得到对象中的元素。