目录

    • 第一步:生成微信扫描二维码(网页内嵌的二维码)接口
    • 第二步:处理微信回调 ,获取扫描人信息

微信开放平台:

https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html

第一步:生成微信扫描二维码(网页内嵌的二维码)接口

提供了第二种获取 code 的方式,支持网站将微信登录二维码内嵌到自己页面中,用户使用微信扫码授权后通过 JS 将code返回给网站。 JS微信登录主要用途:网站希望用户在网站内就能完成登录,无需跳转到微信域下登录后再返回,提升微信登录的流畅性与成功率。 网站内嵌二维码微信登录 JS 实现办法:

步骤1:在页面中先引入如下 JS 文件(支持https):

http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js

步骤2:在需要使用微信登录的地方实例以下 JS 对象:

var obj = new WxLogin({ self_redirect:true, id:"login_container",  appid: "",  scope: "",  redirect_uri: "",  state: "", style: "", href: "" });

参数说明

我们需要写必须的那几个参数;

生成微信扫描二维码(网页内嵌的二维码)接口.此几口返回的数据是:生成二维码需要的参数

注意:接口的后台需要准备这几个参数:

appid : 应用唯一标识,在微信开放平台提交应用审核通过后获得
scope :应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可
redirect_uri : 重定向地址,需要进行UrlEncode
state : 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止 csrf 攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加 session 进行校验

1.需要在显示二维码的页面 引入前端api接口:

import weixinApi from ‘@/api/weixin’

