目录

概述

微信登录接口说明

关于获取微信用户的信息

前端代码(uni-app)

后端代码(SpringBoot)

配置文件:application.yml

配置文件:Pom.xml

类:WeChatModel

类:WeChatSessionModel

类:UserInfoController

业务层实现类:UserInfoServiceImpl

工具类:JWTUtils

拦截器配置-自定义拦截器

拦截器配置-注册自定义拦截器

测试(Postman)

总结


概述

本篇博本主要为了记录使用uni-app开发微信小程序时实现微信一键登录功能,并且使用JWT实现身份认证

微信登录接口说明

可以点击==>官方的登录时序图<== ,看到官方描述登录的流程。

大概描述就是 :uni-app调用login()方法,获取临时凭证code,将此临时凭证发送到我们的后端,后端通过我们传入的临时凭证code,调用微信接口服务获取当前微信用户的唯一标识openid,我们就可以凭借此openid知道哪一个用户在进行登录操作。

值得注意的是

1.通过login()获取的临时凭证code的有效期为5分钟,并且只能使用一次。

2.后端调用微信凭证验证接口获取openid需要appIdappSecret,两者都可以到微信小程序官网==>开发管理==>开发设置 中获取。

如下是我画的整体大概流程

总体说明 整个流程就是当用户点击"微信一键登录",传入临时凭证code,后端通过临时凭证code去微信服务接口获取该用户的openid,此openid是唯一的不会变的。 那么我们就可以将openid存储用户数据表中,用来标识此用户。

关于获取微信用户的信息

关于API:uni.getUserInfo(OBJECT) 和 uni.getUserProfile(OBJECT) 接口的说明。

目前两个接口都无法获取用户信息,只能获取到默认用户信息,名称为'微信用户',头像为灰色用户头像。

关于官方描述:==>原文<==

 那么解决方案只能是用户登录后,让用户自行上传修改信息。

前端代码(uni-app)

前端的代码很简单,只是调用uni.login()获取临时凭证code传入后端接口即可。

<template><view><button id="loginHanle" @tap="goLogin">微信一键登录</button></view>
</template>
<script>export default {methods: {// 登录按钮触发loginHanle() {// 获取临时登录凭证code。uni.login({provider: 'weixin',success(res) {console.log(res.code);// 调用后端接口,传入codeaxios.post('http://localhost:8888/api/userInfo/login',{code:res.code})
.then(res=>{// 登录成功后的逻辑处理...
})}})}}
</script>

后端代码(SpringBoot)

后端需要接收前端传入的临时凭证code,向微信服务器发送请求获取登录用户的openid。并且操作数据库后返回用户信息,以及响应头返回token


配置文件:application.yml

# JWT配置
jwt:header: "Authorization" #token返回头部tokenPrefix: "Bearer " #token前缀secret: "maohe101" #密钥expireTime: 3600000 #token有效时间 3600000毫秒 ==> 60分钟
# 微信小程序配置码
APPID: 自己的appid
APPSECRET: 自己的密匙

配置文件:Pom.xml

添加需要依赖 

<!-- jwt支持 -->
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.19.2</version>
</dependency><!-- json格式化 -->
<dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.9</version>
</dependency>

类:WeChatModel

接收前端传入参数

package com.mh.common;import lombok.Data;/*** Date:2023/5/24* author:zmh* description: 接收小程序传入参数**/@Data
public class WeChatModel {/*** 临时登录凭证*/private String code;/*** 微信服务器上的唯一id*/private String openId;
}

类:WeChatSessionModel

接收调用微信验证code后返回的数据。

package com.mh.common;import lombok.Data;/*** Date:2023/5/24* author:zmh* description: 接收微信服务器返回参数**/@Data
public class WeChatSessionModel {/*** 微信服务器上辨识用户的唯一id*/private String openid;/*** 身份凭证*/private String session_key;/*** 错误代码*/private String errcode;/*** 错误信息*/private String errmsg;
}

类:UserInfoController

接收临时凭证code,调用业务层方法

