目录
一、开发文档说明
1、移动应用APP端(安卓/苹果)的微信登录
2、网站应用PC端的微信扫码登录
3、微信开放平台申请账号
二、业务接口开发
1、配置
2、第一步:请求CODE
3、第二步:通过code获取access_token
4、工具方法
5、测试
准备工作
移动应用微信登录是基于OAuth2.0 协议标准构建的微信 OAuth2.0 授权登录系统。
在进行微信 OAuth2.0 授权登录接入之前,在微信开放平台注册开发者帐号,并拥有一个已审核通过的移动应用,并获得相应的 AppID 和 AppSecret,申请微信登录且通过审核后,可开始接入流程。
授权流程说明
微信 OAuth2.0 授权登录让微信用户使用微信身份安全登录第三方应用或网站,在微信用户授权登录已接入微信 OAuth2.0 的第三方应用后,第三方可以获取到用户的接口调用凭证(access_token),通过 access_token 可以进行微信开放平台授权关系接口调用,从而可实现获取微信用户基本开放信息和帮助用户实现基础开放功能等。
微信 OAuth2.0 授权登录目前支持 authorization_code 模式,适用于拥有 server 端的应用授权。该模式整体流程为:
获取 access_token 时序图:
注:具体请参考官网开发文档,这里只摘抄部分:https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html
网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统。 在进行微信OAuth2.0授权登录接入之前,在微信开放平台注册开发者帐号,并拥有一个已审核通过的网站应用,并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程。
PC端其实与移动应用端差不多,我们后端开发时,提供给PC端、APP端是统一的接口,些许差异再具体业务逻辑层处理。对接微信,具体参考官网:https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
上面对接微信登录接口的开发文档中,准备工作已经明确说明:在进行微信 OAuth2.0 授权登录接入之前,在微信开放平台注册开发者帐号,并拥有一个已审核通过的移动应用/网站应用,并获得相应的 AppID 和 AppSecret,申请微信登录且通过审核后,可开始接入流程。具体请登录去申请:https://open.weixin.qq.com/
如下,是我已经申请审核通过的一个网站应用,具体直接创建一步一步跟着填写即可,最后的授权回调域写自己的网站域名即可,无需加http或https,如:www.baidu.com
注:因为采用前后端分离,这里只展示提供后端接口的业务开发流程。
项目采用SpringBoot构建,配置中心与注册中心使用nacos,可将一些配置信息放在nacos上。
(1)网站应用-PC
将在微信开放平台申请到的网站应用(PC)的相关信息放到配置文件(appId、appSercet等),当然,微信的接口地址基本是固定不变的,也可以写到常量类中。
其中,callBack配置的是获取微信授权的code后的回调地址(公网地址),可以是后端接口或前端接口(前后端分离,由前端再调后端)。
(2)移动应用-Android、IOS
其中,微信的接口地址与PC端一样,就无需再配置
这里的配置信息,统一由后端接口获取并提供,无需前端写死,方便以后可以直接配置中心修改即可;然后再由前端发起调用微信方,用户授权后,就会回调返回code。
(1) 网站应用-PC
/**
* pc端获取微信二维码地址
*
* 返回code地址等参数给前端,由前端js请求跳转二维码
* 用户扫码确认授权后,微信会回调返回code、state
*
*
* @return
* @throws
*/
@RequestMapping("/pc/weixin/login/getCodeUrl")
public Result getCodeUrl() throws Exception {
//String codeUrl = "https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE";
String codeUrl = pcConfig.getCodeUrl();
// state用于保持请求和回调的状态,授权请求后原样带回给第三方。
// 该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验
// 注:这里采用AES加密,使用当前时间作为明文加密,x小时内有效
String encryptState = AesUtils.encrypt(DateUtils.getNowYYYYMMdd(), AesUtils.getAssetsDevPwdField());
Map map = new HashMap<>();
map.put("appId",pcConfig.getAppId());
map.put("redirectUrl",pcConfig.getCallBack());
map.put("scope",pcConfig.getScope());
map.put("state",encryptState);
map.put("codeUrl",codeUrl);
return Result.success(map);
}
(2)移动应用-Android、IOS
/**
* APP获取移动应用的appId等配置
*
* @return
* @throws
*/
@RequestMapping("/app/weixin/login/getAppConfig")
public Result getAppConfig() throws Exception {
String appId = appConfig.getAppId();
// 采用AES加密,使用当前时间作为明文加密,x小时内有效
String encryptState = AesUtils.encrypt(DateUtils.getNowYYYYMMdd(), AesUtils.getAssetsDevPwdField());
Map map = new HashMap<>();
map.put("appId", appId);
map.put("state", encryptState);
return Result.success(map);
}
当用户授权微信登录时,微信会回调,返回code与state
返回说明
用户允许授权后,将会重定向到redirect_uri(也就是我们上面配置的callBack)的网址上,并且带上code和state参数
redirect_uri?code=CODE&state=STATE
若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数
redirect_uri?state=STATE
通过code获取access_token
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
参数说明
参数 | 是否必须 | 说明 |
---|---|---|
appid | 是 | 应用唯一标识,在微信开放平台提交应用审核通过后获得 |
secret | 是 | 应用密钥AppSecret,在微信开放平台提交应用审核通过后获得 |
code | 是 | 填写第一步获取的code参数 |
grant_type | 是 | 填authorization_code |
因为项目采用前后端分离,这里PC端的callBack回调地址是填的前端地址,再由前端调用后端接口(/weixin/login/callBack)
大致流程:用户授权--->code--->获取access_token、openid等--->获取微信用户的基本信息--->注册/登录
/**
* 授权回调,微信返回code、state---【PC端、APP端公用】
*
* 1、然后通过code、appid、secret去请求获取access_token、openid
* 2、最后通过access_token、openid去获取用户信息(unionid)
*
*/
@RequestMapping("/weixin/login/callBack")
public Result callBack(@RequestBody WeixinLoginDTO dto, HttpServletRequest request) throws Exception {
log.info("【微信登录】回调,code:{},state:{}", dto.getCode(), dto.getState());
try{
// AES解密state
boolean b = checkState(dto.getState());
if(!b){
log.error("【微信登录】回调异常,state已失效或被篡改:{}", dto.getState());
return Result.error(ResultCode.STATE_IS_INVALID);
}
// 先判断缓存中是否已存在(微信获取access_token的接口code只能使用一次)
String weixinUserJson = (String) redisUtils.get(RedisPrefix.weixin_user_info, dto.getCode());
if (StringUtils.isNotBlank(weixinUserJson)) {
// 直接登录
return this.weixinLogin(dto.getCode(),weixinUserJson, dto.getSource(), request);
}
//1.通过code获取access_token
//String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
String accessTokenUrl = pcConfig.getAccessTokenUrl();
if ("PC".equals(dto.getSource())) {//pc端
accessTokenUrl = accessTokenUrl.replace("APPID", pcConfig.getAppId()).replace("SECRET", pcConfig.getAppSecret()).replace("CODE", dto.getCode());
} else {// app端
accessTokenUrl = accessTokenUrl.replace("APPID", appConfig.getAppId()).replace("SECRET", appConfig.getAppSecret()).replace("CODE", dto.getCode());
}
HttpClientResult tokenResult = HttpClientUtils.doGet(accessTokenUrl);
log.info("【微信登录】获取access_token结果:{}",tokenResult);
if (200==tokenResult.getCode()) {
JSONObject tokenInfoObject = JSON.parseObject(tokenResult.getContent());
if (StringUtils.isNotEmpty(tokenInfoObject.getString("errcode"))) {
log.error("【微信登录】获取access_token失败:{}", tokenInfoObject.getString("errmsg"));
return Result.error(ResultCode.WEIXIN_LOGIN_ERROR);
}
//2.通过access_token和openid获取用户信息
//String userInfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
String userInfoUrl = pcConfig.getUserInfoUrl();
userInfoUrl = userInfoUrl.replace("ACCESS_TOKEN", tokenInfoObject.getString("access_token")).replace("OPENID", tokenInfoObject.getString("openid"));
HttpClientResult weixinUserResult = HttpClientUtils.doGet(userInfoUrl);
log.info("【微信登录】获取微信用户信息结果:{}",weixinUserResult);
if (200==weixinUserResult.getCode()) {
JSONObject weixinUserObject = JSON.parseObject(weixinUserResult.getContent());
if (StringUtils.isNotEmpty(weixinUserObject.getString("errcode"))) {
log.error("【微信登录】获取微信用户信息失败:{}", weixinUserObject.getString("errmsg"));
return Result.error(ResultCode.WEIXIN_LOGIN_ERROR);
}
// todo 登录或注册
return this.weixinLogin(dto.getCode(),weixinUserResult.getContent(), dto.getSource(), request);
}
}
}catch (Exception e){
log.error("================【微信登录】回调接口异常:{}",e.getMessage());
}
log.error("【微信登录】回调失败!");
return Result.error(ResultCode.WEIXIN_LOGIN_ERROR);
}
其中,“todo 登录或注册”就是你自己要根据业务需求进行具体的注册/登录业务逻辑开发。
返回说明:通过access_token和openid获取用户信息
正确的Json返回结果:
{
"openid":"OPENID",
"nickname":"NICKNAME",
"sex":1,
"province":"PROVINCE",
"city":"CITY",
"country":"COUNTRY",
"headimgurl": "https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
"privilege":[
"PRIVILEGE1",
"PRIVILEGE2"
],
"unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
PC端扫描授权登录,看能否回调后获取微信用户信息
注:由于项目采用前后端分离,本文仅提供后端接口开发说明,大致流程类似,具体的请求参数与响应信息,参考官网即可。
参考:
https://open.weixin.qq.com/
https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
●史上最强Tomcat8性能优化
●阿里巴巴为什么能抗住90秒100亿?--服务端高并发分布式架构演进之路
●B2B电商平台--ChinaPay银联电子支付功能
●学会Zookeeper分布式锁,让面试官对你刮目相看
●SpringCloud电商秒杀微服务-Redisson分布式锁方案
查看更多好文,进入公众号--撩我--往期精彩
一只 有深度 有灵魂 的公众号0.0