

基于微信登录实现小程序的登录
如果是新用户需要自动完成注册
老板开网吧前,得先想清楚:我这个网吧到底要提供什么服务?给谁用?
在苍穹外卖中,主要有两类人:
把业务流程串起来看:
顾客浏览菜单 → 加购物车 → 下单 → 支付 → 商家接单 → 制作 → 配送/自取
每一步背后,都需要系统提供支持。
想清楚了要做什么,接下来就是具体怎么实现。接口设计就是定义“前后端之间怎么对话”。
还是用“点餐”这个核心功能来举例,看看从需求到接口是怎么一步步落地的。
用户打开小程序,看到菜品列表,点击“加入购物车”,然后去结算,下单,最后支付。
接口文档通常包含以下要素:
/user/cart/add)
接口一:获取菜品列表
/user/dish/list
categoryId(分类ID,比如川菜的ID是101)
接口二:加入购物车
/user/shoppingCart/add
dishId(菜品ID)、dishFlavor(口味,比如“微辣”)
接口三:提交订单
/user/order/submit
amount(总价)、addressId(收货地址)、payMethod(支付方式)、remark(备注,比如“不要香菜”)
接口四:商家接单
/admin/order/confirm
orderId(订单ID)
为了保证画出来的图纸大家都能看懂,苍穹外卖通常会遵循一些规范:
/orders(查列表)
/orders(新建)
/orders/{id}(查某个具体订单)
/orders/{id}(修改某个具体订单)
/user/:给小程序用户用的接口。
/admin/:给后台管理员用的接口。
/user),员工走后门(/admin)。
json { "code": 1, // 1成功,0失败 "msg": "操作成功", // 提示信息 "data": { ... } // 真正的数据 }
token(刚才说的网吧会员卡),用来识别身份。
在苍穹外卖中,当我们看到类似这样的结构时,就能对应起来了:
需求:用户登录 -> 接口:POST /user/user/login (参数:code)
需求:管理员新增菜品 -> 接口:POST /admin/dish (参数:json格式的菜品信息,头:token)
你走到前台,对网管说:“老板,开台机子。” 网管不会直接给你开机,他会先问你:“身份证带了吗?” 你把身份证递给他。 对应技术动作:
网管拿到你的身份证后,他不会凭肉眼看看就相信你,他会把身份证号输入到 公安内部系统 里进行查询。 这个公安系统里记录了你的真实姓名、年龄、是否有案底等信息。 公安系统验证通过后,会给网管返回一个确认信息:“此人真实有效,可以上网。”
对应技术动作:
code,加上自己的 AppID 和 AppSecret(相当于派出所的接入密码),去请求微信服务器。
openid(用户的唯一身份证号)和 session_key(会话密钥)。
公安系统确认你是好公民后,网管不会每次都让你把身份证拿出来刷,那样太慢了。 所以,网管会在他的网吧系统里,给你建立一个档案(如果第一次来的话),然后发给你一张 “网吧临时会员卡”。 这张卡上有你的信息(比如卡号 9527),但最重要的是,这张卡只有在这家网吧(苍穹外卖)才有效。下次你来,直接刷卡就行了,不用再出示身份证。 对应技术动作:
openid,如果没找到,就在 user 表里新注册一条记录。
user_id 生成一个自定义的登录凭证,比如 jwt_token(JSON Web Token)。这个 token 就相当于网吧的会员卡。
token 返回给小程序。
拿到会员卡后,你走到电脑前,刷一下卡(或者输入卡号),电脑解锁,你就可以点炒饭、点可乐了。 对应技术动作:
token。
token 是不是真的、有没有过期。验证通过,就知道你是哪个用户了(user_id 是多少),然后才给你处理具体的业务逻辑(比如把订单关联到你的ID上)。
code(介绍信)给后端,后端拿着 code 去微信官方那里换 openid(用户真实身份)。这样 openid 永远只保存在后端,小程序端是拿不到的,保证了安全。
LoginController 接收小程序传来的 code。
LoginService 调用 WeChatUtil 的工具类,通过 code 请求微信的 jscode2session 接口,拿到 openid。
openid 查询 user 表,如果没有就创建新用户。
JWT Token。
token 返回给小程序,小程序存起来,后续请求都带上。
这样一来,小程序用户就在苍穹外卖系统里完成了一次丝滑的匿名转实名的登录过程。
微信登录在小程序里扮演什么角色? 微信登录,就是这个小程序平板用来确认你是谁的一种方式。 没有微信登录会怎样? 你坐在餐桌前,点了一堆菜,然后平板问:"请先登录"。 你说:"怎么登?" 平板说:"输入用户名密码,或者手机号注册一下。" 你心想:"吃个饭还要注册?太麻烦了,换一家吧。" 这就是没有微信登录的问题——用户流失率会非常高。 有了微信登录会怎样? 你打开平板,上面只有一个按钮:"微信一键登录"。 你点一下,屏幕跳转到微信授权页面,你点"允许",咻的一下,你就登录成功了,直接开始点菜。 所以,微信登录 = 让用户不用注册,直接使用微信身份进来的"快速通道"。
还是用刚才点餐平板的场景:
步骤 | 小程序在做什么 | 微信登录在做什么 | 后端在做什么 |
|---|---|---|---|
1 | 你打开平板,小程序显示一个"微信登录"按钮 | — | 等着接收请求 |
2 | 你点按钮,小程序调用 wx.login() 接口,拿到一个临时的 code | 微信告诉你:"这是这个用户的临时身份证" | — |
3 | 小程序把这个 code 发给自己家的后端 | — | 后端拿着 code 去问微信:"这个 code 对应谁?" |
4 | — | 微信告诉后端:"这是 openid=xxx 的用户" | 后端收到 openid,在自己数据库里找或创建用户,生成一张"会员卡"(token) |
5 | 小程序收到后端返回的 token,把它存起来 | — | 后端把 token 返回给小程序 |
6 | 接下来你点菜、下单,小程序都在请求里带上这个 token | — | 后端看到 token,就知道你是谁,给你处理对应的业务 |
你可能会想:"那能不能直接让用户用微信登录,然后就用微信本身点餐?" 这就好比问:"能不能直接用身份证去点菜,不要点餐平板?" 不行,因为:
微信 = 操作系统/平台(像Windows) 小程序 = 运行在微信里的APP(像Word、Excel) 微信登录 = 小程序里用微信身份快速进入的功能 开发者的分工:
所以,在苍穹外卖项目中:
Cotroller:
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.GetMapping;
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 {
@Autowired
private UserService userService;
@Autowired
private JwtProperties jwtProperties;
/**
* 微信登录
* @param userLoginDTO
* @return
*/
@GetMapping("/login")
@ApiOperation("微信登录")
public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){
log.info("微信用户登录:{}",userLoginDTO);
//微信登录
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);
}
}Service:
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Autowired
private WeChatProperties weChatProperties;
@Autowired
private UserMapper userMapper;
//微信接口的地址
public static final String WX_LOGIN="https://api.weixin.qq.com/sns/jscode2session";
/**
* 微信登录
* @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 null;
}
private String getOpenid(String code) {
//调用微信接口的服务,获得当前用户的openid,使用前面学习的httpclient
Map<String, String> map=new HashMap<>();
//把接口文档需要的四个参数封装到map集合中
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;
}
}Mapper:
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 {
/**
* 查询是否为新用户
* @param openid
* @return
*/
@Select("select *from sky_take_out.user where openid=#{openid}")
User getByOpenid(String openid);
/**
* 插入数据,还要返回主键值要用到
* @param user
*/
void insert(User user);
}
结语:如果对你有帮助,请点赞,关注,收藏!