1.官方网站:微信登录功能 / 网页应用授权用户信息变更 (qq.com)
2.登陆流程:
和登陆微信没关系,是用户的微信号,和我程序的程序编号(微信给的 目前用的老师的),去请求微信的接口,微信给这个用户一个token,这个token在本系统中 只和该用户对应。登陆成功。
实际上有点出入,是用户扫码后确定登录后,自动去请求微信的接口,我拿不到用户的微信号。
3.具体实现
1》用户在页面点击登陆按钮,前端发送请求给后端
<li><a id="weixin" class="weixin" target="_blank" href="http://localhost:8160/eduuser/usercenter/wx/login"><i class="iconfont icon-weixin"/></a></li>
2》后端接收到请求,通过重定向去请求微信接口。返回重定向地址后,此时浏览器页面会自动新建一个页签,是微信的二维码登陆界面。
重定向到微信接口需要参数 appid这里使用谷粒学院老师的。
重定向地址是自己写的,在扫码之后微信会访问这个地址。
其他参数不清楚。
@GetMapping("login")public String genQrConnect(HttpSession session) {// 微信开放平台授权baseUrlString baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +"?appid=%s" +"&redirect_uri=%s" +"&response_type=code" +"&scope=snsapi_login" +"&state=%s" +"#wechat_redirect";// 回调地址String redirectUrl = ConstantWxUtils.REDIRECT_URL; //获取业务服务器重定向地址try {redirectUrl = URLEncoder.encode(redirectUrl, "UTF-8"); //url编码} catch (UnsupportedEncodingException e) {throw new GuLiExeception(20001, e.getMessage());}// 防止csrf攻击(跨站请求伪造攻击)//String state = UUID.randomUUID().toString().replaceAll("-", "");//一般情况下会使用一个随机数String state = "imhelen";//为了让大家能够使用我搭建的外网的微信回调跳转服务器,这里填写你在ngrok的前置域名// 采用redis等进行缓存state 使用sessionId为key 30分钟后过期,可配置//键:"wechar-open-state-" + httpServletRequest.getSession().getId()//值:satte//过期时间:30分钟//生成qrcodeUrlString qrcodeUrl = String.format(baseUrl,ConstantWxUtils.APP_ID,redirectUrl,state);return "redirect:" + qrcodeUrl;}
3.用户在扫码登陆之后,微信会访问第二步携带的回调接口,并携带这个一个临时参数,在controller中获得此参数后,携带secret,访问正式的三方登陆接口,会返回access_token,这个access_token就是 该微信用户,在本系统中,唯一的token,token和用户是一对一的关系,微信登陆完成。
获得这个token后,可以再请求一次微信用户信息接口,获取用户的头像、微信名信息。
在获取完用户的头像、微信名信息后,重定向到前端地址,以重新加载刚才的扫码页面,并且在路径中携带token,前端发现路径中有token后,获取这个token,并携带这个token访问获取用户信息接口,获取用户信息。
@GetMapping("callback")public String callBack(String code, String state) {//从redis中将state获取出来,和当前传入的state作比较//如果一致则放行,如果不一致则抛出异常:非法访问//向认证服务器发送请求换取access_tokenString baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +"?appid=%s" +"&secret=%s" +"&code=%s" +"&grant_type=authorization_code";String accessTokenUrl = String.format(baseAccessTokenUrl,ConstantWxUtils.APP_ID,ConstantWxUtils.APP_SECRECT,code);// 使用httpClient发送请求,得到返回结果JSONObject accesstokenInfo = HttpClientUtils.httpGet(accessTokenUrl);String access_token = (String) accesstokenInfo.get("access_token");String openid = (String) accesstokenInfo.get("openid");LambdaQueryWrapper<UcenterMember> lambdaQueryWrapper = new LambdaQueryWrapper();lambdaQueryWrapper.eq(UcenterMember::getOpenid, openid);UcenterMember checkUser = ucenterMemberService.getOne(lambdaQueryWrapper);if (checkUser == null) {//访问微信的资源服务器,获取用户信息String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +"?access_token=%s" +"&openid=%s";String userInfoUrl = String.format(baseUserInfoUrl, access_token, openid);try {JSONObject jsonObject = HttpClientUtils.httpGet(userInfoUrl);String nickName = jsonObject.getString("nickname");String headUrl = jsonObject.getString("headimgurl");UcenterMember ucenterMember = new UcenterMember(openid, nickName, headUrl);ucenterMemberService.save(ucenterMember);String jwtToken = JwtUtils.getJwtToken(ucenterMember.getId(), nickName);return "redirect:http://localhost:3000?guli_token=" + jwtToken;} catch (Exception e) {throw new GuLiExeception(20001, "用户未注册,获取腾讯用户信息失败");}}String jwtToken = JwtUtils.getJwtToken(checkUser.getId(), checkUser.getNickname());String temp=JwtUtils.getMemberIdByJwtTokenString(jwtToken);System.out.println("=======");System.out.println(temp);return "redirect:http://localhost:3000?guli_token="+jwtToken;}
前端代码, 前端发现路径中有token后,获取这个token,并携带这个token访问获取用户信息接口,获取用户信息。这是default.vue,当用户没有输入访问的具体地址时,默认访问此界面。
<template><div class="in-wrap"><!-- 公共头引入 --><header id="header"><section class="container"><h1 id="logo"><a href="#" title="谷粒学院"><img src="~/assets/img/logo.png" width="100%" alt="谷粒学院"></a></h1><div class="h-r-nsl"><ul class="nav"><router-link to="/" tag="li" active-class="current" exact><a>首页</a></router-link><router-link to="/course" tag="li" active-class="current"><a>课程</a></router-link><router-link to="/teacher" tag="li" active-class="current"><a>名师</a></router-link><router-link to="/article" tag="li" active-class="current"><a>文章</a></router-link><router-link to="/qa" tag="li" active-class="current"><a>问答</a></router-link></ul><ul class="h-r-login"><li v-if="!loginInfo.id" id="no-login"><a href="/login" title="登录"><em class="icon18 login-icon"> </em><span class="vam ml5">登录</span></a>|<a href="/register" title="注册"><span class="vam ml5">注册</span></a></li><li v-if="loginInfo.id" id="is-login-one" class="mr10"><a id="headerMsgCountId" href="#" title="消息"><em class="icon18 news-icon"> </em></a><q class="red-point" style="display: none"> </q></li><li v-if="loginInfo.id" id="is-login-two" class="h-r-user"><a href="/ucenter" title><img:src="loginInfo.avatar"width="30"height="30"class="vam picImg"alt/><span id="userName" class="vam disIb">{{loginInfo.nickname}}</span></a><ahref="javascript:void(0);"title="退出"@click="logout()"class="ml5">退出</a></li><!-- /未登录显示第1 li;登录后显示第2,3 li --></ul><aside class="h-r-search"><form action="#" method="post"><label class="h-r-s-box"><inputtype="text"placeholder="输入你想学的课程"name="queryCourse.courseName"value/><button type="submit" class="s-btn"><em class="icon18"> </em></button></label></form></aside></div><aside class="mw-nav-btn"><div class="mw-nav-icon"></div></aside><div class="clear"></div></section></header><!-- /公共头引入 --><nuxt /><!-- 公共底引入 --><footer id="footer"><section class="container"><div class><h4 class="hLh30"><span class="fsize18 f-fM c-999">友情链接</span></h4><ul class="of flink-list"><li><a href="http://www.atguigu.com/" title="尚硅谷" target="_blank">尚硅谷</a></li></ul><div class="clear"></div></div><div class="b-foot"><section class="fl col-7"><section class="mr20"><section class="b-f-link"><a href="#" title="关于我们" target="_blank">关于我们</a>|<a href="#" title="联系我们" target="_blank">联系我们</a>|<a href="#" title="帮助中心" target="_blank">帮助中心</a>|<a href="#" title="资源下载" target="_blank">资源下载</a>|<span>服务热线:010-56253825(北京) 0755-85293825(深圳)</span><span>Email:info@atguigu.com</span></section><section class="b-f-link mt10"><span>©2018课程版权均归谷粒学院所有 京ICP备17055252号</span></section></section></section><aside class="fl col-3 tac mt15"><section class="gf-tx"><span><img src="~/assets/img/wx-icon.png" alt /></span></section><section class="gf-tx"><span><img src="~/assets/img/wb-icon.png" alt /></span></section></aside><div class="clear"></div></div></section></footer><!-- /公共底引入 --></div>
</template>
<script>
import "~/assets/css/reset.css";
import "~/assets/css/theme.css";
import "~/assets/css/global.css";
import "~/assets/css/web.css";
import "~/assets/css/base.css";
import "~/assets/css/activity_tab.css";
import "~/assets/css/bottom_rec.css";
import "~/assets/css/nice_select.css";
import "~/assets/css/order.css";
import "~/assets/css/swiper-3.3.1.min.css";
import "~/assets/css/pages-weixinpay.css";
import cookie from "js-cookie";
import loginApi from "@/api/login";
export default {data() {return {guli_token: "",loginInfo: {id: "",age: "",avatar: "",mobile: "",nickname: "",sex: "",},};},created() {// 获取路径中的token中的值this.guli_token = this.$route.query.guli_token;if (this.guli_token) {cookie.set("guli_token", this.guli_token, { domain: "localhost" });console.log("执行微信登录");//如果可以取到token,用微信的方式登录this.wxLogin();}this.showInfo();},methods: {//创建方法,从cookie中获取用户信息showInfo() {var userStr = cookie.get("guli_ucenter");if (userStr) {//把字符串转换为JSON对象this.loginInfo = JSON.parse(userStr);}},//退出logout() {// domain 作用范围cookie.set("guli_ucenter", "", { domain: "localhost" });cookie.set("guli_token", "", { domain: "localhost" });console.log("回首页");//回到首页面window.location.href = "/";},//微信登陆wxLogin() {if (this.guli_token == "") return;//登录成功根据token获取用户信息loginApi.getLoginInfo().then((response) => {this.loginInfo = response.data.data.member;//将用户信息记录cookiecookie.set("guli_ucenter", JSON.stringify(this.loginInfo), {domain: "localhost",});});},},
};
</script>
后端接收到前端发送的token,根据这个token查询用户信息并返回(用户信息在第三步上面已经获取到,并且保存数据库,这里只查就可以)
@GetMapping("/info")public R geInfo(HttpServletRequest request) {String headers = "";Enumeration<String> names = request.getHeaderNames();while (names.hasMoreElements()) {String name = names.nextElement();String value = request.getHeader(name);headers += name + "=" + value + "\n";}System.out.println(headers + "请求用户信息请求头如上");String memberId = JwtUtils.getMemberIdByJwtToken(request);UcenterMember uCenterMember = ucenterMemberService.getById(memberId);return R.ok().data("member", uCenterMember);}