史上最详细微信小程序授权登录与后端SprIngBoot交互操作说明!!!附源代码

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 史上最详细微信小程序授权登录与后端SprIngBoot交互操作说明!!!附源代码

你好,我是博主宁在春,一起学习吧!!!

写这篇文章的原因,主要是因为最近在写毕业设计,用到了小程序,这中间曲曲折折,一言难尽啊。毕业设计真的让人麻脑阔😂。唉

最近在持续更新,每天推送完代码,遇到的问题都记下来,希望对大家也能有所帮助。

在网上找了很多很多,看了不下几十篇,说实话,有些给出了核心代码,添上一个微信官方的那张流程图就结束了,会的人一下就懂了。但是说实话,真的不适合入门学者,浪费很多时间都不一定能解决问题,将代码复制完不是少这就是少那,或者就是不齐,不知道看到这篇文章的你有没有遇到过这样的问题。


所以我自己将踩坑的经历写下来了,希望能够帮助到大家,开源进步,交流进步,一起学习!!!

挺多小伙伴遇到过这个问题,如果大家对文章内容存有疑惑或者实现不了这个小demo亦或者文章中有什么错误,可以直接评论、留言或可以直接发问题到邮箱:nzc_wyh@163.com

希望能够帮助到大家(当然,如果我可以做到的话 🦮)

看到都会尽快回复大家,谢谢大家,一起努力

微信官方文档

一、微信小程序官方登录流程图

1704460231242.jpg

个人理解

  1. 调用wx.login() 获取code,这个code的作用是实现微信临时登录的url中的一个非常重要的参数。
  1. 把获取到的code传给我们自己的SpringBoot后端,由我们后端向微信接口服务发送请求。
String url = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";
String replaceUrl = url.replace("{0}", appid).replace("{1}", secret).replace("{2}", code);
String res = HttpUtil.get(replaceUrl);//后面有代码的,莫急
  • appid:应用ID,secret:应用密钥,js_code:前台传给我们的code
  • secret获取方式:
  1. 进入微信公众平台
  2. 左侧菜单选择【开发管理】
  3. 右侧tab选择【开发设置】
  4. AppSecret栏右侧点击重置会弹出一个二维码,需要开发者扫描二维码才可以重置AppSecret。出现AppSecret后点击复制,并保存你的AppSecret。
  5. 没保存就只能重新生成了。
  1. 后端发送请求后获取到的返回信息:
{"session_key":"G59Evf\/Em54X6WsFsrpA1g==","openid":"o2ttv5L2yufc4-VoSPhTyUnToY60"}
  1. 按照官方文档所讲:自定义登录态与openid和session_key关联,有很多方式可以实现的,如:
  • 第一种方式:我们可以将openid和session_key存进redis中,前端来访问的时候带上就能够访问了。
  • 第二种方式:利用jwt方式生成Token返回给前端,让前端下次请求时能够带上,就能允许他们访问了。
  1. 前端将token存入storage
  2. 前端在wx.request()发起业务请求携带自定义登录态,后端进行请求头的检查就可以了。
  3. 后端返回业务数据

上述就是官方的方式,但是在现在的时代,数据是非常重要的,不可能说不将用户数据持久化的,所以这个流程会稍稍多一些操作的。

二、个人实现登录流程图

1704460276506.jpg

三、小程序端

先说一下,这里只是测试的Demo,是分开测试的。

我本地没有微信的编程坏境是拿小伙伴的测试的。

2.1、调用wx.login()

wx.login({
    success:function(res){
        if(res.code){
            console.log(res.code);
        }
    }
})

就是这样的一个字符串:

1704460309380.jpg

我们将这个返回的code,先保存起来,稍后我们在后端测试中会用上的。

2.2、调用getUserInfo()

<button open-type="getUserInfo" bindgetuserinfo="userInfoHandler"> Click me <tton>
// 微信授权
wx.getUserInfo({
    success: function(res) {
    console.log(res);
    }
})

打印出来是这样的一些数据。

1704460276506.jpg

我们需要保存的是

  1. encrytedData:包括敏感数据在内的完整用户信息的加密数据(即可以通过反解密,获取出用户数据),详见 用户数据的签名验证和加解密
  2. iv:加密算法的初始向量,详见 用户数据的签名验证和加解密

至此,我们需要在前台获取的数据,已经结束了,接下来就用我们获取到的数据一起来看后端吧!!!


四、SpringBoot后端

为了将代码精简,我这边只是把获取到的数据输出出来,并未真实的保存到数据中。业务操作用注释在文中展示。

项目结构:

1704460338364.jpg

3.1、相关jar

创建一个SpringBoot项目,或者maven项目都可以。

<parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.5.2</version>
        <relativePath/>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.72</version>
        </dependency>
        <!--使用hutool中对http封装工具类 调用 HTTP 请求-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.6.5</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

3.2、yml配置文件

server:
  port: 8081
spring:
  application:
    name: springboot-weixin
  redis:
    database: 0
    port: 6379
    host: localhost
    password:
weixin:
  appid: 'appid'
  secret: '应用密钥'

3.3、公共类

就是一常量

public class RedisKey {
    public static final String WX_SESSION_ID = "wx_session_id";
}
/**
 * 统一响应结果集
 * @author crush
 */
@Data
public class Result<T> {
    //操作代码
    Integer code;
    //提示信息
    String message;
    //结果数据
    T data;
    public Result() {
    }
    public Result(ResultCode resultCode) {
        this.code = resultCode.code();
        this.message = resultCode.message();
    }
    public Result(ResultCode resultCode, T data) {
        this.code = resultCode.code();
        this.message = resultCode.message();
        this.data = data;
    }
    public Result(String message) {
        this.message = message;
    }
    public static Result SUCCESS() {
        return new Result(ResultCode.SUCCESS);
    }
    public static <T> Result SUCCESS(T data) {
        return new Result(ResultCode.SUCCESS, data);
    }
    public static Result FAIL() {
        return new Result(ResultCode.FAIL);
    }
    public static Result FAIL(String message) {
        return new Result(message);
    }
}
/**
 * 通用响应状态
 */
public enum ResultCode {
    /* 成功状态码 */
    SUCCESS(0, "操作成功!"),
    /* 错误状态码 */
    FAIL(-1, "操作失败!"),
    /* 参数错误:10001-19999 */
    PARAM_IS_INVALID(10001, "参数无效"),
    PARAM_IS_BLANK(10002, "参数为空"),
    PARAM_TYPE_BIND_ERROR(10003, "参数格式错误"),
    PARAM_NOT_COMPLETE(10004, "参数缺失"),
    /* 用户错误:20001-29999*/
    USER_NOT_LOGGED_IN(20001, "用户未登录,请先登录"),
    USER_LOGIN_ERROR(20002, "账号不存在或密码错误"),
    USER_ACCOUNT_FORBIDDEN(20003, "账号已被禁用"),
    USER_NOT_EXIST(20004, "用户不存在"),
    USER_HAS_EXISTED(20005, "用户已存在"),
    /* 系统错误:40001-49999 */
    FILE_MAX_SIZE_OVERFLOW(40003, "上传尺寸过大"),
    FILE_ACCEPT_NOT_SUPPORT(40004, "上传文件格式不支持"),
    /* 数据错误:50001-599999 */
    RESULT_DATA_NONE(50001, "数据未找到"),
    DATA_IS_WRONG(50002, "数据有误"),
    DATA_ALREADY_EXISTED(50003, "数据已存在"),
    AUTH_CODE_ERROR(50004, "验证码错误"),
    /* 权限错误:70001-79999 */
    PERMISSION_UNAUTHENTICATED(70001, "此操作需要登陆系统!"),
    PERMISSION_UNAUTHORISE(70002, "权限不足,无权操作!"),
    PERMISSION_EXPIRE(70003, "登录状态过期!"),
    PERMISSION_TOKEN_EXPIRED(70004, "token已过期"),
    PERMISSION_LIMIT(70005, "访问次数受限制"),
    PERMISSION_TOKEN_INVALID(70006, "无效token"),
    PERMISSION_SIGNATURE_ERROR(70007, "签名失败"),
    //操作代码
    int code;
    //提示信息
    String message;
    ResultCode(int code, String message) {
        this.code = code;
        this.message = message;
    }
    public int code() {
        return code;
    }
    public String message() {
        return message;
    }
    public void setCode(int code) {
        this.code = code;
    }
    public void setMessage(String message) {
        this.message = message;
    }
}
package com.crush.mybatisplus.config;
import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
/**
 * redis 配置类
 *
 * @author crush
 */
@EnableCaching
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        //key hasKey的序列化
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(redisConnectionFactory);
        return stringRedisTemplate;
    }
}

3.4、Controller

import com.crush.weixin.commons.Result;
import com.crush.weixin.entity.WXAuth;
import com.crush.weixin.service.IWeixinService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
 *
 * @author crush
 * @since 2021-09-14
 */
@Slf4j
@RestController
@RequestMapping("/weixin")
public class WeixinController {
    @Autowired
    IWeixinService weixinService;
    //这个就是那个使用传code进来的接口
    @GetMapping("/sessionId/{code}")
    public String getSessionId(@PathVariable("code") String code){
        return  weixinService.getSessionId(code);
    }
    @PostMapping("/authLogin")
    public Result authLogin(@RequestBody WXAuth wxAuth) {
        Result result = weixinService.authLogin(wxAuth);
        log.info("{}",result);
        return result;
    }
}

