Day113.尚医通:微信登录二维码、登录回调接口

目录

一、微信扫描登录准备

二、微信扫描登录二维码

1、后端接口实现

2、前端页面实现

三、微信扫描登录回调接口 - 内网穿透  ★

1、后端接口开发

2、整合微信扫描登录前端


一、微信扫描登录准备

官网:微信开放平台

1. 注册、资质

(1)登录注册

微信开放平台

(2)激活、完善开发者资料

(3)开发者资质认证

(4)创建网站应用

(5)获取开发必要参数

2. 明确三方对接方式

1.SDK:按照要求导入相关依赖 (对接相关信息封装到依赖内)

2. API:访问固定地址,传递固定数目参数,参考API文档。会使用httpclient工具

3. 登录流程

 文档:微信开放平台

时序图|泳道图

二、微信扫描登录二维码

1、后端接口实现

1、查看文档

微信开放平台

方式一:拼写url进行重定向页面跳转

方拾二:通过js实现二维码获取

2、准备工作 

1. 添加配置信息

#没有自动配置类 需要手动读取
# 微信开放平台 appid
wx.open.app_id=你的appid
# 微信开放平台 appsecret
wx.open.app_secret=你的appsecret
# 微信开放平台 重定向url
wx.open.redirect_url=http://你的服务器名称/api/ucenter/wx/callback

2. utils 包下创建常量类

@Component
//@PropertySource("classpath:application.properties") 变量名需要与配置文件相匹配
public class ConstantPropertiesUtil implements InitializingBean {

	@Value("${wx.open.app_id}")
	private String appId;

	@Value("${wx.open.app_secret}")
	private String appSecret;

	@Value("${wx.open.redirect_url}")
	private String redirectUrl;

	public static String WX_OPEN_APP_ID;
	public static String WX_OPEN_APP_SECRET;
	public static String WX_OPEN_REDIRECT_URL;

	@Override
	public void afterPropertiesSet() throws Exception {
		WX_OPEN_APP_ID = appId;
		WX_OPEN_APP_SECRET = appSecret;
		WX_OPEN_REDIRECT_URL = redirectUrl;
	}
}

3. 实现接口

1. 接口分析

*参数:无

*返回值:map (获取二维码参数)

2. 实现 WeixinApiController

@Api(tags = "微信接口")
@Controller
@RequestMapping("/api/ucenter/wx")
public class WeixinApiController {

    //获取微信登录参数
    @GetMapping("getLoginParam")
    @ResponseBody
    public R genQrConnect(HttpSession session) throws UnsupportedEncodingException {
        Map<String, Object> map = new HashMap<>();
        //地址进行URLEncoder编码
        String redirectUri = URLEncoder.encode(ConstantPropertiesUtil.WX_OPEN_REDIRECT_URL, "UTF-8");

        map.put("appid", ConstantPropertiesUtil.WX_OPEN_APP_ID);
        map.put("redirectUri", redirectUri);
        map.put("scope", "snsapi_login");
        map.put("state", System.currentTimeMillis()+"");//System.currentTimeMillis()+""
        return R.ok().data(map);
    }
}

3. 修改端口,添加网关配置

