基于Uniapp+SpringBoot实现微信小程序授权登录

简介: 基于Uniapp+SpringBoot实现微信小程序授权登录

image.png

开发需求:

我们团队在开发微信小程序过程中,需要绑定微信用户的信息到数据库里,那么就需要获得用户的唯一标识openid,而微信为了安全,是禁止小程序直接访问该接口,因此我们不能直接拿到用户的openid,从而需要通过调用微信接口实现授权登录。

这篇文章是基于uniapp+springboot的微信小程序授权登录交互,对uniapp不熟悉的可以去这篇两万字的博客(【前端之旅】uni-app学习笔记)了解一下。


官方登录流程图(逻辑流程):

1665330036815.jpg



主要步骤:

  1. 前端获取到code(wx.login),将code通过uni.request()接口传到后台服务器。
  2. 后台服务器通过code、AppID和AppSecret等参数访问官方接口(传给微信服务器),获取到OpenId及本次会话秘钥session_key等。
  3. 后台服务器将OpenId进行相应的业务处理并返回给前端。


注意事项

会话密钥 session_key 是对用户数据进行 加密签名 的密钥。为了应用自身的数据安全,开发者服务器不应该把会话密钥下发到小程序,也不应该对外提供这个密钥。

临时登录凭证 code 只能使用一次。


一、uni.login请求临时code


在微信小程序中,使用微信开放接口:wx.login(Object object),调用接口获取登录凭证(code)


在uniapp中,我们通过uni.login(OBJECT),调用接口获取登录凭证(code)。


OBJECT 参数说明

image.png


success 返回参数说明

image.png


示例


uni.login({
  provider: 'weixin',
  success: function (res) {
    console.log(res.Result);
  }
});

二、uni.request向后台交换数据

我们通过 uni.request(OBJECT):向后台发起请求,后台通过前台发送得到的凭证(code),需要后台发送请求到微信接口,然后微信返回一个json格式的字符串到后台,后台处理之后,进而换取用户登录态信息,包括用户在当前小程序的唯一标识(openid)、微信开放平台帐号下的唯一标识(unionid)及本次登录的会话密钥(session_key)等, 再返回到前台。


uni.request(OBJECT):发起网络请求。


在各个小程序平台运行时,网络相关的 API 在使用前需要配置域名白名单。


OBJECT 参数说明

image.pngimage.png



必须大写,有效值在不同平台差异说明不同。

image.png


success 返回参数说明

image.png


data 数据说明


最终发送给服务器的数据是 String 类型,如果传入的 data 不是 String 类型,会被转换成 String。转换规则如下:


对于 GET 方法,会将数据转换为 query string。例如 { name: 'name', age: 18 } 转换后的结果是 name=name&age=18。

对于 POST 方法且 header['content-type'] application/json 的数据,会进行 JSON 序列化。

对于 POST 方法且 header['content-type']application/x-www-form-urlencoded 的数据,会将数据转换为 query string

示例


uni.request({
    url: 'https://www.example.com/request', //仅为示例,并非真实接口地址。
    data: {
        text: 'uni.request'
    },
    header: {
        'custom-header': 'hello' //自定义请求头信息
    },
    success: (res) => {
        console.log(res.data);
        this.text = 'request success';
    }
});

返回值


如果希望返回一个 requestTask 对象,需要至少传入 success / fail / complete 参数中的一个。例如:


var requestTask = uni.request({
    url: 'https://www.example.com/request', //仅为示例,并非真实接口地址。
    complete: ()=> {}
});
requestTask.abort();

如果没有传入 success / fail / complete 参数,则会返回封装后的 Promise 对象:Promise 封装


通过 requestTask,可中断请求任务。


requestTask 对象的方法列表

image.png


示例


const requestTask = uni.request({
    url: 'https://www.example.com/request', //仅为示例,并非真实接口地址。
    data: {
        name: 'name',
        age: 18
    },
    success: function(res) {
        console.log(res.data);
    }
});
// 中断请求任务
requestTask.abort();

Tips


请求的 header 中 content-type 默认为 application/json。

避免在 header 中使用中文,或者使用 encodeURIComponent 进行编码,否则在百度小程序报错。

网络请求的 超时时间 可以统一在 manifest.json 中配置 networkTimeout。

H5 端本地调试需注意跨域问题,参考:调试跨域问题解决方案

H5端 cookie 受跨域限制(和平时开发网站时一样),旧版的 uni.request 未支持 withCredentials 配置,可以直接使用 xhr 对象或者其他类库。

uni-app 插件市场有flyio、axios等三方封装的拦截器可用

localhost、127.0.0.1等服务器地址,只能在电脑端运行,手机端连接时不能访问。请使用标准IP并保证手机能连接电脑网络

