peise网站,成都软件开发培训机构,wordpress最新文章的分类名称,网站建设需要什么条件什么是三方登录
用户可以在三方主流平台#xff08;微信#xff0c;qq#xff0c;支付宝。。#xff09;登录#xff0c;然后自己平台#xff08;pethome#xff09;就不需要登录了。基于用户在主流平台上已有的账号和密码来快速完成己方应用的登录或者注册的功能。而这…什么是三方登录
用户可以在三方主流平台微信qq支付宝。。登录然后自己平台pethome就不需要登录了。基于用户在主流平台上已有的账号和密码来快速完成己方应用的登录或者注册的功能。而这里的主流平台一般是已经拥有大量用户的平台国外的比如FacebookTwitter等国内的比如微博、微信、QQ等。 第三方登录的目的是使用用户在其他平台上频繁使用的账号来快速登内录己方产品也可以实现不用注册就能登录好处就是登录比较快捷不用注册。
一、前期准备流程 1、注册邮箱账号。
2、根据邮箱账号注册微信开放平台账号完善开发者资料。
3、申请开发者资质认证、填写相关资料、填写发票、支付认证金额。提交并等待认证结果
4、认证成功后创建网站应用填写基本信息、下载网站信息登记表填写并上传扫描件、填写授权回调域等。提交审核等待结果。
5、认证成功后创建移动应用至少选择安卓、IOS、WP8其中一种平台
6、创建应用成功后申请微信登陆等待审核结果待审核通过后可进行微信登陆的开发。
注创建应用和开发者资质认证可同时进行 准备工作大致流程图
二、具体实现步骤 1、注册邮箱账号 支持网易邮箱、QQ邮箱等常规邮箱。此邮箱将成为日后登陆开放平台的账号。
2、注册微信开放平台账号、完善开发者资料 1填写邮箱进行注册、开放平台将发送邮件到填写邮箱中点击邮件上的地址进行开发者资料的完善。开放平台注册地址https://open.weixin.qq.com/cgi-bin/readtemplate?tregist/regist_tmpllangzh_CN
2开发者资料完善主要填写注册人信息。如真实姓名、真实手机号码、联系地址、身份证号码。使用注册人的微信扫码绑定为管理员。提交信息。邮件信息包含地址点击后进行资料完善
3完善资料后根据邮箱及密码进行登录
3、申请开发者资质认证 1申请开发者资质认证 登录后点击右上角邮箱号进入“基本资料”点击“开发者资质认证”显示未认证点击“现在申请”。
认证成功后这里将变成认证成功 认证成功后这里将变成认证成功 OAUTH协议为用户资源的授权提供了一个安全的开放而简易的标准。与以往的授权方式不同之处是OAUTH的授权不会使第三方触及到用户的帐号信息如用户名与密码即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权因此OAUTH是安全的。oAuth是Open Authorization的简写目前的版本是2.0版。
为了让大家理解OAuth的适用场合下面我先举一个栗子。
有一个“云打印”的网站可以将用户存储在QQ的照片打印出来。用户为了使用该服务必须让“云打印”读取自己存储在QQ上的照片。 问题是只有得到用户的授权QQ才同意“云打印”读取这些照片。那么“云打印”如何才能获得QQ用户的授权呢有比较传统的方法用户自己将QQ的用户名和密码告诉“云打印”后者就可以读取用户的照片了。但是这么做会有以下几个严重的缺点
“云打印”为了后续的服务会保存用户的用户名和密码这样很不安全。“云打印”拥有了获取用户在QQ上所有资料的权利用户没有办法限制“云打印”的访问资 源的权限和有限期。用户只有修改密码才能收回“云打印”的权限。但是这么做会使得其他所有获得用户授权 的第三方应用程序全部失效。只要有一个第三方应用程序被破解就会导致用户密码泄露以及所有被这个密码保护的数据 也会泄露
OAuth就是为了解决上面这些问题而诞生的。 我们平台需要保存三方平台账号和密码
名词定义
在详细介绍OAuth2.0之前需要了解几个专有名词这些名词经常出现在各种应用场合对于我们理解RCF 6749的内容至关重要。
1Third-party application第三方应用程序本文中又称客户端client即栗子中的云打印。
2HTTP serviceHTTP服务提供商本文中简称服务提供商即上一节例子中的QQ。
3Resource Owner资源所有者本文中又称用户user。
4User Agent用户代理本文中就是指浏览器。
5Authorization server认证服务器即服务提供商专门用来处理认证的服务器。
6Resource server资源服务器即服务提供商存放用户生成的资源的服务器。它与认证服务器可以是同一台服务器也可以是不同的服务器了解了上面的名词就不难理解OAuth的作用就是让“客户端”安全可控的获取“用户”的授权与“服务提供商”进行互动。
OAuth的思路
OAuth在客户端与服务提供商之间设置了一个授权层authorization layer。客户端不能直接登录服务提供商只能登录授权层以此将用户与客户端区分开来。客户端登录授权层使用的是令牌token与用户的密码不同。用户可以在登录的时候指定令牌的权限范围和有效期。
客户端登录授权层以后服务提供商根据令牌的权限范围和有效期向客户端开放用户储存的资料。
运行流程
OAuth 2.0的运行流程如下图摘自RCF 6749。 A用户打开客户端以后客户端要求用户给予授权。
B用户同意给予客户端授权。
C客户端使用上一步获得的授权向认证服务器申请令牌。
D认证服务器对客户端进行认证以后确认无误同意发放令牌。
E客户端使用令牌向资源服务器申请获取资源。
F资源服务器确认令牌无误同意向客户端开放资源。 上述六个步骤中B是关键即用户怎样才能给客户端授权。有了这个授权以后客户端就可以获取令牌进而凭借令牌获取资源。
客户端授权 - 授权码模式
客户端必须得到用户的授权才能获得令牌OAuth2.0定义了四种授权方式
授权码模式authorization code简化模式implicit 密码模式resource owner password credentials客户端模式client credentials
这里我们主要介绍一下授权码模式
授权码模式是功能最完整流程最严密的授权模式。它的特点是通过客户端的后台服务器与“服务提供商”的认证服务器进行互动先看下面一张图。 用户访问客户端后者将前者导向认证服务器。用户选择是否给予客户端授权。假设用户给予授权认证服务器将用户导向客户端事先指定的重定向URI同时附上一个授权 码。客户端收到授权码附上早先的重定向URI向认证服务器申请令牌。这一步是在客户端的后 台的服务器上完成的对用户不可见。认证服务器核对了授权码和重定向URI确认无误后向客户端发送访问令牌access token和 更新令牌refresh token。 为什么选择微信作为三方登录服务提供方 三方登录产品有很多,比如微信登录,qq登录,支付宝登录,新浪微博登录.但是都是遵循oauth2.0协议,只要学会一个其他就触类旁通. 微信在做的各位都使用很频繁的社交软件老少皆宜。今天我们以微信为例进行讲解。 微信开放平台供开发者可以基于微信做很多事情 https://open.weixin.qq.com/ 自己的网站可以接入网站应用开发为用户提供了微信登录功能降低了注册门槛并可在用户授权后获取用户基本信息包括头像、昵称、性别、地区。出于安全考虑网站应用的微信登录需通过微信扫描二维码来实现。 数据库设计 第三方登录流程分析 工具类的封装
使用httpclient组件发送http请求 * get现在只用到get * post */public class HttpClientUtils { /** * 发送get请求 * param url 请求地址 * return 返回内容 json */ public static String httpGet(String url){ // 1 创建发起请求客户端 try { HttpClient client new HttpClient(); // 2 创建要发起请求-tet GetMethod getMethod new GetMethod(url);// getMethod.addRequestHeader(Content-Type,// application/x-www-form-urlencoded;charsetUTF-8); getMethod.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET,utf8); // 3 通过客户端传入请求就可以发起请求,获取响应对象 client.executeMethod(getMethod); // 4 提取响应json字符串返回 String result new String(getMethod.getResponseBodyAsString().getBytes(utf8)); return result; } catch (IOException e) { e.printStackTrace(); } return null; }}
常量封装
//微信登录相关常量public class WxConstants { public static final String APPID wxd853562a0548a7d0; public static final String SECRET 4a5d5615f93f24bdba2ba8534642dbb6; public static final String GET_ACK_URL https://api.weixin.qq.com/sns/oauth2/access_token?appidAPPIDsecretSECRETcodeCODEgrant_typeauthorization_code; public static final String GET_USER_URL https://api.weixin.qq.com/sns/userinfo?access_tokenACCESS_TOKENopenidOPENID;}
检查配置回调域名hosts配置 核心代码实现
!--处理json-- !-- https://mvnrepository.com/artifact/com.alibaba/fastjson -- dependency groupIdcom.alibaba/groupId artifactIdfastjson/artifactId version1.2.58/version /dependency
跳转授权界面
Login.html
script typetext/javascript //创建vue是需要给他传递一个对象 new Vue({ el: #loginMain, //id在hmtl模板中要对应 data: { //数据模型 loginForm: { username: , password: }, wxAuthUrl: https://open.weixin.qq.com/connect/qrconnect?appidwxd853562a0548a7d0 redirect_urihttp://bugtracker.itsource.cn/callback.htmlresponse_typecodescopesnsapi_loginstate1#wechat_redirect }, 回调页面
!DOCTYPE htmlhtml langenhead meta charsetUTF-8 title回调/title !--集成vue和axios还要axios全局配置导入common.js-- !--script src方式引入vue和axios-- script srcjs/plugins/vue/dist/vue.js/script script srcjs/plugins/axios/dist/axios.js/script !--全局配置以后只要用vueaxios的页面都引入common.js-- script srcjs/common.js/script/headbody div idmyDiv /div script typetext/javascript new Vue({ el:#myDiv, mounted(){ //解析参数对象 let url location.href; let paramObj parseUrlParams2Obj(url); //获取发送请求参数 let binderUrl http://bugtracker.itsource.cn/binder.html let params {code:paramObj.code,binderUrl:binderUrl}; //发起微信登录请求 this.$http.post(/login/wechat,params) .then(result{ result result.data; if(result.success){ //已经关联了 //做登录 //提示 alert(登录成功) //把token和loginInfo存放到localStorage let {token,loginInfo} result.resultObj; localStorage.setItem(token,token); //把对象转换为json字符串存放 localStorage.setItem(loginInfo,JSON.stringify(loginInfo)); //跳转主页 location.href /index.html; }else{ //没有关联跳转关联页面 let url result.resultObj; location.href url; } }) .catch(result{ alert(系统错误); console.log(result); }) } }); /script/body/html
跳转后台微信登录
LoginInfocontroller 判断是否已经绑定如果绑定就免密登录否则返回一个未绑定错误配合前端跳转到绑定页面
PostMapping(/wechat)public AjaxResult loginWechat(RequestBody MapString,String params){ try { return loginInfoService.loginWechat(params); } catch (Exception e) { e.printStackTrace(); return AjaxResult.me().setMessage(系统错误e.getMessage()); }}
LoginInfoServiceImpl Overridepublic AjaxResult loginWechat(MapString, String params) { //1 获取code String code params.get(code); String binderUrl params.get(binderUrl); //2 获取accessToken String getAckUrl WxConstants.GET_ACK_URL .replace(APPID, WxConstants.APPID) .replace(SECRET, WxConstants.SECRET) .replace(CODE, code); String jsonStr HttpClientUtils.httpGet(getAckUrl); JSONObject jsonObject JSONObject.parseObject(jsonStr); String accessToken jsonObject.getString(access_token); String openid jsonObject.getString(openid); //就相当于微信号 //3 判断是否已经关联了 WxUser wxUser wxUserMapper.loadByOpenId(openid); if(wxUser!null wxUser.getUser_id()!null){ //查询Logininfo LoginInfo loginInfo loginInfoMapper.loadByUserId(wxUser.getUser_id()); //3.1 如果关联了实现免密登录 String token UUID.randomUUID().toString(); redisTemplate.opsForValue().set(token,loginInfo,30, TimeUnit.MINUTES); MapString,Object result new HashMap(); result.put(token,token); loginInfo.setSalt(null); loginInfo.setPassword(null); result.put(loginInfo,loginInfo); return AjaxResult.me().setResultObj(result); }else{ //3.2 否则跳转到绑定页面 binderUrl binderUrl?accessTokenaccessTokenopenIdopenid; return AjaxResult.me().setSuccess(false).setResultObj(binderUrl); }}
绑定页面
!DOCTYPE htmlhtml head langen meta charsetUTF-8 title注册/title meta http-equivX-UA-Compatible contentIEedge meta nameviewport contentwidthdevice-width, initial-scale1.0, minimum-scale1.0, maximum-scale1.0, user-scalableno meta nameformat-detection contenttelephoneno meta namerenderer contentwebkit meta http-equivCache-Control contentno-siteapp / link relstylesheet href./AmazeUI-2.4.2/assets/css/amazeui.min.css / link href./css/dlstyle.css relstylesheet typetext/css script src./AmazeUI-2.4.2/assets/js/jquery.min.js/script script src./AmazeUI-2.4.2/assets/js/amazeui.min.js/script !--script src方式引入vue和axios-- script srcjs/plugins/vue/dist/vue.js/script script srcjs/plugins/axios/dist/axios.js/script !--全局配置以后只要用vueaxios的页面都引入common.js-- script srcjs/common.js/script !--一个一个页面配置搞一个公共common.js以后只需要引入它就ok-- !--script typetext/javascript-- !--//配置axios的全局基本路径-- !--axios.defaults.baseURLhttp://localhost:8080/-- !--// axios.defaults.baseURL/api //前端跨域配置-- !--//全局属性配置在任意组件内可以使用this.$http获取axios对象-- !--Vue.prototype.$http axios-- !--/script-- /head body div classlogin-boxtitle a hrefhome/demo.htmlimg alt src./images/logobig.png //a /div div classres-banner div classres-main div classlogin-banner-bgspan/spanimg src./images/big.jpg //div div classlogin-box div classam-tabs iddoc-my-tabs div classam-tabs-bd div classam-tab-panel am-activeidmyDiv form methodpost div classuser-phone label forphonei classam-icon-mobile-phone am-icon-md/i/label input typetel name idphone v-modelphoneUserForm.phone placeholder请输入手机号 /div div classverification label forcodei classam-icon-code-fork/i/label input typetel name idcode v-modelphoneUserForm.verifyCode placeholder请输入验证码 !--a classbtn hrefjavascript:void(0); οnclicksendMobileCode(); idsendMobileCode-- !--span iddyMobileButton获取/span/a-- button typebutton clicksendMobileCode获取/button /div /form div classlogin-links label forreader-me input idreader-me typecheckbox 点击表示您同意商城《服务协议》 /label /div div classam-cf input typebutton clickbinder name value绑定授权 classam-btn am-btn-primary am-btn-sm am-fl /div hr /div script $(function() { $(#doc-my-tabs).tabs(); }) /script /div /div /div /div div classfooter div classfooter-hd p a href# 恒望科技/a b|/b a href# 商城首页/a b|/b a href# 支付宝/a b|/b a href# 物流/a /p /div div classfooter-bd p a href# 关于恒望/a a href# 合作伙伴/a a href# 联系我们/a a href# 网站地图/a em© 2015-2025 Hengwang.com 版权所有. 更多模板 a hrefhttp://www.cssmoban.com/ target_blank title模板之家模板之家/a - Collect from a hrefhttp://www.cssmoban.com/ title网页模板 target_blank网页模板/a/em /p /div /div /body script typetext/javascript new Vue({ el:#myDiv, data:{ phoneUserForm:{ phone:13330964748, verifyCode:, accessToken:null, openId:null } }, methods:{ binder(){ this.$http.post(/login/binder/wechat,this.phoneUserForm) .then(result{ result result.data; //提示 alert(登录成功) //把token和loginInfo存放到localStorage let {token,loginInfo} result.resultObj; localStorage.setItem(token,token); //把对象转换为json字符串存放 localStorage.setItem(loginInfo,JSON.stringify(loginInfo)); console.log(result,fjfjjfjfjfjfjjfjf) //跳转主页 location.href /index.html; }) .catch(result{ console.log(result,jjjjj) alert(系统错误); }) }, sendMobileCode(){ //1.判断手机号不为空 if(!this.phoneUserForm.phone){ alert(手机号不能为空); return; } //2.获取按钮禁用按钮 发送时灰化不能使用发送成功倒计时60才能使用如果发送失败立即可以发送 var sendBtn $(event.target); sendBtn.attr(disabled,true); this.$http.post(/verifycode/smsCode, {phone:this.phoneUserForm.phone,type:binder}).then((res) { console.log(res); var ajaxResult res.data; if(ajaxResult.success){ alert(手机验证码已经发送到您的手机请在3分钟内使用); //3.1.发送成倒计时 var time 60; var interval window.setInterval( function () { //每一条倒计时减一 time time - 1 ; //把倒计时时间搞到按钮上 sendBtn.html(time); //3.2.倒计时完成恢复按钮 if(time 0){ sendBtn.html(重发); sendBtn.attr(disabled,false); //清除定时器 window.clearInterval(interval); } },1000); }else{ //3.3.发送失败提示恢复按钮 sendBtn.attr(disabled,false); alert(发送失败:ajaxResult.message); } }); } }, mounted(){ let paramObj parseUrlParams2Obj(location.href); if(paramObj){ this.phoneUserForm.accessToken paramObj.accessToken; this.phoneUserForm.openId paramObj.openId; } } }) /script/html
改造发送短信验证接口
RestControllerRequestMapping(/verifycode)public class VerifyCodeController { Autowired private IVerifyCodeService verifyCodeService; //一定情况下Map能够代替类使用 PostMapping(/smsCode) //注册验证码 public AjaxResult sendSmsCode(RequestBody MapString,String params){ String phone params.get(phone); String type params.get(type); //register binder login try { verifyCodeService.sendSmsCode(params); return AjaxResult.me(); } catch (BusinessException e){ e.printStackTrace(); return AjaxResult.me().setMessage(发送失败!e.getMessage()); } catch (Exception e) { return AjaxResult.me().setMessage(系统错误!e.getMessage()); } }}
VerifyCodeServiceImpl
Override public void sendSmsCode(MapString,String params) { String phone params.get(phone); String type params.get(type); //1 校验 //1.1 手机号不能为null if (!StringUtils.hasLength(phone)) throw new BusinessException(请输入手机号); if (register.equals(type)){ //注册 //1.2 不能被注册 User user userMapper.loadByPhone(phone); if (user!null) throw new BusinessException(用户已经被注册); String businessKey UserConstants.REGISTER_CODE_PREFIX phone; sendSmsCode(businessKey); }else if(binder.equals(type)){ //绑定 String businessKey UserConstants.BINDER_CODE_PREFIX phone; sendSmsCode(businessKey); } } private void sendSmsCode(String businessKey){ //2 判断原来的是否有效 Object codeObj redisTemplate .opsForValue().get(businessKey); //code:time String code ; //2.1 如果有效 if (codeObj!null){ String codeStr (String) codeObj; //2.1.1 判断是否已过重发时间 String time codeStr.split(:)[1]; //114555558888 long intervalTime System.currentTimeMillis() - Long.valueOf(time); if (intervalTime1*60*1000){ //2.1.1.1 如果没有过提示错误 throw new BusinessException(请勿重复发送短信验证码); } //2.1.1.2 如果过了就使用原来验证码 code codeStr.split(:)[0]; }else{ //2.2 如果没有 //2.2.1 重新生成验证码 code StrUtils.getComplexRandomString(4); } //3 保存验证码到redis redisTemplate.opsForValue().set(businessKey ,code:System.currentTimeMillis() ,3, TimeUnit.MINUTES); //4 调用短信接口发送短信// SmsUtil.sendMsg(phone,您的验证码为code,请在3分钟之内使用); System.out.println(您的验证码为code,请在3分钟之内使用); }
绑定实现
LoginController
PostMapping(/binder/wechat)public AjaxResult binderWechat(RequestBody MapString,String params){ try { return loginInfoService.binderWechat(params); } catch (Exception e) { e.printStackTrace(); return AjaxResult.me().setMessage(系统错误e.getMessage()); }}
LoginInfoService
//前台输入手机号是否有账号如果有创建wxUser帮上就ok如果没有创建账号在绑定Overridepublic AjaxResult binderWechat(MapString, String params) { //参数 String phone params.get(phone); String verifyCode params.get(verifyCode); String accessToken params.get(accessToken); String openId params.get(openId); //0 验证码比对 Object codeObj redisTemplate.opsForValue().get(UserConstants.BINDER_CODE_PREFIX phone); if(codeObjnull){ return AjaxResult.me().setMessage(请重新获取验证码后再操作); }else{ String codeStr (String) codeObj; String code codeStr.split(:)[0];//codetime if (!verifyCode.equalsIgnoreCase(code)){ return AjaxResult.me().setMessage(请输入正确验证码后再操作); } } //1 获取微信用户信息 String url WxConstants.GET_USER_URL .replace(ACCESS_TOKEN, accessToken) .replace(OPENID, openId); String jsonStr HttpClientUtils.httpGet(url); //2通过电话和type获取用户登录信息 LoginDto loginDto new LoginDto(); loginDto.setUsername(phone); loginDto.setLoginType(1); LoginInfo info loginInfoMapper.loadByPhone(loginDto); //3如果用户登录信息不存在 User user null; if (infonull){ user wxUser2User(phone); info user2LoginInfo(user); //3.1 创建loginInfo loginInfoMapper.save(info); //3.2 创建User user.setInfo(info); userMapper.save(user); }else{ //4用户存在 查询用户 user userMapper.loadByPhone(phone); } //5把用户和wxUser进行绑定 WxUser wxUser wxUserJsonStr2WxUser(jsonStr); wxUser.setUser_id(user.getId()); wxUserMapper.save(wxUser); //6做免密登录 //3.1 如果关联了实现免密登录 String token UUID.randomUUID().toString(); redisTemplate.opsForValue().set(token,info,30, TimeUnit.MINUTES); MapString,Object result new HashMap(); result.put(token,token); info.setSalt(null); info.setPassword(null); result.put(loginInfo,info); return AjaxResult.me().setResultObj(result);}private LoginInfo user2LoginInfo(User user) { LoginInfo info new LoginInfo(); BeanUtils.copyProperties(user,info); //按照同名原则拷贝属性 return info;}private User wxUser2User(String phone) { User user new User(); user.setUsername(phone); user.setPhone(phone); user.setEmail(null); //给一个随机密码 String salt UUID.randomUUID().toString(); String password MD5Utils.encrypByMd5(StrUtils.getComplexRandomString(6)salt); user.setPassword(password); user.setSalt(salt); user.setState(1); user.setCreatetime(new Date()); return user;}private WxUser wxUserJsonStr2WxUser(String jsonStr) { JSONObject jsonObject JSONObject.parseObject(jsonStr); WxUser wxUser new WxUser(); wxUser.setOpenid(jsonObject.getString(openid)); wxUser.setNickname(jsonObject.getString(nickname)); wxUser.setSex(jsonObject.getInteger(sex)); wxUser.setAddress(null); wxUser.setHeadimgurl(jsonObject.getString(headimgurl)); wxUser.setUnionid(jsonObject.getString(unionid)); return wxUser;}
后台获取登录用户
public class LoginContext { /** * 获取当前登录用户信息 * param request * return */ public static LoginInfo getLoginInfo(HttpServletRequest request){ //从请求头中获取token String token request.getHeader(token); //使用token从redis中获取登录信息 if (!StringUtils.isEmpty(token)){ //1 获取spring容器 WebApplicationContext context WebApplicationContextUtils .getWebApplicationContext(request.getServletContext()); //2 通过容器获取bean RedisTemplate redisTemplate (RedisTemplate) context .getBean(redisTemplate); //3 获取登录信息 Object loginInfoObj redisTemplate.opsForValue().get(token); if (loginInfoObj!null) return (LoginInfo) loginInfoObj; } return null; }} 大致流程分析
1.在项目的登录页准备微信登录的按钮 2.当用户点击微信扫码登录向微信发起获取授权的请求 ① 3.微信直接展示扫描二维码给用户询问用户要不要给我们pethome-web项目授权 4.用户扫码确认授权项目可以获取微信用户信息 5.微信收到确认生成code授权码通过回调域拼接code返回 6.我们的项目在callback.html页面上就可以获取授权码了 一微信登录流程 1.在callback.html页面中我们定义钩子函数。 从请求栏中获取code并且触发调用后端的微信登录接口将code传送到后端 2.后端接口收到请求交给service处理 3.service业务流 4.code不能为空 5.根据code从微信获取token 使用httpClient ② 6.拿到tokenopenId 7.判断openId是已经存在查询t_wxUser 7.1.如果已经有了并且userid不为空直接免密登录 7.2 如果为空需要让用户绑定个人用户信息 返回tokenopenId 前端帮我们跳转到绑定页面 二微信绑定流程 1.在callback.html页面的钩子函数中 2.接收微信登录流程的返回值 AjaxResult {success:false,resultObj:?tokenasdfaf$openId136462315546} 3.跳转到binder.html页面 location.hrefbinder.htmlresultObj; 4.binder.html页面解析地址栏参数并且接收用户输入的参数 5.发起微信绑定流程 phone verifyCode token openId 6.后端接收参数交给service处理 7.service业务流 一校验 1.空校验 2.判断验证码 二判断phone是否被注册 user 1.如果注册了那么我们可以直接绑定微信用户了 2.如果没有注册过生成t_user t_loginInfo 三通过 tokenopenId 查询微信信息 wxuser ③ 生成t_wxuser 四绑定user和wxuser 五免密登录