当前位置: 首页 > news >正文

【知识点总结】微信登录流程与Java Spring 实现

微信登录流程

微信登录:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html

img

步骤分析:

  1. 微信小程序端, 调用 wx.login 请求微信接口服务, 获得登录临时代码code, 就是授权码;
  2. 微信小程序端, 调用 wx.request 发送请求并携带 code, 请求开发者服务器(自己编写的后端服务).
  3. 开发者服务端, 通过 HttpClient 向微信接口服务发送请求, 并携带 appid + appsecret + code 三个参数和默认grant_type.
  4. 开发者服务端,接收微信接口服务返回的数据,session_key+opendId等。opendId是微信用户的唯一标识。
  5. 开发者服务端,自定义登录态,生成令牌(token)和openid等数据返回给小程序端,方便后绪请求身份校验。
  6. 小程序端,收到自定义登录态,存储storage。
  7. 小程序端,后绪通过wx.request()发起业务请求时,携带token。
  8. 开发者服务端,收到请求后,通过携带的token,解析当前登录用户的id。
  9. 开发者服务端,身份校验通过后,继续相关的业务逻辑处理,最终返回业务数据。

问题思考: 微信登录是什么, 和正常的登录区别在哪里?

  1. 微信登录是为了获得 openid
  2. 前端也是可以通过网络请求获得 openid 的, 但是这样会暴露 appid , secret,session_key 等隐私信息
  3. 所以微信登录的逻辑是前端调用微信登录接口, 后端调用微信接口, 后端返回 openid 给前端
  4. 后端获得到 openid 后会自动注册用户, 并返回 jwt 令牌给前端, jwt 令牌里面包含用户 id, 可以通过 id 查询数据库获得用户信息

问题思考: openid 是干嘛用的?

微信登录得到的OpenID在微信小程序和公众平台环境中扮演着至关重要的角色。以下是OpenID的具体用途:

  1. 用户身份验证

    • OpenID是微信用户在公众帐号(包括小程序)、H5、APP下的唯一标识。每个应用都有一个appid,不同的appid对应的openid不同。通过OpenID,开发者可以在小程序或公众平台中对用户进行身份验证,确保用户的安全性和唯一性。这有助于防止恶意用户创建多个账户进行不当行为。
  2. 跨平台登录

    • OpenID允许用户使用已有的微信账户信息进行登录,无需在每个小程序或应用中再次创建新的账户。这为用户提供了极大的便利,减少了重复注册和登录的麻烦。
  3. 用户信息获取

    • 小程序在用户登录后会返回一个包含OpenID的用户信息。开发者可以通过OpenID获取用户的基本信息,如用户头像、昵称等。这些信息对于个性化推荐、用户管理等操作非常有用。
  4. 数据统计与分析

    • 通过OpenID,开发者可以对用户的行为进行统计和分析,例如用户的浏览记录、购买记录、搜索记录等。这些数据对于优化产品和服务、提高用户体验具有重要意义。
  5. 安全性增强

    • OpenID采用加密传输技术,保障用户信息的传输安全。同时,OpenID作为用户的唯一标识,在需要进行安全验证的操作时(如对消息进行加密和解密、对数据进行签名等)也非常有用。
  6. 隐私保护

    • OpenID采用非实名制认证方式,用户可以保护自己的隐私信息不被滥用。在获取OpenID的过程中,开发者需要遵循微信小程序平台的相关规定,确保用户隐私和利益的保护。同时,在传输和存储OpenID时,也需要进行加密和解密等安全处理,防止信息泄露。

综上所述,微信登录得到的OpenID在微信小程序和公众平台环境中具有多种重要作用。它是用户身份验证、跨平台登录、用户信息获取、数据统计与分析、安全性增强以及隐私保护等方面的重要工具。

代码开发

1 定义相关配置

配置微信登录所需配置项:

application-dev.yml

sky:wechat:appid: wxffb3637a228223b8secret: 84311df9199ecacdf4f12d27b6b9522d

application.yml

sky:wechat:appid: ${sky.wechat.appid}secret: ${sky.wechat.secret}

配置为微信用户生成jwt令牌时使用的配置项:

application.yml