单次网络请求数据量建议控制在50K以下(仅指json数据,不含图片),过多数据应分页获取,以提升应用体验。

附:了解各种平台的ID,它们都有相同的功能。


OpenID:在微信应用(公众号、小程序等)默认使用 OpenID,在开发中请求的接口返回的一般都是OpenID。在小程序或微信网页里不用做授权,静默情况下也能拿到 OpenID。可以说 OpenID 是微信生态里最重要的一个 ID。可以理解 OpenID 是通过 AppID 和微信用户 ID 加密得到的,其与微信应用(每个应用会有 AppID)相关,每个微信应用都会生成一个唯一的用户的识别。

AppID 和 AppSecret:公众号和小程序都会有一个 AppID 用来标识当前的微信应用,而如果需要开发的话,接口的请求都需要用到 AppSecret。

微信用户 ID:微信用户 ID 是有加密的,是无法拿到的。一般我们使用微信机器人开发的话,常用的是微信号或者微信昵称作为 ID。

UnionID:在微信开放平台里面,做了账号绑定后,就会生成一个统一的 UnionID,绑定后的微信应用(小程序、公众号等)都可以使用一个 ID。获取 UnionID 需要经过用户授权。

UUID:主要是针对于前端的设备,比如小程序或者网页的,因为获取 OpenID 需要一定的开发,所以如果在获取不到的情况下,我们一般会给当前浏览器或者小程序生成一个随机的 ID。

UserID:用户的真实 ID,一般是存在数据库的 ID。

三、源代码

1.调用微信API wx.login()得到code。

2.把得到的code传给后端,后端获取code请求微信小程序官方接口,返回给前端openid。

3.知道openid后进行用户相关的操作,可以用于分别用户的登录状态。


前台:在GetUserInfo中添加接口


GetUserInfo() {
        var _this = this;
        // 增加约束,不选协议无法进行授权
        if (this.value === false) {
          uni.showToast({
            title: '请阅读勾选协议',
            icon: 'error',
            duration: 2000
          });
        } else {
          uni.getUserProfile({
            desc: '登录',
            lang: 'zh_CN',
            success: (res) => {
              console.log('获取的信息', res.userInfo);
              _this.nickName = res.userInfo.nickName;
              _this.setNn(res.userInfo.nickName);
              uni.getLocation({
                type: 'gci02',
                success: res => {
                  uni.reLaunch({
                    url: 'Login2'
                  });
                }
              });
            },
            fail: (res) => {
              console.log('用户拒绝了授权');
              uni.showToast({
                title: '授权失败',
                icon: 'error',
                duration: 2000
              });
            }
          });
        }
      }
login() {
          let _this = this;
          uni.showLoading({
            title: '登录中...'
          });
          // 获取登录用户 code
          uni.login({
            provider: 'weixin',
            success: function(res) {
              if (res.code) {
                let code = res.code;
                console.log('用户code:', res.code);
                uni.request({
                  url: "https://xxxxxxx:8084/wxLogin/getOpenid",
                  method: 'POST',
                  header: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                  },
                  data: {
                    code: res.code, //wx.login 登录成功后的code 
                    role: _this.role,
                  },
                  success: function(cts) {
                    var openid = cts.data.openid; //openid 用户唯一标识
                    var userInfo = {
                      openid: openid,
                      role: _this.role
                    };
                    console.log(_this.role);
                    _this.saveUserInfo(userInfo);
                    uni.hideLoading();
                    uni.switchTab({
                                            // 登录成功后的跳转
                      url: '../myCenter/myCenter'
                    });
                  }
                });
              } else {
                uni.showToast({
                  title: '登录失败!',
                  duration: 2000
                });
                console.log('登录失败!' + res.errMsg)
              }
            },
          });
      }
    }

