微信登录-Java后端
------------恢复内容开始------------
1.背景:现在很多app或者网站都想要接入微信登录,可以使用户不需要注册就能快速使用APP或网站。
2.微信登录需要一些前置操作
2.1 搜索:微信开放平台 链接:https://open.weixin.qq.com/
2.2 注册成功,获取到开发所需要的appID和appsecret
3.微信的登录的交互流程
3.1 微信用户首先向第三APP请求登录
3.2 第三APP弹出一个二维码(这个二维码可以由客户端或者H5去做),用户扫描二维(请求微信Oauth2.0授权登录)
3.3 app弹出一个确认登录的页面(微信平台请求用户确认)
3.4 用户点击确认
3.5 微信公众平台拉起第三方或者重定向到第三方,带上授权临时票据code(前端或者客户端的工作,前端把回调地址放在3.2步骤的,或者在公众平台配置)
3.6 请求到服务端:服务端需要把code、appId、appscret作为参数,在程序内部调起微信接口,获取到access_token和openId
3.7 获取到用户的access_token和openId后就可以获取到用户的微信信息了
3.8 获取到用户unionId后,后端进行存储,为该用户生成一条用户信息,用户登录成功,后端可以重定向到前端某一个登录的页面,也可以让前端,根据返回值进行跳 转
3.9 登录成功后的操作,前端请求后端接口时把unionId带上,后端根据unionId,确定,当前用户unionId,找到用户在自己APP的ID
4. 附上完整代码:
HTTPClient:maven 依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.6</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.3.6</version>
</dependency>
1 package com.example.student.project.controller; 2 3 import com.example.student.project.domain.WXUserInfoData; 4 import org.apache.commons.lang3.StringUtils; 5 import org.apache.http.HttpEntity; 6 import org.apache.http.HttpResponse; 7 import org.apache.http.client.ClientProtocolException; 8 import org.apache.http.client.methods.HttpGet; 9 import org.apache.http.client.methods.HttpUriRequest; 10 import org.apache.http.entity.ContentType; 11 import org.apache.http.impl.client.CloseableHttpClient; 12 import org.apache.http.impl.client.HttpClientBuilder; 13 import org.apache.http.util.EntityUtils; 14 import org.slf4j.Logger; 15 import org.slf4j.LoggerFactory; 16 import org.springframework.boot.configurationprocessor.json.JSONException; 17 import org.springframework.boot.configurationprocessor.json.JSONObject; 18 import org.springframework.stereotype.Controller; 19 import org.springframework.web.bind.annotation.RequestMapping; 20 import org.springframework.web.bind.annotation.RequestMethod; 21 import org.springframework.web.bind.annotation.ResponseBody; 22 23 import java.io.IOException; 24 import java.io.UnsupportedEncodingException; 25 import java.net.URLEncoder; 26 import java.nio.charset.Charset; 27 import java.nio.charset.StandardCharsets; 28 import java.util.Map; 29 30 @Controller 31 @RequestMapping(value = "/CourtSystem") 32 public class WeiXinLoginController { 33 34 private static final Logger logger = LoggerFactory.getLogger(WeiXinLoginController.class); 35 36 public static final String WX_AUTH_LOGIN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token"; 37 public static final String WX_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo"; 38 //appid和appSecret 是在公众平台上申请的 39 //AppId:应用唯一标识,在微信开放平台提交应用审核通过后获得 40 public static final String WX_APP_ID = "wx9ecd6f7******"; 41 //AppSecret:应用密钥AppSecret,在微信开放平台提交应用审核通过后获得 42 public static final String WX_APP_KEY = "c1bf387181aaf6e5ff********"; 43 44 45 /** 46 * 第三方微信登录 47 * @param code 客户端返回的code 48 * @return 49 */ 50 @RequestMapping(value = "/checkLogin", method = RequestMethod.POST) 51 @ResponseBody 52 public WXUserInfoData checkLogin(String code) throws JSONException { 53 //通过code获取access_token 54 //String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code" 55 StringBuffer loginUrl = new StringBuffer(); 56 //url拼接 57 // WX_AUTH_LOGIN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token" 58 //AppId:应用唯一标识,在微信开放平台提交应用审核通过后获得 59 //WX_APP_ID = "wx9ecd6f7******"; 60 //AppSecret:应用密钥AppSecret,在微信开放平台提交应用审核通过后获得 61 //WX_APP_KEY = "c1bf387181aaf6e5ff********"; 62 loginUrl.append(WX_AUTH_LOGIN_URL).append("?appid=") //AppId 63 .append(WX_APP_ID).append("&secret=") //AppSecret 64 .append(WX_APP_KEY).append("&code=").append(code) //填写第二步获取的code参数 65 .append("&grant_type=authorization_code"); //填authorization_code(固定,来自于官方文档) 66 String loginRet = get(loginUrl.toString()); 67 JSONObject grantObj = new JSONObject(loginRet); 68 String errcode = grantObj.optString("errcode"); 69 if (!StringUtils.isEmpty(errcode)) 70 { 71 logger.error("login weixin error"+loginRet); 72 return null; 73 } 74 String openId = grantObj.optString("openid"); 75 if (StringUtils.isEmpty(openId)) 76 { 77 logger.error("login weixin getOpenId error"+loginRet); 78 return null; 79 } 80 81 String accessToken = grantObj.optString("access_token"); //接口调用凭证 82 String expiresIn = grantObj.optString("expires_in"); // access_token接口调用凭证超时时间,单位(秒) 83 String refreshToken = grantObj.optString("refresh_token"); //用户刷新access_token 84 String scope = grantObj.optString("scope"); //用户授权的作用域,使用逗号(,)分隔 85 86 //通过access_token获取用户微信信息 87 StringBuffer userUrl = new StringBuffer(); 88 // String url = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID"; 89 // 第三步获取的access_token ; OPENID:第三步获取的openId 90 //WX_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo" 91 userUrl.append(WX_USERINFO_URL).append("?access_token=").append(accessToken).append("&openid=").append(openId); 92 String userRet = get(userUrl.toString()); 93 JSONObject userObj = new JSONObject(userRet); 94 WXUserInfoData userInfo = new WXUserInfoData(); 95 userInfo.setOpenId(openId); // 用户标识 96 userInfo.setAuthToken(accessToken); 97 userInfo.setAuthRefreshToken(refreshToken); // 专用于刷新access token的token 98 userInfo.setScope(scope); // scope:权限 99 userInfo.setExpiresIn(Integer.valueOf(expiresIn)); 100 String nickname = userObj.optString("nickname"); // 用户昵称 101 String sex = userObj.optString("sex"); // 1男,2女,0未知 102 String userImg = userObj.optString("headimgurl"); //头像链接 103 String unionid = userObj.optString("unionid"); //全局唯一标识 104 userInfo.setName(nickname); 105 userInfo.setIcon(userImg); 106 userInfo.setGender(sex); 107 userInfo.setLoginId(unionid); 108 return userInfo; 109 } 110 111 112 public static String get(String url) { 113 String body = null; 114 try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { 115 logger.info("create httppost:" + url); 116 HttpGet get = new HttpGet(url); 117 get.addHeader("Accept-Charset","utf-8"); 118 HttpResponse response = sendRequest(httpClient, get); 119 body = parseResponse(response); 120 } catch (IOException e) { 121 logger.error("send post request failed: {}", e.getMessage()); 122 } 123 124 return body; 125 } 126 127 private static String paramsToString(Map<String, String> params) { 128 StringBuilder sb = new StringBuilder(); 129 try{ 130 for (String key : params.keySet()) { 131 sb.append(String.format("&%s=%s", key, URLEncoder.encode(params.get(key), StandardCharsets.UTF_8.toString()))); 132 } 133 }catch(UnsupportedEncodingException e){ 134 logger.warn("{}: encode url parameters failed", e.getMessage()); 135 } 136 return sb.length() > 0 ? "?".concat(sb.substring(1)) : ""; 137 } 138 139 private static HttpResponse sendRequest(CloseableHttpClient httpclient, HttpUriRequest httpost) 140 throws ClientProtocolException, IOException { 141 HttpResponse response = null; 142 response = httpclient.execute(httpost); 143 return response; 144 } 145 146 private static String parseResponse(HttpResponse response) { 147 logger.info("get response from http server.."); 148 HttpEntity entity = response.getEntity(); 149 150 logger.info("response status: " + response.getStatusLine()); 151 Charset charset = ContentType.getOrDefault(entity).getCharset(); 152 if (charset != null) { 153 logger.info(charset.name()); 154 } 155 156 String body = null; 157 try { 158 body = EntityUtils.toString(entity, "utf-8"); 159 logger.info("body " + body); 160 } catch (IOException e) { 161 logger.warn("{}: cannot parse the entity", e.getMessage()); 162 } 163 164 return body; 165 } 166 167 168 }