#设置路由id
spring.cloud.gateway.routes[4].id=service-user
#设置路由的uri
spring.cloud.gateway.routes[4].uri=lb://service-user
#设置路由断言,代理servicerId为auth-service的/auth/路径
spring.cloud.gateway.routes[4].predicates= Path=/*/ucenter/**

2、前端页面实现

(1)方式一,拼写url进行重定向 (重定向到一个网页二维码)

(2)方式二,通过js实现二维码获取(选择,用户体验更好,页面中嵌入二维码)

1. 创建API接口方法

创建 api/wx.js

import request from '@/utils/request'

const api_name = `/api/ucenter/wx`

export default {
  getLoginParam() {
    return request({
      url: `${api_name}/getLoginParam`,
      method: `get`
    })
  }
}

2. 确认页面元素

3. 改造JS

import wxApi from '@/api/wx'

        ...
        mounted() {
            // 注册全局登录事件对象
            window.loginEvent = new Vue();
            // 监听登录事件
            loginEvent.$on('loginDialogEvent', function () {
                document.getElementById("loginDialog").click();
            })
            // 触发事件,显示登录层:loginEvent.$emit('loginDialogEvent')
            //初始化微信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)

            // 微信登录回调处理
            let self = this;
            window["loginCallback"] = (name, token, openid) => {
                self.loginCallback(name, token, openid);
            }
        },
        methods: {
            //微信登录回调方法
            loginCallback(name, token, openid) {
                //1.第一次登录,绑定手机号
                //2.不是第一次,设置登录状态
            },
            //切换微信登录
            weixinLogin() {
                this.dialogAtrr.showLoginType = 'weixin'
                wxApi.getLoginParam().then(response => {
                    let 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
                    });
                });
            },

三、微信扫描登录回调接口 - 内网穿透  ★

1、内网穿透

外网请求怎么请求到内网地址

解决方案:

1. 在公司中解决内网穿透  

找运维人员解决,登录交换机控制台,添加内外网的映射关系

例如:192.168.140.100:12345 =>  192.168.43.86:8160

2. 个人解决:修改系统配置文件host,添加域名和ip映射

优点:简单

缺点:只能使用80端口,(只能配置ip映射不能配置端口映射)

3. 个人解决:使用内网穿透工具

优点:可以配置ip映射,也可以配合端口映射

缺点:花钱

4. 个人解决:在外网主机上发布web服务,做请求重定向到内网

优势:可以配置ip映射,也可以配合端口映射

缺点:内网的接口url,端口不能修改

5. 个人解决:由微信提供内网穿透,原理和方式 (4) 一致

所以,端口号8160 请求url不能变

1、后端接口开发

1. 接口分析

*参数:code 、state

*返回值:重定向地址

2. userService 添加依赖

    <dependencies>
        <!--httpclient-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
    </dependencies>

3. 添加工具类

4. controller方法实现

    //扫码后,微信回调方法,授权临时票据code
    @GetMapping("callback")
    public String callback(String code, String state, HttpSession session) {
        //1.获取微信回调验证码code
        System.out.println("code = " + code);
        System.out.println("state = " + state);
        //2.访问微信接口,用code验证码,换取access_token、open_id
        //2.1拼写url
        //方式一
        String url = "ttps://api.weixin.qq.com/sns/oauth2/access_token?" +
                "appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";

        //方式二
        StringBuffer baseAccessTokenUrl = new StringBuffer()
                .append("https://api.weixin.qq.com/sns/oauth2/access_token")
                .append("?appid=%s")
                .append("&secret=%s")
                .append("&code=%s")
                .append("&grant_type=authorization_code");
        String accessTokenUrl = String.format(baseAccessTokenUrl.toString(),
                ConstantPropertiesUtil.WX_OPEN_APP_ID,
                ConstantPropertiesUtil.WX_OPEN_APP_SECRET,
                code);

        //2.2借助工具发送请求获得响应
        try {
            String accessTokenString = HttpClientUtils.get(accessTokenUrl);
            System.out.println("accessTokenString = " + accessTokenString);

            //2.3从json串中获取access_token、open_id
            JSONObject accessTokenJson = JSONObject.parseObject(accessTokenString);
            String accessToken = accessTokenJson.getString("access_token");
            String openid = accessTokenJson.getString("openid");

            //3.根据open_id查询用户信息
            QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
            wrapper.eq("openid",openid);
            UserInfo userInfo = userInfoService.getOne(wrapper); //openid是唯一的

            //4.用户信息为空,走注册流程
            if(userInfo==null){
                //5.根据access_token、open_id获取用户信息,完成注册流程
                //5.1拼写url
                String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
                        "?access_token=%s" +
                        "&openid=%s";
                String userInfoUrl = String.format(baseUserInfoUrl, accessToken, openid);
                //5.2借助工具发送请求,获取响应
                String resultInfo = HttpClientUtils.get(userInfoUrl);
                System.out.println("resultInfo:"+resultInfo);
                //5.3转化返回json串,获取返回值
                JSONObject resultInfoJson = JSONObject.parseObject(resultInfo);
                //用户昵称
                String nickname = resultInfoJson.getString("nickname");
                //用户头像
                String headimgurl = resultInfoJson.getString("headimgurl");
                //5.4userInfo中存入信息,完成注册操作
                userInfo = new UserInfo();
                userInfo.setOpenid(openid);
                userInfo.setNickName(nickname);
                userInfo.setStatus(1);
                userInfoService.save(userInfo);
            }

            //6.验证用户是否被锁定
            if(userInfo.getStatus()==0){
                throw new YyghException(20001,"用户已被锁定");
            }

            //7.验证用户是否绑定手机号
            //如果已绑定手机号openid=""
            //如果没有绑定手机号openid=微信唯一编号
            Map<String,Object> map = new HashMap<>();
            if(userInfo.getPhone()==null){
                map.put("openid",openid);
            }else {
                map.put("openid","");
            }

            //8.补全用户信息,进行登录
            String name = userInfo.getName();
            if(StringUtils.isEmpty(name)) {
                name = userInfo.getNickName();
            }
            if(StringUtils.isEmpty(name)) {
                name = userInfo.getPhone();
            }
            String token = JwtHelper.createToken(userInfo.getId(), name);
            map.put("token", token);
            map.put("name", name);

            //9.重定向回相关页面
            return "redirect:http://localhost:3000/weixin/callback?token="
                    +map.get("token")+ "&openid="+map.get("openid")
                    +"&name="+URLEncoder.encode((String) map.get("name"),"utf-8");
        } catch (Exception e) {
            e.printStackTrace();
            throw new YyghException(20001,"微信扫码登录失败");
        }
    }

5. 改造手机号验证码登录接口,实现绑定手机号

将用户信息封装进token,存入coocki

    //会员登录
    @Override
    public Map<String, Object> login(LoginVo loginVo) {
        //1.数据校验
        String phone = loginVo.getPhone();
        String code = loginVo.getCode();
        String openid = loginVo.getOpenid();

        if(StringUtils.isEmpty(phone)||StringUtils.isEmpty(code)){
            throw new YyghException(20001,"注册信息有误");
        }
        //2.校验验证码
        //2.1 根据手机号从redis取出验证码
        String rediscode = redisTemplate.opsForValue().get(phone);
        //2.2 对比验证码,注意字符串空指针
        if(!code.equals(rediscode)){
            throw new YyghException(20001,"验证码有误");
        }

        //2.3判断openid,为空走手机号验证码登录,不为空绑定手机号
        Map<String, Object> map = new HashMap<>();
        UserInfo userInfo;
        if(StringUtils.isEmpty(openid)){
            //3.根据手机号查询用户信息
            QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
            wrapper.eq("phone",phone);
            userInfo = baseMapper.selectOne(wrapper);

            //4.用户信息为空,走注册功能
            if(userInfo==null){
                userInfo = new UserInfo();
                userInfo.setPhone(phone);
                userInfo.setStatus(1); //0锁定 1正常
                baseMapper.insert(userInfo);
            }

        }else {
            //8.根据openid查询用户信息
            QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
            wrapper.eq("openid",openid);
            userInfo = baseMapper.selectOne(wrapper);
            if(userInfo==null){
                throw new YyghException(20001,"用户注册信息有误");
            }
            //9.更新用户手机号信息
            userInfo.setPhone(phone);
            baseMapper.updateById(userInfo);

        }

        //5.判断用户是否被锁定
        if(userInfo.getStatus()==0){
            throw new YyghException(20001,"用户已被锁定");
        }
        //6.补全用户信息
        //返回页面显示名称

        String name = userInfo.getName();
        if(StringUtils.isEmpty(name)) {
            name = userInfo.getNickName();
        }
        if(StringUtils.isEmpty(name)) {
            name = userInfo.getPhone();
        }
        //7.用户登录
        String token = JwtHelper.createToken(userInfo.getId(), userInfo.getName());

        map.put("token", token);
        map.put("name", name);

        return map;
    }

相关api:

 

微信名如果是emoji表情,插入数据库会失败。解决方案:java emoji显示乱码_Java处理emoji的方式_雅酷布的博客-CSDN博客

方法2. 添加依赖,进行解析

        <!--emoji小图标解析-->
        <dependency>
            <groupId>com.vdurmont</groupId>
            <artifactId>emoji-java</artifactId>
            <version>4.0.0</version>
        </dependency>

2、整合微信扫描登录前端

1. 需求确认

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

2. 创建空白布局

3. 创建空白页面

<template>
    <!-- header -->
    <div>
    </div>
    <!-- footer -->
</template>
<script>
    export default {
        layout: "empty",
        data() {
            return {
            }
        },
        //页面加载后执行
        mounted() {
            //1.获取参数 query通过问号获取
            let token = this.$route.query.token
            let openid = this.$route.query.openid
            let name = this.$route.query.name
            //2.调用父vue方法
            window.parent['loginCallback'](name, token, openid)
        },
    }
</script>

4. 在myheader.vue添加方法 微信扫码后方法实现

        methods: {
            //微信登录回调方法
            loginCallback(name, token, openid) {
                //1.第一次登录,绑定手机号
                if (openid != "") {
                    this.userInfo.openid = openid
                    //this.dialogAtrr.showLoginType = 'phone'
                    this.showLogin() //showLogin 初始化了弹出层对象
                }
                //2.不是第一次,设置登录状态
                this.setCookies(name, token)

            },
        ....

测试:

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值