后台:SpringBoot后台数据处理


  1. 首先获取的自己小程序的appid、secret,封装为一个接口
  2. 接口当中,拼接sql,向微信发送http请求
  3. 将返回的结果进行封装,封装成map集合返回给前端


    @PostMapping("/getOpenid")
    @ResponseBody
    public Map<String, Object> getOpenId(@RequestParam("code") String code,
                                         @RequestParam(value = "role") Integer role) throws IOException {
        // 返回code
        System.out.println("========== 进入wxLogin/getOpenid方法  ==========");
        System.err.println("微信授权登录");
        System.err.println("code值:  " + code);
        Map<String, Object> resultMap = new HashMap<>();
        String appid = ConstUtil.getAppId();
        String secret = ConstUtil.getSECRET();
        // 拼接sql
        String loginUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appid +
                "&secret=" + secret + "&js_code=" + code + "&grant_type=authorization_code";
        CloseableHttpClient client = null;
        CloseableHttpResponse response = null;
        // 创建httpGet请求
        HttpGet httpGet = new HttpGet(loginUrl);
        // 发送请求
        client = HttpClients.createDefault();
        // 执行请求
        response = client.execute(httpGet);
        // 得到返回数据
        HttpEntity entity = response.getEntity();
        String result = EntityUtils.toString(entity);
        System.out.println("微信返回的结果" + result);
        resultMap.put("data", result);
        // 对返回的结果进行解析
        JSONObject json_test = JSONObject.parseObject(result);
        String openid = json_test.getString("openid");
        String sessionKey = json_test.getString("session_key");
        System.err.println("openid值: " + openid);
        System.err.println("sessionKey值" + sessionKey);
        Users users = usersService.getUserByOpenid(openid);
        System.err.println("用户信息:" + users);
        if (StringUtils.isEmpty(openid)) {
            resultMap.put("state", ResponseCode.SUCCESS.getCode());
            resultMap.put("message", "未获取到openid");
            return resultMap;
        } else {
            // 判断是否为首次登陆
            if (users == null) {
                resultMap.put("state", ResponseCode.SUCCESS.getCode());
                resultMap.put("openid", openid);
                resultMap.put("sessionKey", sessionKey);
                resultMap.put("message", "未查询到用户信息");
            } else {
                // 查询有无结果
                UserRole uRes = userRoleService.getUserRole(openid, role);
                // 封装对象
                UserRole userRole = new UserRole(openid, role);
                // 如果不是第一次登录,第二次登陆进行判断,如果没有这个身份,进行添加,如果有这个身份,不做处理
                if (uRes == null) {
                    userRoleService.insert(userRole);
                }
                resultMap.put("state", ResponseCode.SUCCESS.getCode());
                // 前台拿的数据是map的key
                resultMap.put("openid", openid);
                resultMap.put("sessionKey", sessionKey);
                resultMap.put("user", users);
                resultMap.put("message", "该用户已经存在");
            }
            response.close();
            client.close();
        }
        return resultMap;
    }

四、实现效果

1665329971985.jpg


1665329971985.jpg1665329971985.jpg1665329971985.jpg

相关文章
|
7月前
|
小程序 JavaScript 搜索推荐
基于springboot的考研互助小程序
本项目基于SpringBoot开发考研互助小程序,整合优质资源,提供真题、视频、学习计划等功能,构建交流社区,助力考生高效备考,促进教育公平与信息化发展。
|
小程序 JavaScript Java
基于SpringBoot的智慧停车场微信小程序源码分享
智慧停车场微信小程序主要包含管理端和小程序端。管理端包括停车场管理,公告信息管理,用户信息管理,预定信息管理,用户反馈管理等功能。小程序端包括登录注册,预约停车位,停车导航,停车缴费,用户信息,车辆信息,钱包充值,意见反馈等功能。
740 5
基于SpringBoot的智慧停车场微信小程序源码分享
|
存储 小程序 前端开发
微信小程序与Java后端实现微信授权登录功能
微信小程序极大地简化了登录注册流程。对于用户而言,仅仅需要点击授权按钮,便能够完成登录操作,无需经历繁琐的注册步骤以及输入账号密码等一系列复杂操作,这种便捷的登录方式极大地提升了用户的使用体验
3731 12
|
小程序 前端开发 算法
|
移动开发 前端开发 Android开发
开发指南059-App实现微信扫描登录
App是用uniapp开发的,打包为apk,上传到安卓平板中使用
|
小程序 算法 前端开发
微信小程序---授权登录
微信小程序---授权登录
386 0
|
移动开发 小程序
thinkphp+uniapp开发的多端商城系统源码/H5/小程序/APP支持DIY模板直播分销
thinkphp+uniapp开发的多端商城系统源码/H5/小程序/APP支持DIY模板直播分销
697 0
|
小程序 前端开发 JavaScript
在线课堂+工具组件小程序uniapp移动端源码
在线课堂+工具组件小程序uniapp移动端源码
378 0
在线课堂+工具组件小程序uniapp移动端源码
|
小程序 前端开发 Java
SpringBoot+uniapp+uview打造H5+小程序+APP入门学习的聊天小项目
JavaDog Chat v1.0.0 是一款基于 SpringBoot、MybatisPlus 和 uniapp 的简易聊天软件,兼容 H5、小程序和 APP,提供丰富的注释和简洁代码,适合初学者。主要功能包括登录注册、消息发送、好友管理及群组交流。
426 1
SpringBoot+uniapp+uview打造H5+小程序+APP入门学习的聊天小项目
|
移动开发 小程序 数据可视化
基于npm CLI脚手架的uniapp项目创建、运行与打包全攻略(微信小程序、H5、APP全覆盖)
基于npm CLI脚手架的uniapp项目创建、运行与打包全攻略(微信小程序、H5、APP全覆盖)
4033 3