谷粒学院——第十四章、微信扫码登录

news2024/11/16 11:55:17

准备工作

注册开发者资质

官网:https://open.weixin.qq.com/
image.png

尚硅谷分享

wx:
  open:
    # 微信开放平台 appid

    appid: wxed9954c01bb89b47

    # 微信开放平台 appsecret
    appsecret: a7482517235173ddb4083788de60b90e

    # 微信开放平台 重定向url(guli.shop需要在微信开放平台配置)
    redirecturl: http://guli.shop/api/ucenter/wx/callback

微信登陆流程

官网文档:https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html

微信登陆实现

配置文件

在 service_ucenter 模块的配置文件 application.properties 中,填写微信ID,密钥和域名地址。修改端口(同时修改nginx中的端口)

# 服务端口(在这里需要修改端口)
server.port=8160
# 用于微信登陆
# 微信开放平台 appid
wx.open.app_id=wxed9954c01bb89b47
# 微信开放平台 appsecret
wx.open.app_secret=a7482517235173ddb4083788de60b90e
# 微信开放平台 重定向url
wx.open.redirect_url=http://localhost:8160/api/ucenter/wx/callback

创建 utils 包,然后创建类去读取配置文件中的值:

@Component
public class ConstantWxUtils 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() {
        WX_OPEN_APP_ID = appId;
        WX_OPEN_APP_SECRET = appSecret;
        WX_OPEN_REDIRECT_URL = redirectUrl;
    }
}

生成扫描的二维码

直接请求微信提供的固定地址,然后向地址后面拼接参数。

在 service_ucenter 模块的 controller 层,创建类:

@CrossOrigin
@Controller
@RequestMapping("/api/ucenter/wx")
public class WxApiController {
    