sky:jwt:# 设置jwt签名加密时使用的秘钥admin-secret-key: itcast# 设置jwt过期时间admin-ttl: 7200000# 设置前端传递过来的令牌名称admin-token-name: tokenuser-secret-key: itheimauser-ttl: 7200000user-token-name: authentication
2 DTO设计

根据传入参数设计DTO类:

image-20221205183625049

在sky-pojo模块,UserLoginDTO.java已定义

package com.sky.dto;import lombok.Data;import java.io.Serializable;/*** C端用户登录*/
@Data
public class UserLoginDTO implements Serializable {private String code;}
3 VO设计

根据返回数据设计VO类:

image-20221205183923272

在sky-pojo模块,UserLoginVO.java已定义

package com.sky.vo;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;}
4 Controller层

根据接口定义创建UserController的login方法:

package com.sky.controller.user;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 = "C端用户相关接口")
@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());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);}
}

其中,JwtClaimsConstant.USER_ID常量已定义。

5 Service层接口

创建UserService接口:

package com.sky.service;import com.sky.dto.UserLoginDTO;
import com.sky.entity.User;public interface UserService {/*** 微信登录* @param userLoginDTO* @return*/User wxLogin(UserLoginDTO userLoginDTO);
}
6 Service层实现类

**创建UserServiceImpl实现类:**实现获取微信用户的openid和微信登录功能

package com.sky.service.impl;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*/public User wxLogin(UserLoginDTO userLoginDTO) {String 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);JSONObject jsonObject = JSON.parseObject(json);String openid = jsonObject.getString("openid");return openid;}
}
7 Mapper层

创建UserMapper接口:

package com.sky.mapper;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>
8 编写拦截器

**编写拦截器JwtTokenUserInterceptor:**统一拦截用户端发送的请求并进行jwt校验

package com.sky.interceptor;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 {//判断当前拦截到的是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());log.info("当前用户的id:", userId);BaseContext.setCurrentId(userId);//3、通过,放行return true;} catch (Exception ex) {//4、不通过,响应401状态码response.setStatus(401);return false;}}
}

在WebMvcConfiguration配置类中注册拦截器:

	@Autowiredprivate JwtTokenUserInterceptor jwtTokenUserInterceptor;/*** 注册自定义拦截器* @param registry*/protected void addInterceptors(InterceptorRegistry registry) {log.info("开始注册自定义拦截器...");//.........registry.addInterceptor(jwtTokenUserInterceptor).addPathPatterns("/user/**").excludePathPatterns("/user/user/login").excludePathPatterns("/user/shop/status");}

http://www.mrgr.cn/news/66673.html

相关文章:

  • GenAI 生态系统现状:不止大语言模型和向量数据库
  • 【数据分享】2024年我国省市县三级的生活服务设施数量(46类设施/Excel/Shp格式)
  • Linux系统编程——线程概述、线程控制和线程私有数据
  • stm32学习4
  • k8s 查看cpu使用率最高的pod
  • 第二十八章 Vue之自定义指令
  • 【大模型LLM面试合集】大语言模型架构_llama3
  • ONLYOFFICE 8.2深度体验:高效协作与卓越性能的完美融合
  • idea 配置自动导入设置
  • Mysql,使用Mysqlbinlog,解析binlog日志
  • Docker学习—Docker的安装与使用
  • css 文字一行没有放满不进行换行
  • 【JAVA高级编程-JavaWeb】作业5
  • 【GESP】C++一级真题练习(202306)luogu-B3839,累计相加
  • 数据结构与算法——Java实现 54.力扣1008题——前序遍历构造二叉搜索树
  • 【Windows】让你的磁盘更健康!Windows `chkdsk`命令使用全指南
  • 【算法】【优选算法】滑动窗口(上)
  • java: 无法访问org.springframework.web.bind.annotation.RequestMapping
  • Centos Linux 7 搭建邮件服务器(postfix + dovecot)
  • 【JavaEE】认识进程
  • C#-MemoryMarshal
  • (十三)JavaWeb后端开发——MySQL2
  • 微控制器(MCU)如何运行存储在Flash的程序???
  • 基于python构造电影neo4j知识图谱
  • MongoDB基础介绍以及从0~1语法介绍
  • WEB:如何优化大数据菜单展示的攻略指南