微信小程序登录(生成token,token校验)——后端

写在前面:如果想自己开发微信小程序,需要先到微信小程序官方平台注册账号,地址为:https://mp.weixin.qq.com/wxopen/waregister?action=step1.

登录流程

在这里插入图片描述
其中,开发者服务器就是我们的后端服务器,微信接口服务就是微信提供的服务。openid是微信用户身份的唯一标识。开发者服务器中所谓的自定义登录状态,就是记录当前用户的相关信息,比如存储用户的openid到数据库、生成token等。当小程序获取到开发者服务器返回的自定义登录态(token)后,小程序可以记录下该值,用它作为与开发者服务器业务交互时的令牌。
官方指南:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html

实现

前端实现:
通过小程序发送请求,获取code
官方教程https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.login.html

var jsCode = '';
uni.login({provider: 'weixin',success: function success(loginRes) {if (loginRes.errMsg === 'login:ok') {console.log('-=-=-=-=loginRes-=-=-=', loginRes);jsCode = loginRes.code;}} }); 

后端实现:
1:在实现代码前,还是需要添加相关的配置文件,以方便不同环境下代码的运行:
添加配置属性类

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Component
@ConfigurationProperties(prefix = "sky.wechat")
@Data
public class WeChatProperties {private String appid; //小程序的appidprivate String secret; //小程序的秘钥
}

令牌配置

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Component
@ConfigurationProperties(prefix = "sky.jwt")
@Data
public class JwtProperties {/*** 用户端微信用户生成jwt令牌相关配置*/private String userSecretKey;private long userTtl;private String userTokenName;}

application-dev.yml 这里的appid和secret是注册小程序的相关信息

sky:wechat:appid: xxxsecret: yyyy

applicatio.yml中的相应设置

spring:profiles:active: dev
sky:jwt:# 配置用户端令牌user-secret-key: itcastuser-ttl: 7200000user-token-name: authentication# 微信相关配置wechat:appid: ${sky.wechat.appid}secret: ${sky.wechat.secret}

2:根据接口设置相应的DTO,VO,Entity:
DTO:表示服务层需要接收的数据和返回的数据;
将从前端页面受到的JSON数据封装成UserDTO对象:

import lombok.Data;import java.io.Serializable;/*** C端用户登录*/
@Data
public class UserLoginDTO implements Serializable {private String code;}

VO:表示展示层需要展示的数据;
将返回给前端的数据封装成UserVO对象:

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserLoginVO implements Serializable {private Long id;private String openid;private String token;}

Entity:该类每一个变量对应于数据库中的数据表的一个字段,也是常说的PO;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {private static final long serialVersionUID = 1L;private Long id;//微信用户唯一标识private String openid;//姓名private String name;//手机号private String phone;//性别 0 女 1 男private String sex;//身份证号private String idNumber;//头像private String avatar;//注册时间private LocalDateTime createTime;
}

3:添加登录接口
由上至下:controller–> service --> mapper
UserController

import com.sky.constant.JwtClaimsConstant;
import com.sky.dto.UserLoginDTO;
import com.sky.entity.User;
import com.sky.properties.JwtProperties;
import com.sky.result.Result;
import com.sky.service.UserService;
import com.sky.utils.JwtUtil;
import com.sky.vo.UserLoginVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;@RestController
@RequestMapping("/user/user")
@Api(tags = "用户登录相关接口")
@Slf4j
public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate JwtProperties jwtProperties;/*** 微信登录* @param userLoginDTO* @return*/@PostMapping("/login")@ApiOperation("微信登录")public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){log.info("微信用户登录:{}",userLoginDTO.getCode());// 微信登录User user = userService.wxLogin(userLoginDTO);// 为微信用户生成jwt令牌Map<String, Object> claims = new HashMap<>();claims.put(JwtClaimsConstant.USER_ID,user.getId());// JwtProperties与application中的配置相关联,然后通过注入的方式间接的拿到配置文件中添加的属性值String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), claims);UserLoginVO userLoginVO = UserLoginVO.builder().id(user.getId()).openid(user.getOpenid()).token(token).build();return Result.success(userLoginVO);}
}

Userservice

import com.sky.dto.UserLoginDTO;
import com.sky.entity.User;public interface UserService {/*** 微信登录* @param userLoginDTO* @return*/User wxLogin(UserLoginDTO userLoginDTO);
}

