微信登录功能开发

1. 官方文档

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

流程图:

步骤分析:

  1. 小程序端,调用wx.login()获取code,就是授权码。

  2. 小程序端,调用wx.request()发送请求并携带code,请求开发者服务器(自己编写的后端服务)。

  3. 开发者服务端,通过HttpClient向微信接口服务发送请求,并携带appId+appsecret+code三个参数。

  4. 开发者服务端,接收微信接口服务返回的数据,session_key+opendId等。opendId是微信用户的唯一标识。

  5. 开发者服务端,自定义登录态,生成令牌(token)和openid等数据返回给小程序端,方便后绪请求身份校验。

  6. 小程序端,收到自定义登录态,存储storage。

  7. 小程序端,后绪通过wx.request()发起业务请求时,携带token。

  8. 开发者服务端,收到请求后,通过携带的token,解析当前登录用户的id。

  9. 开发者服务端,身份校验通过后,继续相关的业务逻辑处理,最终返回业务数据。

2. 需求分析和设计

2.1 需求分析

用户进入到小程序的时候,首先需要微信授权登录。获取当前微信用户的相关信息,比如昵称、头像等,这样才能够进入到小程序进行其他操作。是基于微信登录来实现小程序的登录功能,没有采用传统账户密码登录的方式。若第一次使用小程序,就是一个新用户,需要把这个新的用户保存到数据库当中完成自动注册。

业务规则:

  • 基于微信登录实现小程序的登录功能

  • 如果是新用户需要自动完成注册

2.2 表设计

当用户第一次使用小程序时,会完成自动注册,把用户信息存储到user表中。

说明:

手机号字段比较特殊,个人身份注册的小程序没有权限获取到微信用户的手机号。如果是以企业的资质注册的小程序就能够拿到微信用户的手机号。

2.3 接口设计

通过微信登录的流程,如果要完成微信登录的话,最终就要获得微信用户的openid。在小程序端获取授权码后,向后端服务发送请求,并携带授权码,这样后端服务在收到授权码后,就可以去请求微信接口服务。最终,后端向小程序返回openid和token等数据。

基于上述的登录流程,就可以设计出该接口的请求参数返回数据

请求参数:微信用户授权码

返回数据:openid、token

3. 代码开发

重点介绍一下,Service层实现类实现获取微信用户的openid和微信登录功能; 以及拦截器中统一拦截用户端发送的请求并进行jwt校验。

3.1 Service层实现类

实现获取微信用户的openid和微信登录功能。

@Service  // 标记该类为Spring服务层的Bean
@Slf4j    // 引入Lombok的日志记录注解,可以直接使用log对象进行日志记录
public class UserServiceImpl implements UserService {

    // 微信服务接口地址,用于获取微信用户的openid
    public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";

    @Autowired  // 自动注入WeChatProperties配置类,用于获取微信应用的appid和secret等配置信息
    private WeChatProperties weChatProperties;

    @Autowired  // 自动注入UserMapper,用于执行数据库操作
    private UserMapper userMapper;

    /**
     * 微信登录方法
     * @param userLoginDTO 登录请求参数对象,其中包含了微信授权码code
     * @return User 返回用户对象
     */
    public User wxLogin(UserLoginDTO userLoginDTO) {
        // 调用getOpenid方法,通过微信接口获取用户的openid
        String openid = getOpenid(userLoginDTO.getCode());

        // 判断openid是否为空,如果为空则登录失败,抛出业务异常
        if (openid == null) {
            throw new LoginFailedException(MessageConstant.LOGIN_FAILED);  // 抛出登录失败异常
        }

        // 通过openid查询用户信息,判断当前用户是否为新用户
        User user = userMapper.getByOpenid(openid);

        // 如果是新用户,自动完成注册
        if (user == null) {
            user = User.builder()  // 使用User的构建器模式创建新用户对象
                    .openid(openid)  // 设置openid
                    .createTime(LocalDateTime.now())  // 设置注册时间
                    .build();
            userMapper.insert(user);  // 将新用户信息插入数据库
        }

        // 返回用户对象(已存在用户或新注册用户)
        return user;
    }