@Autowired
private UserInfoService userInfoService;/*** 微信登录* @param weChatModel 获取临时凭证code* @param response ·* @return 返回执行结果*/
@PostMapping("/login")
public R<String> loginCheck(@RequestBody WeChatModel weChatModel, HttpServletResponse response){// 检查登录Map<String, Object> resultMap = userInfoService.checkLogin(weChatModel.getCode());// resultMap大于1为通过,业务层判断正确后返回用户信息和token,所以应该size为2才正确。if (resultMap.size() > 1){log.info("创建的token为=>{}", resultMap.get("token"));// 将token添加入响应头以及返回用户信息response.setHeader(JWTUtils.header, (String) resultMap.get("token"));return R.success(resultMap.get("user").toString());}else{// 当返回map的size为1时,即为报错信息return R.error(resultMap.get("errmsg").toString());}
}

业务层实现类:UserInfoServiceImpl

登录验证的逻辑处理 

package com.mh.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.mh.common.R;
import com.mh.common.WeChatSessionModel;
import com.mh.common.WeChatModel;
import com.mh.dao.FansInfoDao;
import com.mh.dao.FollowInfoDao;
import com.mh.dao.UserInfoDao;
import com.mh.pojo.FansInfo;
import com.mh.pojo.FollowInfo;
import com.mh.pojo.UserInfo;
import com.mh.service.UserInfoService;
import com.mh.utils.JWTUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;import java.util.HashMap;
import java.util.Map;
import java.util.UUID;/*** Date:2023/5/24* author:zmh* description: 用户信息业务层实现类**/@Service
@Slf4j
public class UserInfoServiceImpl extends ServiceImpl<UserInfoDao, UserInfo> implements UserInfoService {@Autowiredprivate UserInfoDao userInfoDao;@Value("${APPID}")private String appid;@Value("${APPSECRET}")private String appsecret;@Autowiredprivate RestTemplate restTemplate;// 用于存储用户信息和tokenMap<String,Object> map = new HashMap<>();/*** 登录验证* @param code 临时登录码* @return ·*/public Map<String,Object> checkLogin(String code){// 根据传入code,调用微信服务器,获取唯一openid// 微信服务器接口地址String url = "https://api.weixin.qq.com/sns/jscode2session?appid="+appid+ "&secret="+appsecret+"&js_code="+ code +"&grant_type=authorization_code";String errmsg = "";String errcode = "";String session_key = "";String openid = "";WeChatSessionModel weChatSessionModel;// 发送请求ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, String.class);// 判断请求是否成功if(responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) {// 获取主要内容String sessionData = responseEntity.getBody();Gson gson = new Gson();//将json字符串转化为实体类;weChatSessionModel = gson.fromJson(sessionData, WeChatSessionModel.class);log.info("返回的数据==>{}",weChatSessionModel);//获取用户的唯一标识openidopenid = weChatSessionModel.getOpenid();//获取错误码errcode = weChatSessionModel.getErrcode();//获取错误信息errmsg = weChatSessionModel.getErrmsg();}else{log.info("出现错误,错误信息:{}",errmsg );map.put("errmsg",errmsg);return map;}// 判断是否成功获取到openidif ("".equals(openid) || openid == null){log.info("错误获取openid,错误信息:{}",errmsg);map.put("errmsg",errmsg);return map;}else{// 判断用户是否存在,查询数据库LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(UserInfo::getOpenid, openid);UserInfo userInfo = userInfoDao.selectOne(queryWrapper);// 不存在,加入数据表if (userInfo == null){// 填充初始信息UserInfo tempUserInfo = new UserInfo(UUID.randomUUID().toString(), openid, "微信用户", 1,"default.png", "",0,0,0);// 加入数据表userInfoDao.insert(tempUserInfo);// 加入map返回map.put("user",tempUserInfo);// 调用自定义类封装的方法,创建tokenString token = JWTUtils.createToken(tempUserInfo.getId().toString());map.put("token",token);return map;}else{// 存在,将用户信息加入map返回map.put("user",userInfo);String token = JWTUtils.createToken(userInfo.getId().toString());map.put("token",token);return map;}}}}

工具类:JWTUtils

用于创建,验证和更新token

package com.mh.utils;import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.TokenExpiredException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;import java.util.Date;/*** Date:2023/5/24* author:zmh* description:JWT工具类,JWT生成,验证**/@Component
@Data
@ConfigurationProperties(prefix = "jwt")
@Slf4j
public class JWTUtils {//定义token返回头部public static String header;//token前缀public static String tokenPrefix;//签名密钥public static String secret;//有效期public static long expireTime;public void setHeader(String header) {JWTUtils.header = header;}public void setTokenPrefix(String tokenPrefix) {JWTUtils.tokenPrefix = tokenPrefix;}public void setSecret(String secret) {JWTUtils.secret = secret;}public void setExpireTime(long expireTime) {JWTUtils.expireTime = expireTime;}/*** 创建TOKEN** @param sub* @return*/public static String createToken(String sub) {return tokenPrefix + JWT.create().withSubject(sub).withExpiresAt(new Date(System.currentTimeMillis() + expireTime)).sign(Algorithm.HMAC512(secret));}/*** 验证token** @param token*/public static String validateToken(String token) {try {return JWT.require(Algorithm.HMAC512(secret)).build().verify(token.replace(tokenPrefix, "")).getSubject();} catch (TokenExpiredException e) {log.info("token已过期");return "";} catch (Exception e) {log.info("token验证失败");return "";}}/*** 检查token是否需要更新* @param token ·* @return*/public static boolean isNeedUpdate(String token) {//获取token过期时间Date expiresAt = null;try {expiresAt = JWT.require(Algorithm.HMAC512(secret)).build().verify(token.replace(tokenPrefix, "")).getExpiresAt();} catch (TokenExpiredException e) {return true;} catch (Exception e) {log.info("token验证失败");return false;}//如果剩余过期时间少于过期时常的一般时 需要更新return (expiresAt.getTime() - System.currentTimeMillis()) < (expireTime >> 1);}}

拦截器配置-自定义拦截器

当用户访问非登录接口时,需要拦截请求,判断用户的请求头是否携带了正确的token,携带了代表登录过了,请求通过,返回数据,若未token验证失败则错误提示。

package com.mh.utils;import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** Date:2023/5/26* author:zmh* description: 自定义登录拦截器**/@Slf4j
public class UserLoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 获取请求头,header值为Authorization,承载tokenString token = request.getHeader(JWTUtils.header);//token不存在if (token == null || token.equals("")) {log.info("传入token为空");response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token为空!");return false;}//验证tokenString sub = JWTUtils.validateToken(token);if (sub == null || sub.equals("")){log.info("token验证失败");response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token验证失败!");return false;}//更新token有效时间 (如果需要更新其实就是产生一个新的token)if (JWTUtils.isNeedUpdate(token)){String newToken = JWTUtils.createToken(sub);response.setHeader(JWTUtils.header,newToken);}return true;}}

拦截器配置-注册自定义拦截器

package com.mh.config;import com.mh.utils.UserLoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** Date:2023/5/26* author:zmh* description: MVW配置**/@Configuration
public class WebMvcConfig implements WebMvcConfigurer {/*** 注册自定义拦截器* @param registry ·*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new UserLoginInterceptor()).addPathPatterns("/api/**") // 拦截地址.excludePathPatterns("/api/userInfo/login");// 开放登录路径}}

测试(Postman)

1.测试微信一键登录 

微信小程序获取临时凭证code

 通过返回的code到postman中测试调用后端登录接口

 获取到返回,代表登录成功。

2.测试token的验证 

调用非登录接口,会被拦截进行token的检查。

后端日志输出: 

 携带错误或过期的token,验证失败

 后端日志输出 

 携带正确且在有效期内的token,验证成功,测试通过。

总结

对于如上代码,其实微信登录的逻辑是比较简单的,代码更多的是在处理身份验证(token验证),后端设置了请求拦截器,会去拦截所有非登录接口,通过检查token判断是否登录过了。

对于前端发送请求,如上只是使用了Postman进行接口的访问,并没有从代码层面去发送请求,那么,其实前端是比较需要去封装请求方法的,在封装的请求方法中加入请求头携带token,避免每一次请求都需要手动加上请求头携带token。

如博文内容存在不足之处请指出,感谢访问。

微信小程序一键登录功能,使用uni-app和springboot(JWT鉴权)相关推荐

  1. 2021-11-05 微信小程序实现登录功能

    微信小程序实现登录功能(云函数的创建和调用) Page({data: {userInfo: {},},getUserProfile (e) {wx.getUserProfile({desc: '用于完 ...

  2. 微信小程序-注册登录功能-本地数据保存-页面数据交替

    Title:微信小程序-注册登录功能-本地数据保存-页面数据交替 完美-小程序登录注册功能.rar-- 访问码:yqa5 1.主页面 主页面login.js代码 // pages/login/logi ...

  3. 基于uni-app实现微信小程序一键登录和退出登录功能

    起因 目前正在使用uni-app开发一个微信小程序,开发到登录模块时通过查阅uni-app官方教程.微信小程序官方文档.网上的教程终于是实现了微信小程序的登录模块,现总结分享给大家,共同学习. 总体思 ...

  4. uniapp 阿里云开发微信小程序一键登录

    1.插件市场导入uni-id公用模块 插件市场 uni-id : https://ext.dcloud.net.cn/plugin?id=2116 导入成功后会在项目云文件夹下自动生成common目录 ...

  5. 如何完成微信小程序的登录功能

    前言 微信小程序是一个轻量级的,不用安装包,就可以打开的一个小工具,那我们就看一下,微信小程序是如何登录的 开始 第一步,搭建基础页面. <template><view class= ...

  6. 【微信小程序】登录功能实现及讲解(获取用户唯一标识)

    微信小程序开发交流qq群   173683895    承接微信小程序开发.扫码加微信. 正文: 功能:登录实现并获取到用户唯一标识 官方文档地址:可以先看完我的文章再看官方地址 实现步骤:1.调用微 ...

  7. 微信小程序用户登录功能无法使用

    背景 一个半年前的小程序项目了,最近一个用我项目的朋友说用户登录功能不能用了,小程序端.后端都没有报错.只有我开发时留下的,信息提示. 我第一个反应就是微信小程序在今年三月份更新的接口,wx.getU ...

  8. 微信小程序手机号一键登录--获取用户手机号--微信小程序一键登录

    前提:现在微信小程序登录我参考了几个大的公司的登录,基本分下面几种 1.微信用户一键登录,授权用户基本信息,不获取手机号 2.微信手机号一键登录,根据手机号去关联在其他平台数据,用手机号关联.然后再调 ...

  9. json 微信小程序 筛选_GitHub - zhengyangkang/sl-filter: uni -app 一款使用简单的筛选组件,适配app、微信小程序、H5。...

    sl-filter 筛选 筛选组件,组件名:sl-filter dcloud插件市场地址 sl-filter 简介 一款使用简单的筛选组件,适配app.微信小程序.H5. 感谢分享 效果图 并列菜单 ...

最新文章

  1. 关于深度残差收缩网络,你需要知道这几点
  2. DBUtils开源JDBC类库,对JDBC简单封装(作用是:简化编码工作量,同时不会影响程序的性能)...
  3. C++ algorithm中find系列函数总结
  4. linux桌面创建文档,有没有可以创建.desktop文件的GUI应用程序?
  5. tpshop防止sql注入补丁
  6. JsonRequestBehavior.AllowGet 方便浏览器调试
  7. Python 获取服务器的CPU个数
  8. 处理32人!永久取消申报基金/报奖资格……又一批科研不端案例被通报批评
  9. 假如地球变成甜甜圈形状,世界会变成什么样子?
  10. linux系统写一个脚本,编写一个简单的linuxshell脚本
  11. linux怎么杀掉mpd进程,linux怎么样安装mpd进程管理器
  12. Python GStreamer Tutorial
  13. 【Android智能硬件开发】【004】通过PackageInstaller静默安装apk
  14. 学3D建模的电脑配置
  15. 安卓防止微信调整字体大小
  16. requests关于Exceeded 30 redirects
  17. Cash-secured Puts
  18. 电脑装windows和安卓双系统引导_Remix mini安卓电脑,玩的不只是Windows那张皮
  19. 非科班学弟如何转行斩获 ATM 大厂的 Offer ?
  20. 厦门大学LaTeX模板:页眉页脚设置

热门文章

  1. 星特朗望远镜怎么样_质量曝光星特朗和博冠的**望远镜哪个好?分析有区别没有?真实体验诉说...
  2. 应用图标无法从任务栏取消固定问题
  3. Nginx配置+转发8080端口+页面静态缓存+https配置
  4. 双色球彩票java版
  5. 单片机 怎调用显示屏字库_单片机巧用Windows矢量字库
  6. MySql约束和隔离性问题练习
  7. 嵌入式linux液晶显示中文,嵌入式系统Linux下液晶显示的实现_徐喆.pdf
  8. 零样本迁移?全新多语言预训练模型DeltaLM!
  9. vts传感器采取船舶的_船舶交通服务系统介绍
  10. 主动扫描-Nmap-端口、系统、服务扫描