3.5、service层

public interface IWeixinService extends IService<Weixin> {
    String getSessionId(String code);
    Result authLogin(WXAuth wxAuth);
}
import cn.hutool.core.lang.UUID;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.crush.weixin.commons.RedisKey;
import com.crush.weixin.commons.Result;
import com.crush.weixin.entity.WXAuth;
import com.crush.weixin.entity.Weixin;
import com.crush.weixin.entity.WxUserInfo;
import com.crush.weixin.mapper.WeixinMapper;
import com.crush.weixin.service.IWeixinService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
/**
 * @author crush
 * @since 2021-09-14
 */
@Slf4j
@Service
public class WeixinServiceImpl extends ServiceImpl<WeixinMapper, Weixin> implements IWeixinService {
    @Value("${weixin.appid}")
    private String appid;
    @Value("${weixin.secret}")
    private String secret;
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    WxService wxService;
    @Override
    public String getSessionId(String code) {
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";
        String replaceUrl = url.replace("{0}", appid).replace("{1}", secret).replace("{2}", code);
        String res = HttpUtil.get(replaceUrl);
        String s = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(RedisKey.WX_SESSION_ID + s, res);
        return s;
    }
    @Override
    public Result authLogin(WXAuth wxAuth) {
        try {
            String wxRes = wxService.wxDecrypt(wxAuth.getEncryptedData(), wxAuth.getSessionId(), wxAuth.getIv());
            log.info("用户信息:"+wxRes);
          //用户信息:{"openId":"o2ttv5L2yufc4-sVoSPhTyUnToY60","nickName":"juana","gender":2,"language":"zh_CN","city":"Changsha","province":"Hunan","country":"China","avatarUrl":"头像链接","watermark":{"timestamp":1631617387,"appid":"应用id"}}
            WxUserInfo wxUserInfo = JSON.parseObject(wxRes,WxUserInfo.class);
            // 业务操作:你可以在这里利用数据 对数据库进行查询, 如果数据库中没有这个数据,就添加进去,即实现微信账号注册
            // 如果是已经注册过的,就利用数据,生成jwt 返回token,实现登录状态
            return Result.SUCCESS(wxUserInfo);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.FAIL();
    }
}

2121年11月27号:应该是微信接口更新了,在此处通过解密获取到的信息中,并不包含openId啦,得自己去拿到才可以。

牵扯到用户信息解密的方法,想要了解,可以去微信官方文档中进行了解,我对此没有深入。

import cn.hutool.core.codec.Base64;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.crush.weixin.commons.RedisKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Random;
@Slf4j
@Component
public class WxService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    public String wxDecrypt(String encryptedData, String sessionId, String vi) throws Exception {
        // 开始解密
        String json =  redisTemplate.opsForValue().get(RedisKey.WX_SESSION_ID + sessionId);
        log.info("之前存储在redis中的信息:"+json);
        //之前存储在redis中的信息:{"session_key":"G59Evf\/Em54X6WsFsrpA1g==","openid":"o2ttv5L2yufc4-VoSPhTyUnToY60"}
        JSONObject jsonObject = JSON.parseObject(json);
        String sessionKey = (String) jsonObject.get("session_key");
        byte[] encData = cn.hutool.core.codec.Base64.decode(encryptedData);
        byte[] iv = cn.hutool.core.codec.Base64.decode(vi);
        byte[] key = Base64.decode(sessionKey);
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        return new String(cipher.doFinal(encData), "UTF-8");
    }
}

最后写个启动类就可以开始测试了。

@SpringBootApplication
public class SpringBootWeixin {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootWeixin.class);
    }
}

五、测试

写完后端,接下来,可以利用我们之前收集的那些小程序中获取到的数据啦。

1、先发送第一个请求:

code:就是之前我们获取到的数据。

http://localhost:8081/weixin/sessionId/{code}

会返回一个sessionId回来,在第二个请求中需要携带。

2、再发送第二个请求

http://localhost:8081/weixin/authLogin

请求方式:post

data:json格式数据

{
    "encryptedData":"sYiwcAM73Ci2EB3y9+C6.....",
    "iv": "xZGOj6RwaOS==",
    "sessionId":"我们上一个请求获取到sessionId"
}

请求成功是下面这样的。

1704460359120.jpg

我们把我们需要的存储到数据库持久化即可啦。

六、自言自语

这只是一个小demo,在使用中大都会结合security安全框架和Jwt一起使用,周末吧,周末比较有空,有空就会更新出来。


你好,我是博主宁在春,有问题可以留言评论或者私信我,大家一起交流学习!

不过都看到这里啦,点个赞吧👩‍💻

源码