UserserviceImpl

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sky.constant.MessageConstant;
import com.sky.dto.UserLoginDTO;
import com.sky.entity.User;
import com.sky.exception.LoginFailedException;
import com.sky.mapper.UserMapper;
import com.sky.properties.WeChatProperties;
import com.sky.service.UserService;
import com.sky.utils.HttpClientUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;@Service
@Slf4j
public class UserServiceImpl implements UserService {// 微信服务接口地址public static final String  WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";@Autowiredprivate WeChatProperties weChatProperties;@Autowiredprivate UserMapper userMapper;/*** 用户微信登录* @param userLoginDTO* @return*/@Overridepublic User wxLogin(UserLoginDTO userLoginDTO) {//获取openidString openid = getOpenid(userLoginDTO.getCode());// 判断openid是否为空,如果为空则表示登录失败,抛出业务异常if(openid == null){throw new LoginFailedException(MessageConstant.LOGIN_FAILED);}// 判断当前用户是否是该系统的新用户User user = userMapper.getByOpenid(openid);// 如果是新用户,则自动完成注册if(user == null){user = User.builder().openid(openid).createTime(LocalDateTime.now()).build();userMapper.insert(user);}// 返回用户对象return user;}/*** 调用微信接口服务,获得当前微信用户的openid* @param code* @return*/private String getOpenid(String code){// 调用微信接口服务,获得当前微信用户的openidMap<String, String> map = new HashMap<>();map.put("appid",weChatProperties.getAppid());map.put("secret",weChatProperties.getSecret());map.put("js_code",code);map.put("grant_type","authorization_code");String json = HttpClientUtil.doGet(WX_LOGIN, map); // 得到微信端服务器返回的json数据JSONObject jsonObject = JSON.parseObject(json);String openid = jsonObject.getString("openid"); // 解析json数据,获取openidreturn openid;}
}

UserMapper

