微信第三方登录绝对干货

课程介绍
1. 用户登录-账号登录;(掌握)
2. 三方登录概述;(理解)
3. 三方登录协议-Oauth2.0;(了解)
4. 三方登录选择-微信三方登录;(掌握)
5. 微信三方登录实战;(掌握)
前台登录:
1)账号登录
2)三方登录
3)验证码登录 自己做
4)扫码登录-做不了

1.前台登录-账号登录

1)后台登录接口
2)前台登录实现并且保存loginInfo和token到localStorage,登录成功跳转首页,并展示用户名
3)前台通过axios的前置拦截器携带token到后台
4)后台做token的登录拦截器,如果没有回报错给前台
5)前台通过axios后置拦截器对后台登录拦截错误进行跳转到登录页面
6)前台也要做拦截-有的地址是不需要访问后台

前提:login.html中集成axios和vue


<!--引入vue和axios-->
<script src="js/plugins/vue/dist/vue.js"></script>
<script src="js/plugins/axios/dist/axios.js"></script>
<!--全局配置axios-->
<script src="js/common.js"></script><script type="text/javascript">new Vue({el:".login-form",data:{},methods:{},mounted(){alert(this.$http)}});
</script>

和后台账号登录差不多
7)后台登录接口
8)前台登录实现并且保存uUser和uToken到localStorage
9)前台通过axios的前置拦截器携带token到后台
10)后台做token的登录拦截器,如果没有回报错给前台
11)前台通过axios后置拦截器对后台登录拦截错误进行跳转到登录页面
12)前台也要做拦截-有的地址是不需要访问后台

1.1.前台登录

暂时不依赖
两个不同的域名的localStorage不能直接互相访问。那么如何在aaa.com中如何调用bbb.com的localStorage?

login(){// alert(this.loginInfo)// console.log(this.loginInfo)this.$http.post("/login/account",this.loginInfo).then(result => {result = result.data; //{success,message,resultobj}console.log(result)if (result.success) {let {token, user} = result.resultObj;localStorage.setItem('uToken', token);alert(localStorage.getItem("token"));//JSON.stringify(user) 把对象转换为json字符串localStorage.setItem('uUser', JSON.stringify(user));//跳转到主页location.href = "/index.html"}else{alert(result.message)}}).catch(result => {alert("系统错误!")})
}

1.2.axios携带token-common.js