SpringBoot-weixin-gitee

SpringBoot-weixin-github

补充:

如果拿github查看项目的话,可以在google安装一个扩展程序。看代码会更加的方便

1704460378482.jpg

安装之后,在github上查看项目,会在左侧多一个下面这样的目录结构。比较方便

1704460381253.jpg

今天的文章就到了这里啦,下次再见!!!

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
3月前
|
SQL Java 测试技术
在Spring boot中 使用JWT和过滤器实现登录认证
在Spring boot中 使用JWT和过滤器实现登录认证
246 0
|
3月前
|
小程序 JavaScript Java
微信小程序的后端开发需要使用什么语言?
【8月更文挑战第22天】微信小程序的后端开发需要使用什么语言?
738 65
|
22天前
|
小程序 前端开发 算法
|
1月前
|
Java API Spring
springBoot:注解&封装类&异常类&登录实现类 (八)
本文介绍了Spring Boot项目中的一些关键代码片段,包括使用`@PathVariable`绑定路径参数、创建封装类Result和异常处理类GlobalException、定义常量接口Constants、自定义异常ServiceException以及实现用户登录功能。通过这些代码,展示了如何构建RESTful API,处理请求参数,统一返回结果格式,以及全局异常处理等核心功能。
|
1月前
|
前端开发 Java
学习SpringMVC,建立连接,请求,响应 SpringBoot初学,如何前后端交互(后端版)?最简单的能通过网址访问的后端服务器代码举例
文章介绍了如何使用SpringBoot创建简单的后端服务器来处理HTTP请求,包括建立连接、编写Controller处理请求,并返回响应给前端或网址。
56 0
学习SpringMVC,建立连接,请求,响应 SpringBoot初学,如何前后端交互(后端版)?最简单的能通过网址访问的后端服务器代码举例
|
3月前
|
移动开发 安全 JavaScript
SpringBoot接入微信JSSDK,看这篇妥妥的
这篇教程详细介绍了如何在Spring Boot项目中接入微信JSSDK,实现H5页面的自定义分享和调用相册选取图片等功能。文章首先通过对比理想与现实的分享效果,引出了接入微信JSSDK的必要性。接着,作者提供了GitHub和Gitee上的项目源码链接,并逐步讲解了整个接入过程的关键步骤,包括配置文件、主要类和方法的实现细节,以及必要的微信公众号设置。此外,还特别强调了几个常见问题及其解决方案,如域名绑定、IP白名单设置和签名验证等。最后,通过实际测试验证了功能的正确性。适合初学者快速上手微信JSSDK接入。
63 8
SpringBoot接入微信JSSDK,看这篇妥妥的
|
3月前
|
小程序 前端开发 Java
SpringBoot+uniapp+uview打造H5+小程序+APP入门学习的聊天小项目
JavaDog Chat v1.0.0 是一款基于 SpringBoot、MybatisPlus 和 uniapp 的简易聊天软件,兼容 H5、小程序和 APP,提供丰富的注释和简洁代码,适合初学者。主要功能包括登录注册、消息发送、好友管理及群组交流。
108 0
SpringBoot+uniapp+uview打造H5+小程序+APP入门学习的聊天小项目
|
2月前
|
小程序 PHP
微信小程序给 thinkphp后端发送请求出现错误 Wrong number of segments 问题的解决 【踩坑记录】
本文记录了微信小程序向ThinkPHP后端发送请求时出现"Wrong number of segments"错误的解决方法。问题原因是小程序请求header中的token变量名写错,导致token未正确传递至后端。作者提供了详细的检查步骤和建议,包括验证URL路径、参数规范和路由配置的匹配,以确保请求能正确发送和处理。
|
3月前
|
小程序 前端开发 JavaScript
【项目实战】SpringBoot+uniapp+uview2打造一个企业黑红名单吐槽小程序
【避坑宝】是一款企业黑红名单吐槽小程序,旨在帮助打工人群体辨别企业优劣。该平台采用SpringBoot+MybatisPlus+uniapp+uview2等技术栈构建,具备丰富的注释与简洁的代码结构,非常适合实战练习与学习。通过小程序搜索“避坑宝”即可体验。
104 0
【项目实战】SpringBoot+uniapp+uview2打造一个企业黑红名单吐槽小程序
|
3月前
|
小程序 JavaScript Java
微信小程序+SpringBoot接入后台服务,接口数据来自后端
这篇文章介绍了如何将微信小程序与SpringBoot后端服务进行数据交互,包括后端接口的编写、小程序获取接口数据的方法,以及数据在小程序中的展示。同时,还涉及到了使用Vue搭建后台管理系统,方便数据的查看和管理。
微信小程序+SpringBoot接入后台服务,接口数据来自后端
下一篇
无影云桌面