微信扫码登录大体上有两种实现方式:
- 一种是基于微信开放平台的扫码登录
- 一种是基于微信公众平台的扫码登录
注意: 这两个平台的扫码登录一定要区分开,这两者授权登录是不一样的。
- 微信开放平台需要企业认证才能注册。
- 微信公众平台需要认证微信服务号,授权只能在微信客户端中使用(关注公众号),才能进行扫码登录的开发。
下面我们使用基于微信公众平台的扫码登录。
一、申请公众号
1、申请公众号
查看官方文档:https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Getting_Started_Guide.html
由于我是个人开发者,注册申请一个微信公众平台的测试号就可以了。
2、申请公众测试号
微信公众平台接口测试帐号申请:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
2.1 申请公众测试号
使用微信扫码登录后,就可以拿到 appID 和 appsecret。
我们可以在微信公众平台接口调试工具 (https://mp.weixin.qq.com/debug/) 中使用测试接口。
例如:获取access_token
2.2 关注公众测试号
2.3 配置回调域名
在“网页服务”中找到“网页账号”,修改“网页授权获取用户基本信息”接口的回调域名。
注意:
- 配置网页授权的回调域名,不用填写完整的回调地址,只需要填写域名即可,回调地址在回调域名之下就ok。
- 测试我通过内网穿透(使用 natapp工具)将本地服务映射到外网,让微信服务可以访问你本地电脑的服务。
- 线上生产时,需要将官方 xxx.txt文件放到服务器根路径下,域名需要备案,外网能访问接口保存ok。
接下来就可以开发了。
二、公众号扫码授权登录
微信授权登录的官方文档:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
1、设计思路
1)页面授权流程相关接口如下:
- 获取二维码,二维码文本数据推荐后端生成。
- 用户扫码,如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。
- 通过 code 获取微信接口的调用凭证 access_token
- 通过 access_token授权凭证获取微信用户信息
2)我们PC端使用微信扫码登陆的设计思路
- PC前端调用后端获取二维码文本数据,展示二维码。
- 用户扫码,如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。
redirect_uri,我们指向前端,然后前端调用后端获取微信用户接口,若接口响应成功,微信手机端关闭页面,回到聊天窗口。 - 在第 1 步展示二维码之后,PC前端轮询调用后端微信登录接口,若接口响应成功,则微信扫码登录成功。
注意:
- 后端获取微信用户接口:一般我们建立微信用户和PC端用户的绑定关系。
- 后端微信登录接口:一般是监听 后端获取微信用户接口的绑定关系是否触发,即微信用户是否扫码授权登录成功。
为了方便,我们关注PC后端业务实现,简化上面设计思路,将 redirect_uri 直接指向 后端获取微信用户接口。
2、代码实现
创建 springboot项目,引入依赖。
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
2.1 controller
@RestController
public class WeiXinPublicAccountController {
@Autowired
private WinXinPublicAccountService winXinPublicAccountService;
@RequestMapping("/getAuthUrl")
public String getAuthUrl(HttpSession session) throws Exception {
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
session.setAttribute("state", uuid);
return winXinPublicAccountService.getAuthUrl(uuid);
}
@GetMapping(value = "/callback")
public String callback(HttpServletRequest request) throws Exception {
HttpSession session = request.getSession();
String code = request.getParameter("code");
String state = request.getParameter("state");
String uuid = (String) session.getAttribute("state");
String accessToken = winXinPublicAccountService.getAccessToken(code);
return accessToken;
}
@GetMapping(value = "/weixinLogin")
public String weixinLogin(HttpServletRequest request) throws Exception {
HttpSession session = request.getSession();
String state = request.getParameter("state");
String uuid = (String) session.getAttribute("state");
return "微信扫码登录成功";
}
}
2.2 service
@Service
public class WinXinPublicAccountService {
public String APPID = "wx2a8f0xxx";
public String APPSECRET = "afd4b135xxxxxxxxx";
public static final String AUTH_REDIRECT_URI = "http://qy7cbq.natappfree.cc/weixin/callback";
public String getAuthUrl(String state){
String oauthUrl = WeiXinConstant.AUTH_URL
.replace(WeiXinConstant.APPID, APPID)
.replace(WeiXinConstant.REDIRECT_URI, URLEncoder.encode(AUTH_REDIRECT_URI, StandardCharsets.UTF_8))
.replace(WeiXinConstant.SCOPE, WeiXinConstant.SNSAPI_USERINFO)
.replace(WeiXinConstant.STATE, state);
System.out.println(oauthUrl);
return oauthUrl;
}
public String getAccessToken(String code){
String getAccessTokenUrl = WeiXinConstant.GET_USER_ACCESS_TOKEN_URL
.replace(WeiXinConstant.APPID, APPID)
.replace(WeiXinConstant.SECRET, APPSECRET)
.replace(WeiXinConstant.CODE, code);
JSONObject resJson = null;
try {
resJson = HttpRequestUtils.httpRequestGet(getAccessTokenUrl);
} catch (IOException e) {
System.out.println("获取 accessToken 异常, e=" + e);
return "获取 accessToken 异常";
}
System.out.println("获取 access_token接口成功,resJson=" + resJson);
String accessToken = resJson.getString("access_token");
String openId = resJson.getString("openid");
if (StringUtils.isBlank(accessToken) || StringUtils.isBlank(openId)) {
return "获取 accessToken 为空";
}
getUserInfo(accessToken, openId);
return resJson.toString();
}
public String getUserInfo(String accessToken, String openId){
String getUserInfoUrl = WeiXinConstant.GET_USER_INFO_URL
.replace(WeiXinConstant.ACCESS_TOKEN, accessToken)
.replace(WeiXinConstant.OPENID, openId);
JSONObject resJson = null;
try {
resJson = HttpRequestUtils.httpRequestGet(getUserInfoUrl);
} catch (IOException e) {
System.out.println("获取 UserInfo 异常, e=" + e);
return "获取 UserInfo 异常";
}
System.out.println("获取 UserInfo 接口成功,resJson=" + resJson);
return resJson.toString();
}
}
微信平台常量类:
public class WeiXinConstant {
public static String APPID = "APPID";
public static String SECRET = "SECRET";
public static String REDIRECT_URI = "REDIRECT_URI";
public static String SCOPE = "SCOPE";
public static String STATE = "STATE";
public static String CODE = "CODE";
public static String ACCESS_TOKEN = "ACCESS_TOKEN";
public static String OPENID = "OPENID";
public static String SNSAPI_BASE = "snsapi_base";
public static String SNSAPI_USERINFO = "snsapi_userinfo";
public static final String AUTH_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect";
public static final String GET_USER_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
public static final String GET_USER_INFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
}
HttpRequestUtils工具类:
public class HttpRequestUtils {
public static JSONObject httpRequestGet(String url) throws IOException {
CloseableHttpClient client = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
HttpResponse response = client.execute(httpGet);
HttpEntity entity = response.getEntity();
if (entity != null) {
String result = EntityUtils.toString(entity, "UTF-8");
return JSONObject.parseObject(result);
}
httpGet.releaseConnection();
return null;
}
}
2.3 测试
启动项目.
1)获取二维码,这里通过工具展示。
2)用户扫码登录ok。 3)轮询的登录接口,此时应该响应ok。
到此,基于公众号扫码授权登录就搞定了。
根据设计思路,实现代码比较简陋,实际开发中可根据需求做响应调整。
– 求知若饥,虚心若愚。
|