//==============axios前置拦截器实现每次携带token======================//
axios.interceptors.request.use(config=>{let token = localStorage.getItem("token");if(token){//携带tokenconfig.headers["token"] = token;}return config;
},error => {Promise.reject(error)
})
//==============axios前置拦截器实现每次携带token======================//

1.3.axios后置处理后台拦截错误-common.js

//==============axios后台拦截器实现后端已经退出登录的跳转登录页面======================//
axios.interceptors.response.use(config=>{let data = config.data;if(!data.success && "noLogin"===data.message){localStorage.removeItem("token");localStorage.removeItem("loginInfo");location.href = "/login.html";}return config;
},error => {Promise.reject(error)
})
//==============axios后台拦截器实现后端已经退出登录的跳转登录页面======================//

1.4.前台登录拦截-common.js

//前端的登录拦截-common.js会被所有的界面导入
// 判断localStorage中是否有logininfo
let url = location.href;
//不是登录且不是注册,就要拦截
if(url.indexOf("login.html")==-1 && url.indexOf("register.html")==-1)
{let loginInfo = localStorage.getItem("loginInfo");if(!loginInfo){location.href = "/login.html"}
}

2.三方登录概述

2.1.什么是三方登录
用户可以在三方主流平台(微信,qq,支付宝。。)登录,然后自己平台就不需要登录了。基于用户在主流平台上已有的账号和密码来快速完成己方应用的登录或者注册的功能。而这里的主流平台,一般是已经拥有大量用户的平台,国外的比如Facebook,Twitter等,国内的比如微博、微信、QQ等。 第三方登录的目的是使用用户在其他平台上频繁使用的账号,来快速登内录己方产品,也可以实现不注容册就能登录,好处就是登录比较快捷,不用注册。
2.2.为什么需要

用户:少记忆账号了
2.3.优缺点
优点:这些系统有很大的用户群体;有这些大平台背景,用户的接收度和认可度较好;直接登录,避免用户注册和登录的繁琐过程。这样,可以比较好的推广自己的网站和粘主用户。
缺点:要交钱,除了三方登录还要实现其他的。

2.4.使用场景
面向互联网用户一般都需要三方登录。

3.三方登录协议-OAuth2.0

OAUTH协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是OAUTH的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此OAUTH是安全的。oAuth是Open Authorization的简写,目前的版本是2.0版。

https://oauth.net/2/
3.1.应用场景
为了让大家理解OAuth的适用场合,下面我先举一个栗子。
有一个“云打印”的网站,可以将用户存储在QQ的照片打印出来。用户为了使用该服务,必须让“云打印”读取自己存储在QQ上的照片。

问题是,只有得到用户的授权,QQ才同意“云打印”读取这些照片。那么“云打印”如何才能获得QQ用户的授权呢?有比较传统的方法,用户自己将QQ的用户名和密码告诉“云打印”,后者就可以读取用户的照片了。但是,这么做会有以下几个严重的缺点:

(1)“云打印”为了后续的服务,会保存用户的用户名和密码,这样很不安全。
(2)“云打印”拥有了获取用户在QQ上所有资料的权利,用户没有办法限制“云打印”的访问资 源的权限和有限期。
(3)用户只有修改密码,才能收回“云打印”的权限。但是这么做,会使得其他所有获得用户授权 的第三方应用程序全部失效。
(4)只要有一个第三方应用程序被破解,就会导致用户密码泄露,以及所有被这个密码保护的数据 也会泄露

OAuth就是为了解决上面这些问题而诞生的。 我们平台需要保存三方平台账号和密码
3.2.名词定义
在详细介绍OAuth2.0之前,需要了解几个专有名词,这些名词经常出现在各种应用场合,对于我们理解RCF 6749的内容至关重要。

(1)Third-party application:第三方应用程序,本文中又称"客户端"(client),即栗子中的"云打印"。
(2)HTTP service:HTTP服务提供商,本文中简称"服务提供商",即上一节例子中的QQ。
(3)Resource Owner:资源所有者,本文中又称"用户"(user)。
(4)User Agent:用户代理,本文中就是指浏览器。
(5)Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。
(6)Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器

了解了上面的名词,就不难理解OAuth的作用就是让“客户端”安全可控的获取“用户”的授权,与“服务提供商”进行互动。

3.3.OAuth的思路
OAuth在"客户端"与"服务提供商"之间,设置了一个授权层(authorization layer)。“客户端"不能直接登录"服务提供商”,只能登录授权层,以此将用户与客户端区分开来。"客户端"登录授权层使用的是令牌(token),与用户的密码不同。用户可以在登录的时候,指定令牌的权限范围和有效期。

"客户端"登录授权层以后,"服务提供商"根据令牌的权限范围和有效期,向"客户端"开放用户储存的资料。

3.4.运行流程
OAuth 2.0的运行流程如下图,摘自RCF 6749。

用户授权
获取令牌
访问受限资源

(A)用户打开客户端以后,客户端要求用户给予授权。
(B)用户同意给予客户端授权。
(C)客户端使用上一步获得的授权,向认证服务器申请令牌。
(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
(E)客户端使用令牌,向资源服务器申请获取资源。
(F)资源服务器确认令牌无误,同意向客户端开放资源。

上述六个步骤中,B是关键,即用户怎样才能给客户端授权。有了这个授权以后,客户端就可以获取令牌,进而凭借令牌获取资源。
3.5.客户端授权 - 授权码模式
客户端必须得到用户的授权,才能获得令牌,OAuth2.0定义了四种授权方式:
授权码模式(authorization code)
简化模式(implicit)
密码模式(resource owner password credentials)
客户端模式(client credentials)
这里,我们主要介绍一下授权码模式:
授权码模式是功能最完整,流程最严密的授权模式。它的特点是通过客户端的后台服务器,与“服务提供商”的认证服务器进行互动,先看下面一张图。

(A)用户访问客户端,后者将前者导向认证服务器。
(B)用户选择是否给予客户端授权。
(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI",同时附上一个授权 码。
(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后 台的服务器上完成的,对用户不可见。
(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和 更新令牌(refresh token)。

4.三方登录服务商选择-微信三方登录

4.1.为什么选择微信作为三方登录服务提供方
三方登录产品有很多,比如微信登录,qq登录,支付宝登录,新浪微博登录.但是都是遵循oauth2.0协议,只要学会一个其他就触类旁通.
微信,在做的各位都使用很频繁的社交软件,老少皆宜。今天我们以微信为例进行讲解。
微信开发平台供开发者可以基于微信做很多事情:
https://open.weixin.qq.com/

自己的网站可以接入网站应用开发,为用户提供了微信登录功能,降低了注册门槛,并可在用户授权后,获取用户基本信息,包括头像、昵称、性别、地区。出于安全考虑,网站应用的微信登录,需通过微信扫描二维码来实现。

4.2.注册账号
要想接入微信的登录功能,首先需要在微信开发平台进行用户的注册,同时需要认证为开发者,再创建网站应用,等待微信审批,审批过后,就可以使用相关功能。
4.2.1.注册
打开微信开发平台(https://open.weixin.qq.com/):

使用邮箱进行注册(最好使用企业邮箱,避免使用私人邮箱,后续需要企业的资质认证等,避免你离职后的交接麻烦):


4.2.2.开发者认证
注册完成后,需要进行开发者的认证:

登录后,点击登录名,进入:
大致需要个人身份证等真实信息,还需要企业签字盖章等流程,认证一次300人民币。

4.2.3.创建网站应用
认证成功后,创建网站应用,也需要企业签字盖章,还需要备案的域名,作为微信的回调。

创建完成后,获取到appid和appsecret,配置好回调域名。

4.3.网站微信登录原理
一切参照官方文档:

4.4.配置回调域名
上线:

开发阶段:
路由解析原理

6.4 配置电脑HOST文件
Host文件配置
127.0.0.1 bugtracker.itsource.cn

通过原理分析,要发三个请求
1)授权请求-a标签链接过去就OK
2)获取AccessToken等信息请求-Httpclient
在我们项目中以后这样用代码来发送请求的情况有可能很多,所以抽取一个工具类,以后专门发请求。

5.微信三方登录项目实战

5.1.设计
5.1.1.数据库设计

5.1.2.第三方登录流程分析

5.2. 实现准备
5.2.1.工具类的封装

package cn.itsource.basic.util;import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;import java.io.IOException;/*** 使用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 创建要发起请求-tetGetMethod getMethod = new GetMethod(url);
//            getMethod.addRequestHeader("Content-Type",
//                    "application/x-www-form-urlencoded;charset=UTF-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;}
}

5.2.2.常量封装

package cn.itsource.user.constant;//微信登录相关常量
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?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";public static final String GET_USER_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";}

5.2.3.检查配置回调域名hosts配置

5.3.核心代码实现

<!--处理json--><!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.58</version></dependency>

5.3.1.跳转授权界面

Login.html
<script type="text/javascript">//创建vue是需要给他传递一个对象new Vue({el: "#loginMain", //id在hmtl模板中要对应data: { //数据模型loginForm: {username: '',password: ''},wxAuthUrl:' https://open.weixin.qq.com/connect/qrconnect?appid=wxd853562a0548a7d0' +'&redirect_uri=http://bugtracker.itsource.cn/callback.html&response_type=code&scope=snsapi_login&state=1#wechat_redirect'},

5.3.2.回调页面

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>回调</title><!--集成vue和axios,还要axios全局配置(导入common.js)--><!--script src方式引入vue和axios--><script src="js/plugins/vue/dist/vue.js"></script><script src="js/plugins/axios/dist/axios.js"></script><!--全局配置,以后只要用vue+axios的页面都引入common.js--><script src="js/common.js"></script>
</head>
<body><div id="myDiv"></div><script type="text/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存放到localStoragelet {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>

5.3.3.跳转后台微信登录
LoginInfocontroller 判断是否已经绑定,如果绑定就免密登录,否则返回一个未绑定错误,配合前端跳转到绑定页面

@PostMapping("/wechat")
public AjaxResult loginWechat(@RequestBody Map<String,String> params){try {return  loginInfoService.loginWechat(params);} catch (Exception e) {e.printStackTrace();return AjaxResult.me().setMessage("系统错误!"+e.getMessage());}
}LoginInfoServiceImpl@Override
public AjaxResult loginWechat(Map<String, String> params) {//1 获取codeString code = params.get("code");String binderUrl = params.get("binderUrl");//2 获取accessTokenString 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){//查询LogininfoLoginInfo loginInfo =loginInfoMapper.loadByUserId(wxUser.getUser_id());//3.1 如果关联了实现免密登录String token = UUID.randomUUID().toString();redisTemplate.opsForValue().set(token,loginInfo,30, TimeUnit.MINUTES);Map<String,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+"?accessToken="+accessToken+"&openId="+openid;return AjaxResult.me().setSuccess(false).setResultObj(binderUrl);}
}

5.3.4.绑定页面

<!DOCTYPE html>
<html><head lang="en"><meta charset="UTF-8"><title>注册</title><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"><meta name="format-detection" content="telephone=no"><meta name="renderer" content="webkit"><meta http-equiv="Cache-Control" content="no-siteapp" /><link rel="stylesheet" href="./AmazeUI-2.4.2/assets/css/amazeui.min.css" /><link href="./css/dlstyle.css" rel="stylesheet" type="text/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 src="js/plugins/vue/dist/vue.js"></script><script src="js/plugins/axios/dist/axios.js"></script><!--全局配置,以后只要用vue+axios的页面都引入common.js--><script src="js/common.js"></script><!--一个一个页面配置,搞一个公共common.js,以后只需要引入它就ok--><!--<script type="text/javascript">--><!--//配置axios的全局基本路径--><!--axios.defaults.baseURL='http://localhost:8080/'--><!--// axios.defaults.baseURL='/api' //前端跨域配置--><!--//全局属性配置,在任意组件内可以使用this.$http获取axios对象--><!--Vue.prototype.$http = axios--><!--</script>--></head><body><div class="login-boxtitle"><a href="home/demo.html"><img alt="" src="./images/logobig.png" /></a></div><div class="res-banner"><div class="res-main"><div class="login-banner-bg"><span></span><img src="./images/big.jpg" /></div><div class="login-box"><div class="am-tabs" id="doc-my-tabs"><div class="am-tabs-bd"><div class="am-tab-panel am-active"id="myDiv" ><form method="post"><div class="user-phone"><label for="phone"><i class="am-icon-mobile-phone am-icon-md"></i></label><input type="tel" name="" id="phone" v-model="phoneUserForm.phone" placeholder="请输入手机号"></div>                                                          <div class="verification"><label for="code"><i class="am-icon-code-fork"></i></label><input type="tel" name="" id="code" v-model="phoneUserForm.verifyCode" placeholder="请输入验证码"><!--<a class="btn" href="javascript:void(0);" onclick="sendMobileCode();" id="sendMobileCode">--><!--<span id="dyMobileButton">获取</span></a>--><button type="button" @click="sendMobileCode">获取</button></div></form><div class="login-links"><label for="reader-me"><input id="reader-me" type="checkbox"> 点击表示您同意商城《服务协议》</label></div><div class="am-cf"><input type="button" @click="binder" name="" value="绑定授权" class="am-btn am-btn-primary am-btn-sm am-fl"></div><hr></div><script>$(function() {$('#doc-my-tabs').tabs();})</script></div></div></div></div><div class="footer "><div class="footer-hd "><p><a href="# ">恒望科技</a><b>|</b><a href="# ">商城首页</a><b>|</b><a href="# ">支付宝</a><b>|</b><a href="# ">物流</a></p></div><div class="footer-bd "><p><a href="# ">关于恒望</a><a href="# ">合作伙伴</a><a href="# ">联系我们</a><a href="# ">网站地图</a><em>© 2015-2025 Hengwang.com 版权所有. 更多模板 <a href="http://www.cssmoban.com/" target="_blank" title="模板之家">模板之家</a> - Collect from <a href="http://www.cssmoban.com/" title="网页模板" target="_blank">网页模板</a></em></p></div></div></body><script type="text/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存放到localStoragelet {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>

改造发送短信验证接口

@RestController
@RequestMapping("/verifycode")
public class VerifyCodeController {@Autowiredprivate IVerifyCodeService verifyCodeService;//一定情况下Map能够代替类使用@PostMapping("/smsCode") //注册验证码public AjaxResult sendSmsCode(@RequestBody Map<String,String> params){String phone = params.get("phone");String type = params.get("type"); //register binder logintry {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
@Overridepublic void sendSmsCode(Map<String,String> params) {String phone = params.get("phone");String type = params.get("type");//1 校验//1.1 手机号不能为nullif (!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:timeString code = "";//2.1 如果有效if (codeObj!=null){String codeStr = (String) codeObj;//2.1.1 判断是否已过重发时间String time = codeStr.split(":")[1]; //114555558888long intervalTime = System.currentTimeMillis() - Long.valueOf(time);if (intervalTime<=1*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 保存验证码到redisredisTemplate.opsForValue().set(businessKey,code+":"+System.currentTimeMillis(),3, TimeUnit.MINUTES);//4 调用短信接口发送短信
//        SmsUtil.sendMsg(phone,"您的验证码为:"+code+",请在3分钟之内使用!");System.out.println("您的验证码为:"+code+",请在3分钟之内使用!");}

5.3.5.绑定实现

LoginController
@PostMapping("/binder/wechat")
public AjaxResult binderWechat(@RequestBody Map<String,String> params){try {return  loginInfoService.binderWechat(params);} catch (Exception e) {e.printStackTrace();return AjaxResult.me().setMessage("系统错误!"+e.getMessage());}
}
LoginInfoService
//前台输入手机号是否有账号,如果有创建wxUser帮上就ok,如果没有创建账号在绑定
@Override
public AjaxResult binderWechat(Map<String, 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(codeObj==null){return AjaxResult.me().setMessage("请重新获取验证码后再操作!");}else{String codeStr = (String) codeObj;String code = codeStr.split(":")[0];//code:timeif (!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 (info==null){user = wxUser2User(phone);info = user2LoginInfo(user);//3.1 创建loginInfologinInfoMapper.save(info);//3.2 创建Useruser.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);Map<String,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;
}

5.3.6.后台获取登录用户

package cn.itsource.basic.util;import cn.itsource.user.domain.LoginInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;import javax.servlet.http.HttpServletRequest;/*** 登录的上下文*    登录用户的一些信息放到这里面,以后直接获取就ok了*    1 获取登录用户*    2 获取登录用户的角色或权限*    ....* ==========只是一个工具类不需要交给Spring管理=================** ?? 一个不受spirng管理的bean,要获取受spring管理的bean*/
public class LoginContext {/*** 获取当前登录用户信息* @param request* @return*/public static LoginInfo getLoginInfo(HttpServletRequest request){//从请求头中获取tokenString token = request.getHeader("token");//使用token从redis中获取登录信息if (!StringUtils.isEmpty(token)){//1 获取spring容器WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());//2 通过容器获取beanRedisTemplate redisTemplate = (RedisTemplate) context.getBean("redisTemplate");//3 获取登录信息Object loginInfoObj = redisTemplate.opsForValue().get(token);if (loginInfoObj!=null)return (LoginInfo) loginInfoObj;}return null;}
}

兄弟们,就是干 微信三方登录绝对干货相关推荐

  1. 微信php第三方登录接口,ThinkPHP实现微信三方登录

    小插曲就是app做微信三方登录是很久之前,后面又添加了PC的微信三方登陆,而文档上说unionid是同一个账号下不同应用统一的,但是app拿的是uid,导致pc拿的unionid始终对不上,导致浪费了 ...

  2. 拉起微信三方登录,详细实现步骤

    概述:本篇文章详细记录网页做微信登录的功能 用户一次扫微信登录,直接拉取回调页面,绑定手机号,后端处理数据,为用户生成密码,短信告知用户. 用户第二次扫码.直接登录成功. 这次三方登录利用的Sprin ...

  3. 微信三方登录与注册逻辑处理

    微信,作为三方接口的处理逻辑.以下是个人项目处理的逻辑,欢迎大家吐槽! 这边展示项目部分代码 /*** 微信 登录与注册* @param request 请求* @param code 要解析的cod ...

  4. 微信三方登录相关(Swift)

    微信登录条件 1.微信开放平台注册并认证成功(每年300RMB) 2.相关应用的微信的APPID和secret 3. 遵循微信代理WXApiDelegate 使用处编写相关登录代码 WXApi.reg ...

  5. A072_前台登录_三方登录

    目录 内容介绍 1. 前台登录-账号登录 1.1.前台登录 1.2.axios携带token-common.js 1.3.axios后置处理后台拦截错误-common.js 1.4.前台登录拦截-co ...

  6. Android开发丶集成微信原生登录

    好久没写博客了,大概是与ReactNative大战半月已经有点疲惫了,说起集成微信三方登录,还记得上次实现功能还是刚参加工作时,用Mob的ShareSDK来集成实现的,该平台集成了数个主流平台的分享和 ...

  7. php 微信第三方登录demo,第三方登录 - 快速接入微信、QQ、微博等第三方登录方式 – 基于ThinkPHP和Bootstrap的极速后台开发框架...

    此插件基于FastAdmin和Thinkphp5开发的第三方登录插件,可用于对接微.微博.QQ等第三方登录,目前CMS中的小程序账号登录绑定就是基于此插件开发,在使用CMS中的小程序登录功能之前请务必 ...

  8. Android接入三方登录——QQ、微信、Facebook、Twitter

    Android接入三方登录--QQ.微信.Facebook.Twitter 避坑指南 facebook错误1: facebook错误2: QQ 微信 Facebook Twitter 补充: 避坑指南 ...

  9. 企业微信三方开发(三):网页授权登录

    其他链接 初识微信开发 企业微信三方开发:注册企业微信服务商 企业微信三方开发(一):回调验证及重要参数获取 企业微信三方开发(二):获取access_token 企业微信三方开发(三):网页授权登录 ...

最新文章

  1. UVA 10341 二分搜索
  2. 系统安装重装与优化:chapter7 操作系统的修复与重装
  3. 最小径集的算法_如何为数据集选择正确的聚类算法?
  4. 浅析bootstrap原理及优缺点
  5. (11)verilog语言编写8路分配器
  6. 横空出世,席卷互联网--评微软等公司数据结构和算法面试100题 .
  7. monkey测试_adb monkey压力测试检测安卓手机的5大步骤,你知道吗?
  8. 阿里云容器Kubernetes监控(五) - 离线存储与归档Kubernetes事件
  9. 【USACO】sprime
  10. 2021-2022学期计划
  11. Shell编程—【03】数学运算expr与bc浮点数运算
  12. C#人事工资管理系统
  13. 恒生UFX 统一接入介绍
  14. 如何在线压缩图片?电脑怎么缩小图片kb大小?
  15. 小案例:利用图床自动化批量上传图片并获取图片链接
  16. http://gm100861.blog.51cto.com/1930562/954333
  17. Hello Qt——Qt自定义标题栏
  18. 处理器后面的字母含义_笔记本处理器型号认识
  19. DGL dist sampling OP
  20. gdb调试,GDB调试opencore源码

热门文章

  1. 冰冰学习笔记:内存操作函数
  2. Linux Shell 经典实例(1-30)
  3. 视频教程-【孙伟】APP项目UI设计基础-工具图标设计视频教程-UI
  4. 片袖原型制图_合体连身袖原型造型原理和制图步骤
  5. 铝桁架梁的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  6. 在硬盘留下后门,重装系统都没辙
  7. oracle认证方式分为操作系统认证和…
  8. 公司考勤系统项目设计
  9. jmeter安装 java,JMeter下载、安装、配置完整版(windows 10环境)
  10. 网站关键词排名下降怎么办?