    @ApiOperation("生成微信扫描的二维码")
    @GetMapping("/login")
    public String getWxCode() {
        // 微信开放平台授权baseUrl,%s是占位符
        String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
                "?appid=%s" +
                "&redirect_uri=%s" +
                "&response_type=code" +
                "&scope=snsapi_login" +
                "&state=%s" +
                "#wechat_redirect";
        // 对 redirect_url 进行 URLEncoder 编码
        String redirectUrl = ConstantWxUtils.WX_OPEN_REDIRECT_URL;
        try {
            redirectUrl = URLEncoder.encode(redirectUrl, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        // 设置值
        String url = String.format(
                baseUrl,
                ConstantWxUtils.WX_OPEN_APP_ID,
                redirectUrl,
                "atguigu"
        );
        // 重定向
        return "redirect:" + url;
    }
}

image.png
启动项目,访问:http://localhost:8006/api/ucenter/wx/login,效果如下:
image.png

扫码之后获取信息

步骤图

image.png

image.png

工具类

需要先引入依赖,不过在一开始的时候,在 service 模块的 pom.xml 中已经引入过了,所以不必重新引入。
在 utils 包中复制提供好的工具类:
HttpClientUtils.java

controller 层

WxApiController 类添加方法:

@CrossOrigin
@Controller
@RequestMapping("/api/ucenter/wx")
public class WxApiController {

    @Autowired
    private MemberService memberService;

    @ApiOperation("生成微信扫描的二维码")
    @GetMapping("/login")
    public String getWxCode() {
        // 微信开放平台授权baseUrl,%s是占位符
        String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
                "?appid=%s" +
                "&redirect_uri=%s" +
                "&response_type=code" +
                "&scope=snsapi_login" +
                "&state=%s" +
                "#wechat_redirect";
        // 对 redirect_url 进行 URLEncoder 编码
        String redirectUrl = ConstantWxUtils.WX_OPEN_REDIRECT_URL;
        try {
            redirectUrl = URLEncoder.encode(redirectUrl, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        // 设置值
        String url = String.format(
                baseUrl,
                ConstantWxUtils.WX_OPEN_APP_ID,
                redirectUrl,
                "atguigu"
        );
        // 重定向
        return "redirect:" + url;
    }

    // 获取扫描人信息,添加数据
    @ApiOperation("获取扫描人信息,添加数据")
    @GetMapping("callback")
    public String callback(String code, String state) {
        // 获取code值(临时票据),类似于验证码
        // 拿着 code 请求微信的固定地址,得到两个值 access_token 和 openid
        String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
                "?appid=%s" +
                "&secret=%s" +
                "&code=%s" +
                "&grant_type=authorization_code";
        // 拼接三个参数:id、密钥和 code 值
        String accessTokenUrl = String.format(
                baseAccessTokenUrl,
                ConstantWxUtils.WX_OPEN_APP_ID,
                ConstantWxUtils.WX_OPEN_APP_SECRET,
                code
        );
        // 请求这个拼接好的地址,得到返回的两个值
        // 使用 httpclient 发送请求,得到返回结果
        String accessTokenInfo;
        try {
            accessTokenInfo = HttpClientUtils.get(accessTokenUrl);
        } catch (Exception e) {
            e.printStackTrace();
            throw new GuliException(20001, "登陆失败");
        }
        // 从 accessTokenInfo 中 取出来 access_token 和 openid 并转换成 map,根据 key 获取对应值
        Gson gson = new Gson();
        Map mapAccessToken = gson.fromJson(accessTokenInfo, HashMap.class);
        String access_token = (String) mapAccessToken.get("access_token");
        String openid = (String) mapAccessToken.get("openid");
        // 扫码人的信息添加到数据库中
        // 根据 openid 判断数据库中是否存在相同的微信信息
        UcenterMember member = memberService.getOpenIdMember(openid);
        if (member == null) {
            // 拿着得到的 access_token 和 openid 再去请求微信提供的地址,获得扫码人的信息
            String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
                    "?access_token=%s" +
                    "&openid=%s";
            // 拼接两个参数
            String userInfoUrl = String.format(baseUserInfoUrl, access_token, openid);
            // 发送请求
            String userInfo;
            try {
                userInfo = HttpClientUtils.get(userInfoUrl);
            } catch (Exception e) {
                e.printStackTrace();
                throw new GuliException(20001, "登陆失败");
            }
            // 从 userInfo 中获取扫码人的信息
            Map userInfoMap = gson.fromJson(userInfo, HashMap.class);
            String nickname = (String) userInfoMap.get("nickname"); // 昵称
            String headimgurl = (String) userInfoMap.get("headimgurl"); // 头像
            member = new UcenterMember();
            member.setOpenid(openid);
            member.setNickname(nickname);
            member.setAvatar(headimgurl);
            memberService.save(member);
        }
        // 使用 JWT 根据 mapper 对象生成 token 字符串
        String jwtToken = JwtUtils.getJwtToken(member.getId(), member.getNickname());
        return "redirect:http://localhost:3000?token=" + jwtToken;
    }
}

service 层

MemberService 类添加:

    // 根据 openid 判断
    UcenterMember getOpenIdMember(String openid);

实现类添加:

    // 根据 openid 判断
    @Override
    public UcenterMember getOpenIdMember(String openid) {
        QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
        wrapper.eq("openid", openid);
        return baseMapper.selectOne(wrapper);
    }

前端页面显示登陆信息

default.vue

修改 layouts/default.vue

<script>
import '~/assets/css/reset.css'
import '~/assets/css/theme.css'
import '~/assets/css/global.css'
import '~/assets/css/web.css'
import cookie from 'js-cookie'
import loginApi from '@/api/login'

export default {
  data() {
    return {
      token: '',
      loginInfo: {
        id: '',
        age: '',
        avatar: '',
        mobile: '',
        nickname: '',
        sex: ''
      }
    }
  },
  created() {
    // 获取路径中的 token 值
    this.token = this.$route.query.token
    if (this.token) {
      // 如果能取到就用微信扫码登陆
      this.wxLogin()
    }
    this.showInfo()
  },
  methods: {
    // 微信登陆显示的方法
    wxLogin() {
      // 把 token 放到 cookie
      cookie.set('guli_token', this.token, { domain: 'localhost' })
      cookie.set('guli_ucenter', '', { domain: 'localhost' })
      // 调用接口,根据 token 值获取用户信息
      loginApi.getLoginUserInfo().then(response => {
        this.loginInfo = response.data.data.userInfo
        cookie.set('guli_ucenter', this.loginInfo, { domain: 'localhost' })
      })
    },
    // 退出的方法
    logout() {
      // 清空cookie值
      cookie.set('guli_token', '', { domain: 'localhost' })
      cookie.set('guli_ucenter', '', { domain: 'localhost' })
      // 回到首页面
      window.location.href = '/'
    },
    // 创建方法从cookie中获取信息
    showInfo() {
      // 从cookie中获取信息
      var userStr = cookie.get('guli_ucenter')
      // 转字符串转换成json对象(js对象)
      if (userStr) {
        this.loginInfo = JSON.parse(userStr)
      }
    }
  }
}
</script>

测试

访问:http://localhost:8160/api/ucenter/wx/login

微信扫码登陆后,地址栏返回:http://localhost:3000/?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJndWxpLXVzZXIiLCJpYXQiOjE2MjExNTUzMTgsImV4cCI6MTYyMTI0MTcxOCwiaWQiOiIxMzkzODQ3NTQ3MDc1Nzg4ODAyIiwibmlja25hbWUiOiJKeXVua2FpIn0.-IdNv8SCo3L39BQkxDxRqlALst2JnXe3wLbTHX-dOYk

查看数据库中增加了一条信息:
image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/140533.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

腾讯云存储

文章目录一、开通腾讯云存储1.注册腾讯云账号&#xff0c;开通对象服务2. 创建存储桶3.获取SecretId 和 SecretKey提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、开通腾讯云存储 在项目钟的图片以及文件需要归档存储。如果归档文件只保存到服务器…

WebRTC学习总结

WebRTC (Web Real-Time Communications) 是一项实时通讯技术&#xff0c;它允许网络应用或者站点&#xff0c;在不借助中间媒介的情况下&#xff0c;建立浏览器之间点对点&#xff08;Peer-to-Peer&#xff09;的连接&#xff0c;实现视频流和&#xff08;或&#xff09;音频流…

二十五、Docker (1)

&#x1f33b;&#x1f33b; 目录一、Docker的概述1.1 为什么要去学习Docker1.2 Docker 概述1.3 Docker的历史1.4 Docker 官网1.5 Docker能做什么1.6 DevOps(开发、运维)二、Docker安装启动&#xff08;官网&#xff09;2.1 Docker 架构2.1.1 镜像&#xff08;image)2.1.2 容器…

DICOM 图像传输:使用 LeadTools 实现 C-Store SCP 服务

文章目录开发环境创建 Qt Widgets 程序设计界面配置 LeadTools 路径编写代码使用 LDicomNet 实现 SCP 的步骤日志输出编写 SCP Server 类编写 SCP Client 类启动 LDicomNet 及启动监听编译程序运行程序发布与部署测试程序界面美化参考开发环境 LeadTools 17Qt 5.15.2 MSVC2019…

【Linux】进程间通信(万字详解) —— 上篇

&#x1f387;Linux&#xff1a; 博客主页&#xff1a;一起去看日落吗分享博主的在Linux中学习到的知识和遇到的问题博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 看似不起波澜的日复一日&#xff0c;一定会在某一天让你看见坚持…

谷粒学院——第九章、阿里云视频点播

阿里云视频点播 开通 地址&#xff1a; 上传测试 开通以后&#xff0c;点击控制台&#xff0c;然后选择音/视频&#xff1a; 注意&#xff1a;先点击启用存储地址再上传。 添加转码模版&#xff1a; 开发文档 官方地址&#xff1a;https://help.aliyun.com/p…

批量统计不同块的数量

CAD收集块的数量一般采用FI等命令&#xff0c;或者使用天正等软件&#xff0c;这些方法或多或少都存在某些问题。这时就可以编写插件满足不同场景的使用。已应用到实际工作中。 一、界面及其功能 采用c#制作cad插件&#xff0c;框选待统计范围&#xff0c;直接输出到表格中&…

数据库连接超时的处理

报错信息&#xff1a;Caused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms.at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:695)at com.zaxxer.hikari.pool.H…

Java高手速成 | 数据库实训:图书馆管理系统建模

图书馆管理系统是常见的管理信息系统&#xff0c;考虑到系统的推广性&#xff0c;本系统采用SQL SERVER2000作为数据库。并且采用PowerDesigner进行数据建模&#xff0c;从而自动生成sql脚本。 01、数据库概念设计 1. 数据库表设计 管理员表admin&#xff1a;管理员编号&am…

[JavaEE] volatile与wait和notify

专栏简介: JavaEE从入门到进阶 题目来源: leetcode,牛客,剑指offer. 创作目标: 记录学习JavaEE学习历程 希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长. 学历代表过去,能力代表现在,学习能力代表未来! 目录 一.volatile 关键字. 1.volatile 能保证内存可见性…

12个爆款 Java 开源项目

1JavaGuidehttps://github.com/Snailclimb/JavaGuide Star 10503【Java学习面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识。2symphonyhttps://github.com/b3log/symphony Star 6664一款用 Java 实现的现代化社区&#xff08;论坛/BBS/社交网络/博客&#xff09;平台…

17-Golang中的包

Golang中的包基本介绍包的三大作用相关说明包的注意事项和细节说明基本介绍 1.在实际的开发中&#xff0c;我们往往需要在不同的文件中&#xff0c;去调用其他文件的定义的幻术&#xff0c;比如main.go中&#xff0c;去使用utils.go文件中的函数2.包的本质就是创建不同的文件夹…

vue简单的数据传输

很久没有水文了&#xff0c;最近又得了新冠才好&#xff0c;学习也没什么进度&#xff0c;先复习下之前的组件的数据传输吧&#xff01; props传值 这个很简单就是在组件标签上转递数据&#xff0c;值得注意的是如果不使用v-bind:&#xff08;&#xff1a;&#xff09;,转递的…

任务二:Web隐藏信息获取

任务二:Web隐藏信息获取 任务环境说明: 服务器场景名:web20200604服务器场景用户名:未知通过本地PC中渗透测试平台Kali使用Nmap扫描目标靶机HTTP服务子目录,将扫描子目录命令所需参数及第四条扫描结果关键目录以&符号拼接,作为Flag提交(例:-p 22&/root/); …

【信息学CSP-J近16年历年真题64题】真题练习与解析 第11题之纪念品

纪念品 描述 小伟突然获得一种超能力,他知道未来 T 天 N 种纪念品每天的价格。某个纪念品 的价格是指购买一个该纪念品所需的金币数量,以及卖出一个该纪念品换回的金币数量。 每天,小伟可以进行以下两种交易无限次: 任选一个纪念品,若手上有足够金币,以当日价格购买该…

Android---DrawerLayout + NavigationView

现在 Android Studio 已经直接提供左滑菜单功能&#xff0c;只需要在创建新项目时选择 Navigation Drawer Activity 就可以直接创建一个有左滑菜单功能的 APP。 目录 DrawerLayout NavigationView android:src 与 app:srcCompat fitsSystemWindows DrawerLayout …

数据报告重要的是业务看得懂

一、前言上篇文章我们从指标库的角度梳理了指标是如何计算出来的&#xff0c;确保业务人员有数可寻&#xff0c;但对于业务人员而言&#xff0c;并不是所有的指标都看得懂&#xff0c;也不是所有的指标都需要理解&#xff0c;笔者见过太多不知所云的数据报告&#xff0c;各种模…

Maven高级-分模块拆分

Maven高级 分模块开发与设计 聚合 继承 属性 版本管理 资源配置 多环境开发配置 跳过测试 私服 分模块开发与设计 拷贝原始项目中对应的相关内容到ssm_pojo模块中 ​ 实体类&#xff08;User&#xff09; ​ 配置文件&#xff08;无&#xff09; User.java package com.i…

S32K144-S32DS 导入/编译/烧录 遇到的问题

找到你开发套件软件安装包的路径&#xff0c;里面有四个例子&#xff0c;任选一个&#xff1b;不要忘记复制到工作区 问题一&#xff1a;修改电机库的路径 问题二&#xff1a;freemaster路径问题 编译还是报错&#xff0c;好像还必须安装FreeMaster 安装之后&#xff0c;还是这…

电子招标采购系统源码:构建高效智能数字化采购

过去几十年&#xff0c;公用事业行业发生了重大变化。能源需求的转变导致企业利润率的波动&#xff0c;但不是运营成本的波动。 许多公用事业公司通过后勤部门流程自动化来削减成本&#xff0c;比如招采流程自动化。 在招采活动中&#xff0c;人工招采会产生盲点。由于公共事业…