• 【SpringBoot学习】46、SpringBoot 集成 Uniapp 实现微信公众号授权登录


    一、公众号环境搭建

    本篇文章使用的虽然是微信公众号测试号,正式公众号同理,也需要下面几个步骤,只是说每个操作多了一次管理员扫码校验而已


    然后下一步,关注自己的测试号,否则无法授权登录


    继续修改网页授权配置


    修改里面的域名信息,填写的是域名,不需要 http,直接写域名

    二、Spring Boot 集成微信公众号

    1、application.yml 微信配置

    # 微信配置
    business:
      baseUrl:  https://tellsea.4kb.cn
      appUserUrl: http://192.168.3.6:8081/gyplay-app-user/#
      weixin:
        appId: 上面获取的公众号appId
        secret: 上面获取的公众号secret
        # 微信授权登录回调
        callBackUrl: ${business.baseUrl}/gyplay-service/au/weiXin/callBack
        # 重定向到登录页
        redirectLoginUrl: ${business.appUserUrl}/pages/login/login
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    为了方便获取配置文件的内容,这里读取配置信息使用的是 @ConfigurationProperties 注解的方式

    package com.ruoyi.business.appuser.config;
    
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    /**
     * @author Tellsea
     * @date 2022/8/22
     */
    @Data
    @Component
    @ConfigurationProperties(prefix = "business.weixin")
    public class WeiXinProperties {
    
        private String appId;
    
        private String secret;
    
        /**
         * 微信授权登录回调
         */
        private String callBackUrl;
        /**
         * 重定向到登录页
         */
        private String redirectLoginUrl;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    2、控制层接口

    然后增加一个微信相关的控制器,里面有两个方法

    • 微信授权登录,返回前端调用的连接
    • 用户授权登录成功之后的回调地址请求接口,重定向到前端页面

    还有最后一个流程就是前端页面根据登录授权信息,比如 token 换取用户信息等,这就是整个授权登录流程

    控制层代码如下

    package com.ruoyi.business.appuser.controller;
    
    import cn.hutool.http.HttpUtil;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.ruoyi.business.appuser.config.WeiXinProperties;
    import com.ruoyi.common.core.domain.AjaxResult;
    import com.ruoyi.common.core.domain.entity.SysRole;
    import com.ruoyi.common.core.domain.entity.SysUser;
    import com.ruoyi.common.core.domain.model.LoginBody;
    import com.ruoyi.common.core.domain.model.LoginUser;
    import com.ruoyi.common.exception.CustomException;
    import com.ruoyi.common.utils.ServletUtils;
    import com.ruoyi.framework.web.service.SysLoginService;
    import com.ruoyi.framework.web.service.SysPermissionService;
    import com.ruoyi.framework.web.service.TokenService;
    import com.ruoyi.system.domain.SysUserRole;
    import com.ruoyi.system.mapper.SysUserRoleMapper;
    import com.ruoyi.system.service.ISysUserService;
    import com.zhhy.consts.RoleConstants;
    import com.zhhy.tool.utils.IntegerUtils;
    import io.swagger.annotations.ApiOperation;
    import lombok.RequiredArgsConstructor;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.util.CollectionUtils;
    import org.springframework.web.bind.annotation.*;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.net.URLEncoder;
    import java.util.List;
    import java.util.Set;
    import java.util.stream.Collectors;
    
    /**
     * @author Tellsea
     * @date 2021/07/01
     */
    @RestController
    @RequestMapping("/au/weiXin")
    @RequiredArgsConstructor(onConstructor_ = @Autowired)
    public class AuWeiXinController {
    
        private final ISysUserService userService;
        private final SysPermissionService permissionService;
        private final TokenService tokenService;
        private final SysUserRoleMapper sysUserRoleMapper;
        private final SysLoginService loginService;
        private final WeiXinProperties weiXinProperties;
    
        @ApiOperation("登录检查")
        @GetMapping("beforeLogin")
        public AjaxResult beforeLogin() {
            return AjaxResult.success("检查成功");
        }
       
        @ApiOperation("微信授权登录")
        @GetMapping("login")
        public AjaxResult login() {
            try {
                String redirect_uri = URLEncoder.encode(weiXinProperties.getCallBackUrl(), "UTF-8");
                String url = "https://open.weixin.qq.com/connect/oauth2/authorize?"
                        + "appid=" + weiXinProperties.getAppId()
                        + "&redirect_uri=" + redirect_uri
                        + "&response_type=code"
                        + "&scope=snsapi_userinfo"
                        + "&state="
                        + "&connect_redirect=1#wechat_redirect";
                return AjaxResult.success("请求成功", url);
            } catch (Exception e) {
                e.printStackTrace();
                return AjaxResult.error("微信授权登录失败");
            }
        }
    
        @ApiOperation("回调地址")
        @GetMapping("callBack")
        public void callBack(HttpServletRequest request, HttpServletResponse response) {
            String code = request.getParameter("code");
            String state = request.getParameter("state");
            String url = "https://api.weixin.qq.com/sns/oauth2/access_token?"
                    + "appid=" + weiXinProperties.getAppId()
                    + "&secret=" + weiXinProperties.getSecret()
                    + "&code=" + code
                    + "&grant_type=authorization_code";
            JSONObject jsonObject = JSON.parseObject(HttpUtil.get(url));
            Integer errcode = jsonObject.getInteger("errcode");
            if (errcode != null) {
                String errmsg = jsonObject.getString("errmsg");
                throw new CustomException("【微信回调错误】错误码:" + errcode + ",错误信息:" + errmsg);
            }
            String openid = jsonObject.getString("openid");
            String access_token = jsonObject.getString("access_token");
            String refresh_token = jsonObject.getString("refresh_token");
            Integer expires_in = jsonObject.getInteger("expires_in");
    
            String infoUrl = "https://api.weixin.qq.com/sns/userinfo?"
                    + "access_token=" + access_token
                    + "&openid=" + openid
                    + "&lang=zh_CN";
            // 获取微信用户信息
            JSONObject userInfo = JSON.parseObject(HttpUtil.get(infoUrl));
            String country = userInfo.getString("country");
            String province = userInfo.getString("province");
            String city = userInfo.getString("city");
            Integer sexValue = userInfo.getInteger("sex");
            String nickName = userInfo.getString("nickname");
            String avatar = userInfo.getString("headimgurl");
            String language = userInfo.getString("language");
            String sex;
            if (IntegerUtils.eq(sexValue, 1)) {
                sex = "0";
            } else if (IntegerUtils.eq(sexValue, 2)) {
                sex = "1";
            } else {
                sex = "2";
            }
            List<SysUser> list = userService.list(new LambdaQueryWrapper<SysUser>().eq(SysUser::getOpenId, openid));
            String token;
            SysUser user = null;
            if (CollectionUtils.isEmpty(list)) {
                // 注册用户
                SysUser entity = new SysUser();
                entity.setOpenId(openid);
                entity.setUserName(openid);
                entity.setNickName(nickName);
                entity.setAvatar(avatar);
                entity.setDeptId(0L);
                entity.setSex(sex);
                entity.setRoleIds(new Long[]{RoleConstants.USER.getRoleId()});
                userService.insertUser(entity);
                SysUser sysUser = userService.selectUserByUserName(entity.getUserName());
                LoginUser loginUser = new LoginUser(sysUser, permissionService.getMenuPermission(sysUser));
                token = tokenService.createToken(loginUser);
            } else if (list.size() == 1) {
                // 更新用户
                user = list.get(0);
                user.setNickName(nickName);
                userService.updateById(user);
                SysUser sysUser = userService.selectUserByUserName(user.getUserName());
                List<SysRole> roles = sysUser.getRoles();
                List<String> roleKeys = roles.stream().map(SysRole::getRoleKey).collect(Collectors.toList());
                if (!roleKeys.contains(RoleConstants.USER.getRoleKey())) {
                    // 给角色用户
                    sysUserRoleMapper.insert(new SysUserRole().setUserId(user.getUserId()).setRoleId(RoleConstants.USER.getRoleId()));
                    sysUser = userService.selectUserByUserName(user.getUserName());
                }
                LoginUser loginUser = new LoginUser(sysUser, permissionService.getMenuPermission(sysUser));
                token = tokenService.createToken(loginUser);
            } else {
                throw new CustomException("数据异常:OpenId存在多个");
            }
            try {
                // 重定向到登录,自动跳转首页
                String redirectUrl = weiXinProperties.getRedirectLoginUrl() + "?token=" + token;
                response.sendRedirect(redirectUrl);
            } catch (IOException e) {
                e.printStackTrace();
                throw new CustomException("【微信回调错误】重定向地址错误");
            }
        }
    
        @ApiOperation("获取用户信息")
        @GetMapping("getInfo")
        public AjaxResult getInfo() {
            LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
            SysUser user = loginUser.getUser();
            Set<String> permission = permissionService.getRolePermission(user);
            AjaxResult ajax = AjaxResult.success();
            ajax.put("user", user);
            ajax.put("roles", permission);
            ajax.put("permissions", permissionService.getMenuPermission(user));
            return ajax;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178

    三、Uniapp 实现授权登录

    前端点击登录,获取授权链接,然后访问连接授权,拿到 token 信息,然后使用 token 换取用户信息,放入缓存中,并修改请求工具类的请求拦截器,没有拦截器的,直接判断缓存是否有 token,有就添加到请求头中,因为后台通常都是从 header 里面获取 token 进行校验的

    <template>
      <view class="container">
        <image src="/static/images/wx-login.jpeg">image>
        <view>申请获取以下权限view>
        <view>获得你的公开信息(昵称,头像、地区等)view>
        <view>获得你微信绑定的手机号view>
    
        <u-button type="primary" size="default" class="login-btn" @tap="login">
          微信授权登录
        u-button>
      view>
    template>
    
    <script>
    let that;
    
    export default {
      data() {
        return {
        }
      },
      onLoad(option) {
        that = this;
        let token = option.token;
        if (that.$validator.isNotEmpty(token)) {
          // 登录成功回调
          uni.setStorageSync(that.$config.cachePrefix + 'token', token);
          uni.$u.http.get('/au/weiXin/getInfo').then(res => {
            uni.setStorageSync(that.$config.cachePrefix + 'user', res.user);
            uni.setStorageSync(that.$config.cachePrefix + 'role', res.role);
            uni.setStorageSync(that.$config.cachePrefix + 'permissions', res.permissions);
          });
        }
      },
      methods: {
        login() {
          uni.$u.http.get('/au/weiXin/login').then(res => {
            if (res.code == 200) {
              window.location.href = res.data;
            } else {
              that.$msg(res.msg);
            }
          });
        }
      },
    }
    script>
    
    <style lang="scss" scoped>
    page {
      background-color: white;
    }
    
    .container {
      font-size: 13px;
      color: $u-main-color;
      line-height: 25px;
      padding: 100px 20px;
      height: 100vh;
    
      image {
        width: 60px;
        height: 50px;
        margin-left: calc((100% - 60px) / 2);
      }
    
      .login-btn {
        margin-top: 50px;
      }
    }
    style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    我这里使用的是 uview2.x,是有一个 util/request/requestInterceptors.js 请求拦截器的,只需要增加一个 token 校验就行了

    相关代码这里粘贴如下

    import $config from '@/common/config.js';
    import {validator} from '@/common/validator.js';
    
    /**
     * 请求拦截
     * @param {Object} http
     */
    module.exports = (vm) => {
        uni.$u.http.interceptors.request.use((config) => { // 可使用async await 做异步操作
            // 初始化请求拦截器时,会执行此方法,此时data为undefined,赋予默认{}
            config.data = config.data || {};
            let token = uni.getStorageSync($config.cachePrefix + 'token');
            // header: {
            //     'content-type': 'application/json',
            //         'Authorization': validator.isEmpty(token) ? '' : 'Bearer ' + token
            // },
            config.header.Authorization = validator.isEmpty(token) ? '' : 'Bearer ' + token;
            // 可以在此通过vm引用vuex中的变量,具体值在vm.$store.state中
            // console.log(vm.$store.state);
            return config
        }, (config) => // 可使用async await 做异步操作
            Promise.reject(config))
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24


    检查一下数据库,OpenId、头像、昵称、性别这些全部都是有的

    到此,SpringBoot 集成 Uniapp 实现微信公众号授权登录完成了

  • 相关阅读:
    力扣——链表简单题目
    DOM中text节点详解
    富满FM5001H电源IC移动风扇IC
    数据库与身份认证
    mac安装chromedriver驱动详细步骤
    图片懒加载
    上门预约洗鞋小程序开发;
    【LLM】大语言模型高效微调方案Lora||直击底层逻辑
    Solidity智能合约开发 — 4.3-合约的事件和继承
    AI大全-通往AGI之路
  • 原文地址:https://blog.csdn.net/qq_38762237/article/details/126477861