    /**
     * 调用微信接口服务,获取微信用户的openid
     * @param code 用户登录凭证code
     * @return String 返回微信用户的openid
     */
    private String getOpenid(String code) {
        // 调用微信接口服务,构建请求参数
        Map<String, String> map = new HashMap<>();
        map.put("appid", weChatProperties.getAppid());       // 设置微信应用的appid
        map.put("secret", weChatProperties.getSecret());     // 设置微信应用的secret
        map.put("js_code", code);                            // 设置登录凭证code
        map.put("grant_type", "authorization_code");         // 设置授权类型为授权码模式

        // 通过HttpClientUtil的doGet方法发送GET请求,获取微信返回的JSON字符串
        String json = HttpClientUtil.doGet(WX_LOGIN, map);

        // 解析JSON字符串,将其转换为JSONObject对象
        JSONObject jsonObject = JSON.parseObject(json);

        // 从JSON对象中获取openid,若不存在则返回null
        String openid = jsonObject.getString("openid");
        return openid;
    }
}

3.2 编写拦截器

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

3.2.1 自定义拦截器

/**
 * jwt令牌校验的拦截器
 */
@Component // 标注该类为Spring的组件,将其作为拦截器Bean注入到Spring上下文中
@Slf4j     // 引入Lombok的日志记录注解,可以直接使用log对象进行日志记录
public class JwtTokenUserInterceptor implements HandlerInterceptor {

    @Autowired  // 自动注入JwtProperties配置类,用于获取JWT的配置属性(例如密钥、token名称等)
    private JwtProperties jwtProperties;

    /**
     * 校验JWT令牌
     *
     * @param request  HTTP请求对象,包含请求头、参数等信息
     * @param response HTTP响应对象,用于设置响应状态和返回信息
     * @param handler  处理器(通常是Controller的方法),表示当前请求将被哪个方法处理
     * @return boolean 返回true表示放行,返回false表示拦截
     * @throws Exception 抛出可能的异常
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 判断当前拦截到的是Controller的方法还是其他静态资源
        if (!(handler instanceof HandlerMethod)) {
            // 如果拦截到的不是Controller方法(可能是静态资源文件),直接放行
            return true;
        }

        // 1. 从请求头中获取JWT令牌,名称由jwtProperties配置中定义
        String token = request.getHeader(jwtProperties.getUserTokenName());

        // 2. 校验令牌
        try {
            // 记录日志信息,显示即将校验的JWT令牌
            log.info("jwt校验:{}", token);

            // 使用JwtUtil工具类解析JWT令牌,验证其合法性并提取载荷信息
            Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);

            // 从解析后的Claims对象中获取用户ID
            Long userId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());

            // 记录日志信息,显示当前用户的ID
            log.info("当前用户的id:{}", userId);

            // 将当前用户ID存储到上下文中,供后续业务逻辑使用
            BaseContext.setCurrentId(userId);

            // 3. 令牌校验通过,放行请求
            return true;
        } catch (Exception ex) {
            // 4. 如果校验失败,捕获异常并设置响应状态码为401(未授权)
            response.setStatus(401);
            return false;
        }
    }
}

3.2.2 注册拦截器

需要在WebMvcConfiguration配置类中注册拦截器

@Autowired
private JwtTokenUserInterceptor jwtTokenUserInterceptor; // 自动注入自定义的JWT令牌校验拦截器,用于对特定请求路径进行JWT验证

/**
 * 注册自定义拦截器
 *
 * @param registry 拦截器注册对象,用于配置拦截器的路径规则
 */
protected void addInterceptors(InterceptorRegistry registry) {
    log.info("开始注册自定义拦截器..."); // 记录日志,表示开始注册自定义拦截器

    // 使用registry对象将自定义拦截器注册到拦截链中
    registry.addInterceptor(jwtTokenUserInterceptor) // 添加自定义的JWT令牌校验拦截器
            .addPathPatterns("/user/**") // 指定拦截的路径模式,所有以/user/开头的请求路径都会被拦截器拦截
            .excludePathPatterns("/user/user/login") // 排除某些特定路径,不拦截登录接口(通常不需要JWT验证)
            .excludePathPatterns("/user/shop/status"); // 排除路径,不拦截/shop/status接口

    // 上述路径配置表示,JWT令牌校验拦截器将会对所有以"/user/"开头的路径进行拦截,
    // 但会忽略"/user/user/login"和"/user/shop/status"这两个路径,不对它们执行JWT验证。
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cyt涛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值