微信开放平台:微信扫码登录功能
官方文件:developers.weixin.qq.com/doc/oplatfo…
1. 授权流程说明微信OAuth2.0的授权登录,让微信用户可以安全地使用微信身份登录第三方应用或网站。微信用户授权登录接入微信OAuth2.0的第三方应用后,第三方可以获取用户的接口调用证书(access_token),通过access_token可以调用微信开放平台的授权关系接口,从而获取微信用户的基本开放信息,帮助用户实现基本的开放功能。
微信OAuth2.0的授权登录目前支持authorization_CODE模式,适合有服务器端授权的应用。该模型的总体流程如下:
①第三方发起微信授权登录请求。微信用户允许第三方应用授权后,微信会拉起应用或者重定向到第三方网站,并携带授权临时票的代码参数;
②交换access _ token通过将AppID和AppSecret添加到code参数中的API
③通过access_token调用接口,获取用户的基础数据资源或者帮助用户实现基本操作。
请注意,第三方在使用网站应用授权登录之前,已经获得了相应的网页授权范围(scope=snsapi_login),然后就可以在PC端打开以下链接:https://open.weixin.qq.com/connect/qrconnect?. APPID = APPID & REDIRECT _ uri = REDIRECT _ URI & response _ type = code & SCOPE = SCOPE & STATE = STATE #微信_redirect
返回指令
在允许用户授权后,它将被重定向到带有代码和状态参数的redirect_uri的URL。
重定向_uri?Code = code & state = state 复制代码如果用户禁止授权,重定向不会带来code参数,只会带来state参数。
重定向_uri?State=STATE 复制代码。比如登录1号店网站打开https://passport.yhd.com/wechat/login.do,1号店后会生成状态参数并跳转到https://open.weixin.qq.com/connect/qrconnect?. appid = wxbdc 5610 cc 59 c 1631 & redirect _ uri = https://passport . yhd . com/we chat/callback . do & response _ type = CODE & scope = sn SAPI _ log in & State = 3d 6 be 0a 4035d 839573 b 04816624 a 415 e #微信_ RedirectWeChat用户扫描二维码
第二步:通过code获取access_token通过代码获取access_token
api.weixin.qq.com/sns/oauth2/…
返回指令
正确返回:
{ & # 34;访问令牌& # 34;:"访问令牌& # 34;, & # 34;expires _ in & # 34:7200, & # 34;刷新_令牌& # 34;:"刷新_令牌& # 34;, & # 34;openid & # 34:"openId & # 34, & # 34;范围& # 34;:"范围& # 34;, & # 34;unionid & # 34: "o6 _ bmasdasdsad 6 _ 2s gvt 7 hmzopfl & # 34; } 复制代码错误返回示例:
{"错误代码& # 34;:40029,"errmsg & # 34:"无效代码& # 34;} 副本代码Appsecret是应用程序接口使用的密钥,可能导致应用程序数据泄露、应用程序用户数据泄露等高风险后果。存储在客户端,极有可能被恶意窃取(比如反编译获取App秘密);
建议将秘密和用户数据(如access_token)放在App云服务器中,请求由云中继接口调用。
第三步:通过access_token调用接口在获得access_token之后,接口调用具有以下前提条件:
对于接口范围,可以调用的接口如下:
微信开放平台的AppiD和APPSecret与微信公众平台不同,需要配置:
#开放平台 we chat . Open-app-ID = wx 6 ad 144 e 54 e 67d 87 we chat . Open-app-secret = 91 a2 ff 6d 38 a2 bcbcfb 7 e 9 f 9079108 e 2e 复制代码@ wechat & # 34) 公开课微信account config { /微信官方账号appid 私有字符串mpaappid //微信官方账号appsecret 私有字符串mpappsecret //商家号 私有字符串mchId //商户密钥 私有字符串mchKey //商家证书路径 私有字符串keyPath //微信支付异步通知 私有字符串notifyUrl //开放平台id 私有字符串openAppId //开放平台密钥 私有字符串openappcertet } @ Configuration public class WechatOpenConfig { @ Autowired private wechatacountconfig accountConfig; @ Bean public WxMpService wxOpenService(){ WxMpService wxOpenService = new WxMpServiceImpl(); wxopenservice . setwxmpconfigstorage(wxOpenConfigStorage()); 返回wxOpenService } @ Bean public WxMpConfigStorage wxOpenConfigStorage(){ WxMpInMemoryConfigStorage WxMpInMemoryConfigStorage = new WxMpInMemoryConfigStorage(); wxmpinmemoryconfigstorage . set appid(account config . getopen appid()); wxmpinmemoryconfigstorage . set secret(account config . getopenappsecret()); return wxMpInMemoryConfigStorage; } } @ Controller @ request mapping(& # 34;/微信& # 34;) @ SLF 4j 公共类we chat controller { @ Autowired 私有WxMpService wxMpService @ Autowired private WxMpService wxOpenService; @ get mapping(& # 34;/QR authorize & # 34;) public String QR authorize(){ //returnUrl是用户授权同意后回调的地址 String returnUrl = & # 34;http://heng.nat300.top/sell/wechat/qrUserInfo"; //引导用户访问此链接并授权 String URL = wxOpenService。BuilddqrconnectURL(返回URL,wxconsts。QR connect _ scope _ snsapi _ login,urlencoder。encode(返回URL)); return & # 34;重定向:& # 34;+网址; } //用户授权同意后回叫的地址,代码 @ get mapping(& # 34;/QR userinfo & # 34;) 公共字符串QR userinfo(@ request param(& # 34;代码& # 34;)字符串代码){ wxmpoauth 2 accesstoken wxmpoauth 2 accesstoken = new wxmpoauth 2 accesstoken(); try { //Get access _ token by code wxmopouth 2 accessstoken = wxmopenservice。oauth2 getaccesstoken(代码); } catch(WxErrorException e){ log . error(& # 34;【微信网页授权】{ } & # 34;,e); 引发新的SellException(ResultEnum。微信_MP_ERROR.getCode(),e.getError()。getErrorMsg()); } //Get OpenID String OpenID = wxmopoauth 2 accessstoken。get OpenID(); //这个地址是可选的,反正就是为了获取openid,但是如果没有404错误,就返回一个百度地址 String returnUrl = & # 34;http://www . Baidu . com & # 34;; log . info(& # 34;openid = { } & # 34,openId); return & # 34;重定向:& # 34;+returnUrl+& # 34;?openid = & # 34+openId; } }请求路径:在浏览器中打开。
open.weixin.qq.com/connect/qrc…
得到了OpenID:OpenID = O9 are V7 xr 22 zuk 6 BTV QW82 BB6 AFK
@ Controller @ request mapping(& # 34;/卖家& # 34;) public class SellerUserController { @ Autowired private seller service seller service; @ Autowired private stringdistemplate redisTemplate; @ Autowired private ProjectUrlConfig ProjectUrlConfig; @ get mapping(& # 34;/log in & # 34;) public ModelAndView登录(@ request param(& # 34;openid & # 34)字符串openid, HttpServletResponse响应, Map & lt;字符串,对象& gtMap) { //1。openid匹配数据库中的数据 seller info seller info = seller service。FindsellerinfobyOpenID(OpenID); if(seller info = = null){ map . put(& # 34;msg & # 34,ResultEnum。log in _ fail . getmessage()); map . put(& # 34;网址& # 34;, "/销售/卖家/订单/列表& # 34;); 返回新的modeland view(& # 34;常见/错误& # 34;); } //2。将令牌设置为redis String token = uuid。随机uuid()。tostring(); //设置令牌的过期时间 Integer expire = redis constant . expire; redis template . ops forvalue()。set(string . format(redis distant。TOKEN_PREFIX,TOKEN),openid,expire,TimeUnit。秒); //3。将令牌设置为cookie cookie util . set(response,cookie constant.token,token,expire); 返回新的modeland view(& # 34;重定向:& # 34;+ "http://heng.nat300.top/sell/seller/order/list"); } @ get mapping(& # 34;/logout & # 34;) public modeland view logout(http servlet request请求, HttpServletResponse响应, Map & lt;字符串,对象& gtMap) { //1。query cookie cookie = cookie util . get(request,cookie constant . token); if (cookie!= null) { //2。清除redis redistemplate.ops中的值()。GetOperations()。delete(string . format(redis constant . token _ prefix,cookie . getvalue())); //3。clear cookie cookie util . set(response,cookie constant.token,null,0); } map . put(& # 34;msg & # 34,ResultEnum。LOGOUT _ success . getmessage()); map . put(& # 34;网址& # 34;, "/销售/卖家/订单/列表& # 34;); 返回新的modeland view(& # 34;普通/成功& # 34;,地图); } }①将上一步获得的openid存储在数据库中。
(2)将授权后的跳转地址改为登录地址。
//用户授权同意后回调的地址,获取代码 @ get mapping(& # 34;/QR userinfo & # 34;) 公共字符串QR userinfo(@ request param(& # 34;代码& # 34;)字符串代码){ wxmpoauth 2 accesstoken wxmpoauth 2 accesstoken = new wxmpoauth 2 accesstoken(); try { //Get access _ token by code wxmopouth 2 accessstoken = wxmopenservice。oauth2 getaccesstoken(代码); } catch(WxErrorException e){ log . error(& # 34;【微信网页授权】{ } & # 34;,e); 引发新的SellException(ResultEnum。微信_MP_ERROR.getCode(),e.getError()。getErrorMsg()); } //Get OpenID String OpenID = wxmopoauth 2 accessstoken。get OpenID(); //授权成功后,跳转到卖家系统的登录地址 String returnUrl = & # 34;http://heng.nat300.top/sell/seller/login"; log . info(& # 34;openid = { } & # 34,openId); return & # 34;重定向:& # 34;+returnUrl+& # 34;?openid = & # 34+openId; } ③在浏览器中请求此链接:https://open.weixin.qq.com/connect/qrconnect?. appid = wx 6 ad 144 e 54 af 67d 87 & redirect _ uri = http://sell . spring boot . cn/sell/QR/otgzpwenc 6 lwo 2 etddf _-uyftqi & response _ type = code & scope = SNS API _ log in & state = http % 3a % 2f % 2f heng . NAT 300 . top % 2f sell % 2f echat % 2f user info
第三个应用请求使用微信扫码登录,而不是使用该网站的密码:
用户同意授权后,登录第三方应用的后台管理系统:
@ Aspect @ Component @ Slf4j public class SellerAuthorizeAspect { @ Autowired private stringdistemplate redisTemplate; @ Pointcut(& # 34;执行(public * com . hh . controller . seller *。*(..))"+ & # 34;&& !执行(public * com . hh . controller . sellerusercontroller . *(..))") public void verify(){ } @ Before(& # 34;verify()& # 34;) public void doVerify(){ ServletRequestAttributes attributes =(ServletRequestAttributes)requestcontextholder . getrequestattributes(); http servlet request request = attributes . get request(); //查询cookie cookie = cookie util . get(request,cookie constant . token); //如果cookie中没有令牌,则表示您已经注销或者根本没有登录 If(cookie = = null){ log . warn(& # 34;【登录验证】Cookie中找不到令牌& # 34;); //如果验证失败,抛出异常 抛出new seller authorize exception(); } /查找 string token value = redis template . ops的值()。redis中的get(string . format(redis constant . token _ prefix,cookie . getvalue()); //如果redis中没有对应的openid,也说明你已经注销或者根本没有登录 if (stringtutils。isempty (tokenvalue)) { log。warn(& # 34;【登录验证】Redis中找不到令牌& # 34;); throw new SellerAuthorizeException(); } } }5。拦截抛出的异常和登录验证失败的异常,让其跳转到登录页面,扫码登录。
@ Controlleradvice
公共类sellexception handler {
//拦截登录异常
@ exception handler(value = sellerauthorizeexception . Class)
公共modeland view handlauthorizeexception(){
//拦截异常后跳转到登录接口
返回新的modeland view(& # 34;重定向:& # 34;。concat(& # 34;https://open.weixin.qq.com/connect/qrconnect? & # 34;+
& # 34;appid = wx 6 ad 144 e 54 af 67d 87 & # 34;+
& # 34;& redirect _ uri = http://sell . spring boot . cn/sell/QR/& # 34;+
& # 34;otgzpwenc 6 lwo 2 et ddf _-uyftqi & # 34;+
& # 34;& response _ type = code & scope = snsapi _ log in & # 34;+
& # 34;& state = http % 3a % 2f % 2f heng . NAT 300 . top % 2f sell % 2f echat % 2f QR userinfo & # 34;));
}
@ exception handler(value = sell exception . class)
@ response body
public ResultVO handlerSellerException(sell exception e){
return resultvoutil . error(e . getcode(),e . getmessage());
}
@ exception handler(value = responsebankexception . class)
@ ResponseStatus(http status。禁止)
公共句柄Responsebankexception(){
}
}
作者:微信官方账号_IT哥
链接:https://juejin.cn/post/70758762419952025999