import com.sky.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;@Mapper
public interface UserMapper {/*** 根据openid查询用户* @param openid* @return*/@Select("select * from user where openid = #{openid}")User getByOpenid(String openid);/*** 创建新用户并返回主键值* @param user*/void insert(User user);
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.UserMapper"><insert id="insert" useGeneratedKeys="true" keyProperty="id">insert into user (openid, name, phone, sex, id_number, avatar, create_time)values (#{openid}, #{name}, #{phone}, #{sex}, #{idNumber}, #{avatar}, #{createTime})</insert></mapper>

补充

生成jwt令牌:(token)

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;public class JwtUtil {/*** 生成jwt* 使用Hs256算法, 私匙使用固定秘钥** @param secretKey jwt秘钥* @param ttlMillis jwt过期时间(毫秒)* @param claims    设置的信息* @return*/public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {// 指定签名的时候使用的签名算法,也就是header那部分SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;// 生成JWT的时间long expMillis = System.currentTimeMillis() + ttlMillis;Date exp = new Date(expMillis);// 设置jwt的bodyJwtBuilder builder = Jwts.builder()// 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的.setClaims(claims)// 设置签名使用的签名算法和签名使用的秘钥.signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))// 设置过期时间.setExpiration(exp);return builder.compact();}/*** Token解密** @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个* @param token     加密后的token* @return*/public static Claims parseJWT(String secretKey, String token) {// 得到DefaultJwtParserClaims claims = Jwts.parser()// 设置签名的秘钥.setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))// 设置需要解析的jwt.parseClaimsJws(token).getBody();return claims;}}

jwt校验:
1:添加拦截器:

import com.sky.constant.JwtClaimsConstant;
import com.sky.context.BaseContext;
import com.sky.properties.JwtProperties;
import com.sky.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** jwt令牌校验的拦截器*/
@Component
@Slf4j
public class JwtTokenUserInterceptor implements HandlerInterceptor {@Autowiredprivate JwtProperties jwtProperties;/*** 校验jwt** @param request* @param response* @param handler* @return* @throws Exception*/public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 输出当前线程的IDSystem.out.println("当前线程的ID:"+Thread.currentThread().getId());//判断当前拦截到的是Controller的方法还是其他资源if (!(handler instanceof HandlerMethod)) {//当前拦截到的不是动态方法,直接放行return true;}//1、从请求头中获取令牌String token = request.getHeader(jwtProperties.getUserTokenName());//2、校验令牌try {log.info("jwt校验:{}", token);Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);Long userId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());// 将empId存储在当前ThreadLocal中(线程局部变量存储空间,具有线程隔离的效果)BaseContext.setCurrentId(userId);log.info("当前用户id:", userId);//3、通过,放行return true;} catch (Exception ex) {//4、不通过,响应401状态码response.setStatus(401);return false;}}
}

在配置文件中注册该拦截器:

import com.sky.interceptor.JwtTokenUserInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;/*** 配置类,注册web层相关组件*/
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {@Autowiredprivate JwtTokenUserInterceptor jwtTokenUserInterceptor;/*** 注册自定义拦截器** @param registry*/protected void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(jwtTokenUserInterceptor).addPathPatterns("user/**").excludePathPatterns("/user/user/login").excludePathPatterns("/user/shop/status");}}

HttpClient
上面的代码在请求微信端的时候,使用的HttpClient发送的请求,其相关的代码见HttpClient基础。


本次的微信小程序登录操作实际也是一个第三方登录,若是想实现其他平台的登录,也可以按照这套流程走~今天又进步了一点点,nice!

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

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

相关文章

传感器基础:传感器使用与编程使用(三)

目录 常用传感器讲解九--雨滴传感器具体讲解电路连接代码实现 常用传感器讲解十--光传感器根据亮度安排灯具体讲解电路连接代码实现 常用传感器讲解七--light cup&#xff08;KY-008&#xff09;具体讲解电路连接代码实现 常用传感器讲解十二--倾斜开关传感器&#xff08;KY-02…

Java版企业电子招标采购系统源码——鸿鹄电子招投标系统的技术特点

在数字化时代&#xff0c;采购管理也正经历着前所未有的变革。全过程数字化采购管理成为了企业追求高效、透明和规范的关键。该系统通过Spring Cloud、Spring Boot2、Mybatis等先进技术&#xff0c;打造了从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通过…

鸿蒙 - arkTs: 页面路由

页面栈最大容量为32&#xff0c;使用router.clear()可以清空页面栈&#xff0c;释放资源 跳转方式&#xff1a; router.pushUrl&#xff1a;目标页压入页面栈&#xff0c;使用router.back()可以返回上个页面。router.replaceUrl&#xff1a;目标页替换当前页&#xff0c;会清…

旧衣回收小程序搭建,稳占回收市场

近几年我国大众的消费水平不断提升&#xff0c;闲置物品也相应增加了不少&#xff0c;尤其是闲置衣服&#xff0c;为了减少资源浪费&#xff0c;旧衣服回收回收行业受到了大众的关注。 目前我国旧衣服回收行业的市场规模达到了300多亿元&#xff0c;旧衣回收行业的商业价值非常…

Linux 查看系统类型和版本(内核版本 | 发行版本)

Linux 查看系统类型和版本 首先普及下linux系统的版本内容1. 查看linux系统内核版本2. 查看linux系统发行版本 首先普及下linux系统的版本内容 内核版本和发行版本区别 内核版本就是指 Linux 中最基层的代码&#xff0c;版本号如 Linux version 3.10.0-327.22.2.el7.x86_64发行…

JavaSE50题:26. (数组练习题)使奇数位于偶数之前

概述 调整数组顺序使得奇数位于偶数之前&#xff0c;调整之后&#xff0c;不关心大小顺序。 如数组&#xff1a;{1,2,3,4,5,6} 调整后可能是&#xff1a;{1&#xff0c;5&#xff0c;3&#xff0c;4&#xff0c;2&#xff0c;6} 方法 定义 left 和 right&#xff0c;二者分别…

第16章Java

通过java的反射机制&#xff0c;程序员可以更深入的控制程序的运行过程。例如&#xff0c;可在程序运行时对象用户输入的信息进行验证&#xff0c;还可以逆向控制程序的执行过程&#xff0c;讲解了反射&#xff0c;另外java还提供了Annotation注解功能&#xff0c;该功能建立在…

PYTHON基础:线性算法--线性回归|岭回归|套索回归模型

常用的三种线性模型算法–线性回归模型、岭回归模型、套索回归模型 线性模型基本概念 线性模型的一般预测模型是下面这个样子的&#xff0c;一般有多个变量&#xff0c;也可以称为多个特征x1、x2、x3 … 最简单的线性模型就是一条直线直线的方程式&#xff0c;b0是截距&#…

vue3 根据用户权限控制左侧菜单和路由拦截

目录 前言 整体思路 详细开发 1.左侧菜单的显隐控制 2.控制路由权限 补充权限控制 总结 前言 我这里是vue3开发的一个后台管理系统&#xff0c;所以涉及用户权限管理&#xff0c;以及页面权限等&#xff0c;其他模块部分可以查看专栏&#xff0c;这里只对怎么实现根据用…

实践:修改正式站表名

一.引言 现在有一个需求&#xff0c;发现正式站的数据库里面有个表名不合理&#xff0c;需要修改&#xff0c;但是正式站一般不能修改表名&#xff0c;所以现在的做法是新建一个表&#xff0c;将旧表的数据复制到新表&#xff0c;然后将旧表删除。由于正式站的数据还在不断产生…

[Redis实战]优惠券秒杀

三、优惠券秒杀 3.1 全局唯一ID 每个店铺都可以发布优惠券&#xff1a; 当用户抢购时&#xff0c;就会生成订单并保存到tb_voucher_order这种表中&#xff0c;而订单表如果使用数据库自增ID就存在一些问题&#xff1a; id的规律性太明显受单表数据量的限制 场景分析一&…

el-date-picker 日期选择限制

el-date-picker 日期选择限制 场景&#xff1a; 选择时间的区间是31天&#xff0c;默认显示最近一周的数据 代码&#xff1a; <el-date-picker v-model"due_date" type"daterange" range-separator"至" start-placeholder"开始日期&qu…

多线程多进程的使用场景和常见问题处理

多线程多进程的使用场景 多线程和多进程都是用来实现并发执行的技术&#xff0c;它们可以提高程序的性能和效率。它们各自适用于不同的场景&#xff1a; 多线程的使用场景&#xff1a; I/O密集型任务&#xff1a;当程序需要进行大量的I/O操作&#xff08;如文件读写、网络通…

【PythonRS】基于Python对栅格数据进行归一化(统一量纲至0~1)

有段时间没有更新Python处理栅格、矢量数据了&#xff0c;一部分是因为之前基本上已经把如何使用Python处理地理数据的方法覆盖完了&#xff0c;另一部分是因为最近有其他方面的知识需要学习和巩固。也是赶巧&#xff0c;最近有个项目需要构建模型对影像进行反演需要用到归一化…

Word2Vec详解: CBOW Skip-gram和负采样

Word2Vec&#xff1a; CBOW & Skip-gram 如果是拿一个词语的上下文作为输入&#xff0c;来预测这个词语本身&#xff0c;则是 CBOW 模型。 而如果是用一个词语作为输入&#xff0c;来预测它周围的上下文&#xff0c;那这个模型叫做 Skip-gram 模型。 CBOW 模型 连续词袋模…

简单实现一个自定义loader

webpack定义的loader需要遵循单一功能原则&#xff0c;也就是一个loader只实现一个功能。在实现开发中&#xff0c;我们会直接使用诸如蓝湖等生成的样式&#xff0c;比如 button{background: rgb(255, 85, 46); }但为了考虑主题换肤&#xff0c;我们实现的想要的可能是 butto…

在用Vite开发时静态图片放哪里,才能保证显示,不出现找不到资源

在用Vite开发时静态图片放哪里 在用Vite开发时静态图片&#xff08;资源&#xff09;放哪里呢 &#xff1f; 如果你想直接全部显示的那么请你把静态资源放到public目录下面&#xff0c;这样你一打包所有的静态资源都会放到打包根目录下。但是此时你在项目中引用的地址一定要是…

OM6621选型指南详细对比应用蓝牙遥控智能穿戴游戏手柄

昂瑞微蓝牙OM6621系列对比选型指南 OM6621EM和OM6621ED性能特点 超低功耗蓝牙SOC精简BLE5.1协议栈主频64Mhz&#xff0c;40KB RAM集成红外线收发电路主要应用在语音遥控、鼠标、水表等 功能特点 功耗&#xff1a;1秒连接平均电流&#xff1a;11uA峰值电流&#xff1a;TX0dBm…

Vue 监听状态 watch 与监听状态 watchEffect

监听状态 watch watch 函数用于监听响应式数据的变化。 使用 watch 函数监听基于 ref 创建的响应式数据 (基本数据类型)。 import { ref, watch } from "vue" export default {setup() {const text ref("")watch(text, (current, previous) > {conso…

javascript2

文章目录 一、 内置对象1&#xff09; 对象2&#xff09; Array 数组1. 创建2. 特点3. 属性和方法4. 二维数组 3&#xff09;String 对象1. 创建2. 特点3. 属性4. 方法 4) Math 对象1. 定义2. 属性3. 方法 5&#xff09;日期对象1. 创建日期对象2. 日期对象方法 1. 创建日期对象…