2.然后在 mounted() 方法中添加以下方法

  mounted() { //初始化微信js    const script = document.createElement('script')    script.type = 'text/javascript'    script.src = 'https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js'    document.body.appendChild(script)   },

3、实例化微信JS对象
添加微信登录方法,即点击微信登录按钮,触发展示微信二维码的方法:

weixinLogin() {  this.dialogAtrr.showLoginType = 'weixin'  weixinApi.getLoginParam().then(response => {    var obj = new WxLogin({      self_redirect:true,      id: 'weixinLogin', // 需要显示的容器id      appid: response.data.appid, // 公众号appid wx*******      scope: response.data.scope, // 网页默认即可      redirect_uri: response.data.redirectUri, // 授权成功后回调的url      state: response.data.state, // 可设置为简单的随机数加session用来校验      style: 'black', // 提供"black"、"white"可选。二维码的样式      href: '' // 外部css文件url,需要https    })  })},

然后记得网关设置:

测试二维码展示:

第二步:处理微信回调 ,获取扫描人信息

手机点击确认后的:

请求网址: http://localhost:8160/api/ucenter/wx/callback” />

  1. 客户应用请求授权服务器获取Access Token
  2. 授权服务器咨询用户意见
  3. 用户同意授权
  4. 授权服务器颁发Access Token 给 客户应用

创建工具类:httpclint:

在server_user模块:

在common-util中引入httpclient依赖:

                    org.apache.httpcomponents            httpclient        

第二步:通过 code 获取access_token

通过 code 获取access_token

https://api.weixin.qq.com/sns/oauth2/access_token" />






说明:我们根据返回openid判断是否需要绑定手机号码,如果需要绑定,那么我们要根据openid用户用户信息,然后更新上手机号码

前端:

3.4 回调返回页面
操作:yygh-site
说明:我们只期望返回一个空页面,然后跟登录层通信就可以了,其实就是一个过渡页面,所以我们要给这个过渡页面定义一个空模板

3.4.2回调返回页面
根据返回路径/weixin/cakkback,我们创建组件/weixin/cakkback.vue

说明:在页面我们就能够接收到返回来的参数

3.4.3 父组件定义回调方法
在myheader.vue添加方法

mounted() 中增加方法:

 // 微信登录回调处理   let self = this;   window["loginCallback"] = (name,token, openid) => {     self.loginCallback(name, token, openid);   }

然后methods中增加方法:

loginCallback(name, token, openid) {      // 打开手机登录层,绑定手机号,改逻辑与手机登录一致      if(openid != '') {        this.userInfo.openid = openid        this.showLogin()      } else {        this.setCookies(name, token)      }    },

3.5 服务器绑定手机号码
页面绑定手机号码会把openid传递过来,我们根据openid找到用户信息,然后绑定手机号码
修改UserInfoServiceImpl类登录方法

 //绑定手机号码        UserInfo userInfo = null;        if(!StringUtils.isEmpty(loginVo.getOpenid())) {            userInfo = this.selectWxInfoOpenId(loginVo.getOpenid());            if(null != userInfo) {                userInfo.setPhone(loginVo.getPhone());                this.updateById(userInfo);            } else {                throw new YyghException(ResultCodeEnum.DATA_ERROR);            }        }        if(userInfo == null){            //判断是否第一次登录,根据手机号查询数据库,如果不存在相同的手机号就是第一次登录            QueryWrapper wrapper = new QueryWrapper();            wrapper.eq("phone",phone);             userInfo = baseMapper.selectOne(wrapper);            if(null==userInfo ){//第一次使用这个手机号登录                //添加信息到数据库                userInfo = new UserInfo();                userInfo.setName("");                userInfo.setPhone(phone);                userInfo.setStatus(1);//设置可用                //this.save(userInfo);//保存到数据库                baseMapper.insert(userInfo);            }        }

package com.fan.yygh.user.controller;import com.alibaba.fastjson.JSONObject;import com.fan.yygh.common.exception.YyghException;import com.fan.yygh.common.result.Result;import com.fan.yygh.common.result.ResultCodeEnum;import com.fan.yygh.helper.JwtHelper;import com.fan.yygh.model.user.UserInfo;import com.fan.yygh.user.service.UserInfoService;import com.fan.yygh.user.utils.ConstantPropertiesUtil;import com.fan.yygh.user.utils.HttpClientUtils;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import org.springframework.stereotype.Controller;import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import javax.annotation.Resource;import java.io.UnsupportedEncodingException;import java.net.URLEncoder;import java.util.HashMap;import java.util.Map;//微信操作的接口@Api("微信")@Controller@RequestMapping("/api/ucenter/wx")public class WeixinApiController {    @Resource    private UserInfoService userInfoService;    /**     * 获取微信登录参数     * 1.生成微信扫描二维码     * 返回生成二维码需要的参数     */    @GetMapping("getLoginParam")    @ResponseBody    public Result genQrConnect() throws UnsupportedEncodingException {        String redirect_uri = URLEncoder.encode(                ConstantPropertiesUtil.WX_OPEN_REDIRECT_URL, "UTF-8");        Map map = new HashMap();        map.put("appid", ConstantPropertiesUtil.WX_OPEN_APP_ID);        map.put("redirect_uri", redirect_uri);        map.put("scope", "snsapi_login");        map.put("state", System.currentTimeMillis()+"");//System.currentTimeMillis()+""        return Result.ok(map);    }    //微信扫描后回调的方法    @GetMapping("callback")    public String callback(String code,String state){        //第一步,获取 临时授权临时票据code        System.out.println("code:"+code);        //第二步:拿着code和微信appid和秘钥appscrect(三个参数换一个参数),--》请求微信固定地址--》换取access_token        StringBuffer baseAccessTokenUrl = new StringBuffer()                .append("https://api.weixin.qq.com/sns/oauth2/access_token")                .append("" /> map = new HashMap();            String name = userInfo.getName();            if(StringUtils.isEmpty(name)){                name = userInfo.getNickName();            }            if(StringUtils.isEmpty(name)){                name = userInfo.getPhone();            }            map.put("name",name);//将名字放入map            //要求每个微信用户要绑定手机号,判断userInfo是否有手机号,            // 如果手机号为空,返回openid            //如果手机号不为空,返回openid值是空字符串            //前端判断:如果openid不为空,绑定手机号,如果openid为空,不需要绑定手机号            if(StringUtils.isEmpty(userInfo.getPhone())){                map.put("openid", userInfo.getOpenid());            }else{                map.put("openid","");            }            //产生token令牌字符串            String token = JwtHelper.createToken(userInfo.getId(), name);            map.put("token",token);//将令牌token放入map            //登录成功后的页面跳转            return "redirect:"+ ConstantPropertiesUtil.YYGH_BASE_URL + "/weixin/callback?token="+                    map.get("token")+"&openid="+                    map.get("openid")+"&name="+URLEncoder.encode(map.get("name"),"utf-8");        } catch (Exception e) {            throw new YyghException(ResultCodeEnum.FETCH_ACCESSTOKEN_FAILD);            //return null;        }    }    /*============我的微信二维码生成的方法====================*/    /*方法返回生成展示二维码的各种前端页面参数*/    @ApiOperation("我的微信二维码生成的方法")    @GetMapping("createWeixinImage")    @ResponseBody  //此注解返回json    public Result   createWeixinImage() throws UnsupportedEncodingException {        //微信官方规定:redirect_uri重定向地址,需要进行UrlEncode        String redirect_uri = URLEncoder.encode(                ConstantPropertiesUtil.WX_OPEN_REDIRECT_URL,                "UTF-8");        HashMap map = new HashMap();        //从官网给的参数中获取appid,appid在配置文件中的        map.put("appid",ConstantPropertiesUtil.WX_OPEN_APP_ID);        //scope:应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可        map.put("scope","snsapi_login");        map.put("redirect_uri",redirect_uri);        /*用于保持请求和回调的状态,授权请求后原样带回给第三方。        该参数可用于防止 csrf 攻击(跨站请求伪造攻击),        建议第三方带上该参数,可设置为简单的随机数加 session 进行校验*/        map.put("state",System.currentTimeMillis()+"");        return Result.ok(map);    }}
package com.fan.yygh.user.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.fan.yygh.common.exception.YyghException;import com.fan.yygh.common.result.ResultCodeEnum;import com.fan.yygh.helper.JwtHelper;import com.fan.yygh.model.user.UserInfo;import com.fan.yygh.user.mapper.UserInfoMapper;import com.fan.yygh.user.service.UserInfoService;import com.fan.yygh.vo.user.LoginVo;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Service;import org.springframework.util.StringUtils;import javax.annotation.Resource;import java.util.HashMap;import java.util.Map;@Service //先继承《M是mapper,T是实体类》后实现public class UserInfoImpl extends ServiceImpl        implements UserInfoService {    @Resource    private UserInfoMapper userInfoMapper;    @Resource    private RedisTemplate redisTemplate;    //判断数据库是否存在微信的扫描人信息,根据openid判断    @Override    public UserInfo selectWxInfoOpenId(String openid) {        QueryWrapper wrapper = new QueryWrapper();        wrapper.eq("openid",openid);        UserInfo userInfo = baseMapper.selectOne(wrapper);        return userInfo;    }    //用户手机号登录接口    @Override    public Map loginUser(LoginVo loginVo) {        //从vo中获取参数        String phone = loginVo.getPhone();        String code = loginVo.getCode();        //参数校验        if(StringUtils.isEmpty(phone) || StringUtils.isEmpty(code)){            throw new YyghException(ResultCodeEnum.PARAM_ERROR);//ResultCodeEnum枚举类        }        //TODO 校验校验验证码        //校验校验验证码        String mobleCode = redisTemplate.opsForValue().get(phone);        if(!code.equals(mobleCode)) {            throw new YyghException(ResultCodeEnum.CODE_ERROR);        }        //绑定手机号码        UserInfo userInfo = null;        if(!StringUtils.isEmpty(loginVo.getOpenid())) {            userInfo = this.selectWxInfoOpenId(loginVo.getOpenid());            if(null != userInfo) {                userInfo.setPhone(loginVo.getPhone());                this.updateById(userInfo);            } else {                throw new YyghException(ResultCodeEnum.DATA_ERROR);            }        }        if(userInfo == null){            //判断是否第一次登录,根据手机号查询数据库,如果不存在相同的手机号就是第一次登录            QueryWrapper wrapper = new QueryWrapper();            wrapper.eq("phone",phone);             userInfo = baseMapper.selectOne(wrapper);            if(null==userInfo ){//第一次使用这个手机号登录                //添加信息到数据库                userInfo = new UserInfo();                userInfo.setName("");                userInfo.setPhone(phone);                userInfo.setStatus(1);//设置可用                //this.save(userInfo);//保存到数据库                baseMapper.insert(userInfo);            }        }        //校验是否被禁用        if(userInfo.getStatus() == 0) {            throw new YyghException(ResultCodeEnum.LOGIN_DISABLED_ERROR);        }        //返回给页面的map信息        HashMap map = new HashMap();        String name = userInfo.getName();        if (StringUtils.isEmpty(name)) {//如果名字为空            name = userInfo.getNickName();//用昵称代替名字        }        if(StringUtils.isEmpty(name)){            name = userInfo.getPhone();//如果名字为空,用手机号代替名字        }        map.put("name",name);//返回name和token        //利用工具生成token        String token = JwtHelper.createToken(userInfo.getId(), name);        map.put("token",token);//token  被写错了        return map;  //记得返回map    }}