JSON Web Token (JWT): 理解与应用
JWT(JSON Web Token)是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间以JSON对象的形式安全地传输信息。JWT通常用于身份验证和授权目的,因为它可以使用JSON对象在各方之间安全地传输信息
官网地址:https://jwt.io/
0.介绍
通俗地说,JWT的本质就是一个字符串,它是将用户信息保存到一个Json字符串中,然后进行编码后得到一个JWT token,并且这个JWT token带有签名信息,接收后可以校验是否被篡改,所以可以用于在各方之间安全地将信息作为Json对象传输。
0.1.JWT的应用场景
身份验证 : 当用户成功登录时,服务器会生成一个JWT并将其发送给客户端。客户端在后续请求中将JWT附加到HTTP请求头中,以此来证明用户的身份。
授权 : JWT中可以包含用户的权限信息,这样服务器可以根据这些信息决定用户是否被允许访问某些资源。
信息传递 : 除了用户的身份和权限外,JWT还可以用来携带其他有用的信息,如用户的偏好设置等。
在 java 中 常与 Spring Security 框架配合使用
JWT的认证流程如下:
- 首先,前端通过Web表单将自己的用户名和密码发送到后端的接口,这个过程一般是一个POST请求。建议的方式是通过SSL加密的传输(HTTPS),从而避免敏感信息被嗅探
- 后端核对用户名和密码成功后,将包含用户信息的数据作为JWT的Payload,将其与JWT Header分别进行Base64编码拼接后签名,形成一个JWT Token,形成的JWT Token就是一个如同lll.zzz.xxx的字符串
- 后端将JWT Token字符串作为登录成功的结果返回给前端。前端可以将返回的结果保存在浏览器中,退出登录时删除保存的JWT Token即可
- 前端在每次请求时将JWT Token放入HTTP请求头中的Authorization属性中(解决XSS和XSRF问题)
- 后端检查前端传过来的JWT Token,验证其有效性,比如检查签名是否正确、是否过期、token的接收方是否是自己等等
- 验证通过后,后端解析出JWT Token中包含的用户信息,进行其他逻辑操作(一般是根据用户信息得到权限等),返回结果

0.2.优点
这种基于token的认证方式相比传统的session认证方式更节约服务器资源,并且对移动端和分布式更加友好。其优点如下:
-
支持跨域访问:cookie是无法跨域的,而token由于没有用到cookie(前提是将token放到请求头中),所以跨域后不会存在信息丢失问题
-
无状态:token机制在服务端不需要存储session信息,因为token自身包含了所有登录用户的信息,所以可以减轻服务端压力
-
更适用CDN:可以通过内容分发网络请求服务端的所有资料
-
更适用于移动端:当客户端是非浏览器平台时,cookie是不被支持的,此时采用token认证方式会简单很多
-
无需考虑CSRF:由于不再依赖cookie,所以采用token认证方式不会发生CSRF,所以也就无需考虑CSRF的防御
1.JWT的结构
JWT是一种自包含的令牌格式。JWT由三个部分组成:头部、载荷和签名。

1.1.头部 (Header)
头部通常包含两个部分:
typ: 表示该令牌的类型,通常是“JWT”。alg: 指定签名算法,例如 HMAC SHA-256 或 RSA。
头部通常以JSON格式书写,并经过Base64Url编码。
1.2.载荷 (Payload)
载荷包含了需要作为声明传输的信息。这些声明可以分为三类:
- 标准声明:由JWT规范定义的声明。
iss(issuer): 发行者。sub(subject): 主题,通常是指用户ID。aud(audience): 接收者,即令牌的预期受众。exp(expiration time): 过期时间。nbf(not before): 该时间之前不可使用。iat(issued at): 发行时间。jti(JWT ID): 一个唯一的标识符,用于防止重放攻击。
- 私有声明:由发行者和接收者约定的声明,例如用户的角色或权限等。
- 公共声明:虽然不是JWT规范的一部分,但可以在任何JWT中使用。
载荷也是经过Base64Url编码的。
1.3.签名 (Signature)
签名部分保证了JWT的完整性和安全性。签名通过将头部和载荷进行编码并使用指定的算法(如HMAC SHA-256 RSA或ECDSA)进行计算得到。签名确保了:
- 令牌没有被篡改。
- 令牌是由可信的一方发行的。
签名部分同样经过Base64Url编码。
1.4.JWS, JWK
JWS ,也就是JWT Signature,其结构就是在之前nonsecure JWT的基础上,在头部声明签名算法,并在最后添加上签名。创建签名,是保证jwt不能被他人随意篡改。我们通常使用的JWT一般都是JWS
为了完成签名,除了用到header信息和payload信息外,还需要算法的密钥,也就是secretKey。
加密的算法一般有2类:
- 对称加密:secretKey指加密密钥,可以生成签名与验签
- 非对称加密:secretKey指私钥,只用来生成签名,不能用来验签(验签用的是公钥)
JWT的密钥或者密钥对,一般统一称为JSON Web Key,也就是JWK
到目前为止,jwt的签名算法有三种:
- HMAC【哈希消息验证码(对称)】:HS256/HS384/HS512
- RSASSA【RSA签名算法(非对称)】 :(RS256/RS384/RS512)
- ECDSA【椭圆曲线数据签名算法(非对称)】 :(ES256/ES384/ES512)
2.实现与工具
2.1.jjwt (Java JWT)
GitHub 仓库地址:https://github.com/jwtk/jjwt
这是一个基于Java的库,用于在JVM和Android平台上创建和验证JSON Web Tokens (JWTs)和JSON Web Keys (JWKs)。
它支持多种JOSE工作组的RFC规范:
- RFC 7519: JSON Web Token (JWT)
- RFC 7515: JSON Web Signature (JWS)
- RFC 7516: JSON Web Encryption (JWE)
- 等等
2.1.1.导入Maven依赖
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.5</version>
</dependency>
2.1.2.测试代码
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.security.Key;
import java.util.Date;
import java.util.UUID;@RestController
public class JwtController {// 密钥 : 实际开发时 应该从 配置文件 / 持久化存储中 获取private String secret;// token时效:24小时public static final long EXPIRE = 1000 * 60 * 60 * 24;@RequestMapping("/create")public String createToken(){// 生成一个随机的密钥 IDString keyId = UUID.randomUUID().toString();// 密钥secret = keyId;// 创建一个 JWT 令牌String jwt = Jwts.builder()// 设置JWT头部参数,指定令牌类型为JWT.setHeaderParam("typ", "JWT")// 设置JWT头部参数,指定签名算法为HS256.setHeaderParam("alg", "HS256")// 设置JWT过期时间,当前时间戳加上设定的过期时长.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))// 设置主题,通常是用户身份标识.setSubject("admin")// 添加角色声明.claim("role", "admin")// 设置用户ID为1.claim("id", 1)// 设置用户昵称为"王小二".claim("nickname", "王小二")// 设置令牌签发时间.setIssuedAt(new Date())// 设置唯一标识符.setId( keyId )// 使用HS256算法和密钥签名.signWith(SignatureAlgorithm.HS256, secret.getBytes())// 将令牌压缩为紧凑形式的字符串.compact();return jwt;}@RequestMapping("/check")public void checkToken(String jwt) {System.out.println("jwt = " + jwt);// 创建一个安全的密钥Key secureKey = Keys.hmacShaKeyFor(secret.getBytes());try {// 使用Jwts.parserBuilder()方法构建一个解析器,该解析器使用secureKey作为签名密钥// 然后使用这个解析器解析jwt字符串,获取到Claims对象,即JWT的主体部分Claims claims = Jwts.parserBuilder().setSigningKey(secureKey).build().parseClaimsJws(jwt).getBody();// 打印解析后的 JWT 信息System.out.println("Subject: " + claims.getSubject());System.out.println("Role: " + claims.get("role", String.class));System.out.println("Issue Time: " + claims.getIssuedAt());System.out.println("JWT ID: " + claims.getId());System.out.println("user nickname: " + claims.get("nickname"));// 可以在这里添加更多逻辑来验证 JWT 的有效性,例如检查过期时间等if(claims.getExpiration()==null){System.out.println("过期时间不能为空 Expiration time cannot be null");}} catch (Exception e) {// 如果 JWT 无法验证,这里会捕获异常System.err.println("Invalid JWT: " + e.getMessage());}}
}
2.2.Nimbus
Nimbus JOSE + JWT 是一个非常强大的 Java 库,用于处理 JSON Web Signature (JWS), JSON Web Encryption (JWE), JSON Web Key (JWK), JSON Web Token (JWT) 和 OAuth 2.0 授权服务器。这个库是由 NimbusDS 开发的,并且广泛应用于身份验证和授权系统中。
2.2.1.导入依赖
首先,您需要在 Maven 项目中添加 json-jwt 的依赖:
<dependency><groupId>com.nimbusds</groupId><artifactId>nimbus-jose-jwt</artifactId><version>9.40</version></dependency>
2.2.2.测试代码
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.text.ParseException;
import java.util.Date;
import java.util.UUID;@RestController
public class JwtController {// 密钥 : 实际开发时 应该从 配置文件 / 持久化存储中 获取private String secret;@RequestMapping("/create")public String createToken() {try {// 生成一个随机的密钥 IDString keyId = UUID.randomUUID().toString();// 密钥secret = keyId;// 创建一个 HMAC 签名器MACSigner signer = new MACSigner(secret.getBytes());// 设置 JWT 的声明JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();builder.subject("admin");builder.claim("role", "admin");builder.issueTime(new Date());builder.jwtID(keyId);// 构建 JWT Claims SetJWTClaimsSet claimsSet = builder.build();// 创建一个空的 SignedJWT 对象SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256),claimsSet);// 签名 JWTsignedJWT.sign(signer);// 将 JWT 转换为紧凑形式String jwt = signedJWT.serialize();return jwt;} catch (JOSEException e) {e.printStackTrace();}return null;}@RequestMapping("/check")public void checkToken(String jwt) {try {System.out.println("jwt = " + jwt);// 解析 JWTSignedJWT signedJWT = SignedJWT.parse(jwt);// 验证 JWTMACVerifier verifier = new MACVerifier( secret.getBytes() );boolean isValid = signedJWT.verify(verifier);if (!isValid) {System.out.println("无效的 JWT 签名 Invalid JWT signature.");return;}// 获取 JWT ClaimsJWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();System.out.println("Subject: " + claimsSet.getSubject());System.out.println("Role: " + claimsSet.getStringClaim("role"));System.out.println("Issue Time: " + claimsSet.getIssueTime());System.out.println("JWT ID: " + claimsSet.getJWTID());} catch (JOSEException | ParseException e) {e.printStackTrace();}}/*** token 通过 header传递* 再通过 request 取出*/@RequestMapping("/checkHeader")public void checkHeaderToken(HttpServletRequest request) {try {String jwt = request.getHeader("Authorization");System.out.println("jwt header=> " + jwt);// 解析 JWTSignedJWT signedJWT = SignedJWT.parse(jwt);// 验证 JWTMACVerifier verifier = new MACVerifier( secret.getBytes() );boolean isValid = signedJWT.verify(verifier);if (!isValid) {System.out.println("无效的 JWT 签名 Invalid JWT signature.");return;}// 获取 JWT ClaimsJWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();System.out.println("Subject: " + claimsSet.getSubject());System.out.println("Role: " + claimsSet.getStringClaim("role"));System.out.println("Issue Time: " + claimsSet.getIssueTime());System.out.println("JWT ID: " + claimsSet.getJWTID());} catch (JOSEException | ParseException e) {e.printStackTrace();}}
}
2.2.3.请求测试
###
GET http://localhost:8080/create
生成token : eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcyMzQ1MDgyOCwiaWF0IjoxNzIzMzY0NDI4LCJqdGkiOiI5Yzc3N2YwZC03NDU5LTQ3MTUtYmVkNy1mNWViYzJiNmMwOTgifQ.Y_P7L4gehl0kJwxTnwUxX8Yy502qrCHQ0hkxod58ly8
###
GET http://localhost:8080/check?jwt=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcyMzQ1MDgyOCwiaWF0IjoxNzIzMzY0NDI4LCJqdGkiOiI5Yzc3N2YwZC03NDU5LTQ3MTUtYmVkNy1mNWViYzJiNmMwOTgifQ.Y_P7L4gehl0kJwxTnwUxX8Yy502qrCHQ0hkxod58ly8
idea 控制台:
Subject: admin
Role: admin
Issue Time: Sun Aug 11 16:20:28 CST 2024
JWT ID: 9c777f0d-7459-4715-bed7-f5ebc2b6c098
###
GET http://localhost:8080/checkHeader
Content-Type: application/json
Authorization: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcyMzQ1MDgyOCwiaWF0IjoxNzIzMzY0NDI4LCJqdGkiOiI5Yzc3N2YwZC03NDU5LTQ3MTUtYmVkNy1mNWViYzJiNmMwOTgifQ.Y_P7L4gehl0kJwxTnwUxX8Yy502qrCHQ0hkxod58ly8
idea 控制台:
Subject: admin
Role: admin
Issue Time: Sun Aug 11 16:20:28 CST 2024
JWT ID: 9c777f0d-7459-4715-bed7-f5ebc2b6c098
相关文章:
JSON Web Token (JWT): 理解与应用
JWT(JSON Web Token)是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间以JSON对象的形式安全地传输信息。JWT通常用于身份验证和授权目的,因为它可以使用JSON对象在各方…...
LeetCode面试题Day12|LC209 长度最小的子数组、LC30 串联所有单词的子串
题目一: 指路: . - 力扣(LeetCode)209 长度最小的子数组 思路与分析: 滑动窗口,目的在于降低算法的时间复杂度,每次只维护一定长度的数组而非原数组的全部元素。那么既然需要长度࿰…...
【开端】JAVA泛型类的使用
一、这是一个类 public class CommonVo<D extends CommonDao> implements Serializable { 我们来探讨一样 CommonVo<D extends CommonDao> 这个尖括号里到底能写啥。 首先这是一个泛型类型D ,D类继承了CommonDao,说明尖括号里只要放入一…...
mp3转换器免费有哪些?6个音频转换器助你一键转换各种音频
音乐如同生活的调味剂,让每一个平凡瞬间都跃动着不凡的旋律。 但有时候,当你想把这些歌曲放到你的设备上时,却发现格式不兼容,无法播放。 别担心!接下来,我们将介绍几款免费mp3转换工具,它们能…...
力扣爆刷第174天之TOP200五连刷136=140(最小k数、字典序、跳跃游戏)
力扣爆刷第174天之TOP200五连刷136140(最小k数、字典序、跳跃游戏) 文章目录 力扣爆刷第174天之TOP200五连刷136140(最小k数、字典序、跳跃游戏)一、LCR 159. 库存管理 III二、450. 删除二叉搜索树中的节点三、440. 字典序的第K小…...
蚁群算法原理与实战(Python、MATLAB、C++)
蚁群算法 1.蚁群算法来源 蚁群算法(Ant Colony Optimization,简称ACO)是一种模拟自然界中蚂蚁寻找食物路径行为的优化算法,主要用于解决组合优化问题。它的灵感来源于意大利学者Marco Dorigo在1992年提出的蚂蚁系统模型。 蚁群算…...
HTML静态网页成品作业(HTML+CSS)——非遗阜阳剪纸介绍设计制作(1个页面)
🎉不定期分享源码,关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 🏷️本套采用HTMLCSS,未使用Javacsript代码,共有1个页面。 二、作品演示 三、代…...
如何做萤石开放平台的物联网卡定向?
除了用萤石自带的4G卡外,我们也可以自己去电信、移动和联通办物联网卡连接萤石云平台。 1、说在前面 注意:以下流程必须全部走完,卡放在设备上才能连接到萤石云平台。 2、大致流程 登录官网→下载协议→盖章(包括骑缝章&#…...
ptrade排坑日记——定时任务执行后,文件权限会变化。
前言 今天要和大家分享的是使用ptrade的定时任务过后,执行的时候,文件权限会发生变化! 一、问题描述 定时任务执行后, /home/fly/data/fundamentals_daily/all.pickle、/home/fly/data/valuation_new/all.pickle 文件权限会从…...
TILs 评分:TCGA 肿瘤浸润淋巴细胞病理切片深度学习评分!图片下载与可视化
生信碱移 病理切片的TILs评分 TCGA 数据库是最大的肿瘤组学公开数据库之一。尽管如此,更多的研究往往仅局限于关注 TCGA 中各类肿瘤样本的上游组学信息或基本病理特征,而忽略了对样本数字化 H&E 病理染色图像的进一步应用。 ▲ TCGA中肿瘤样本的病…...
【运维】如何在浏览器中查看和管理 Cookie 信息?
如何在浏览器中查看和管理 Cookie 信息 引言 Cookie 是我们日常浏览网页时经常遇到的一个重要概念。它们用于存储用户的登录状态、偏好设置以及其他相关信息,帮助网站提供个性化的体验。然而,很多人并不清楚如何在浏览器中找到并查看这些 Cookie 信息。本文将带您了解如何在…...
Selenium实战:深度解析Python中嵌套Frame与iFrame的定位与切换技巧,解决Selenium定位不到的问题
在Web自动化测试中,处理网页中的Frame和iFrame是常见的挑战之一。这些元素在网页中扮演着承载独立HTML文档的角色,使得直接定位或操作其中的元素变得复杂。Python的Selenium库提供了强大的工具来应对这些挑战,本文将详细介绍如何使用Selenium…...
机器学习笔记六-朴素贝叶斯
朴素贝叶斯(Naive Bayes) 是一种基于贝叶斯定理的简单而强大的分类算法,特别适用于文本分类等高维数据集。它被称为“朴素”,因为它假设特征之间是相互独立的,这在现实中可能不完全成立,但这种假设在许多实…...
解决Vue3+Ts打包项目时会生成很多的map文件
正常打包会生成.js和.map文件 怎么去解决它呢? 正常来说我们会在vite.config.ts配置我们的项目打包方式,如下:(我这里的target:es2022是为了支持模块中顶层await的使用) // Vite 配置文件 export default…...
MeterSphere接口测试脚本断言
MeterSphere接口测试脚本断言 我们在接口自动化测试过程中,经常遇到无论我们传入什么数据信息,只要响应体报文中某个字段为不固定的特定信息(如:或1或2或3),就符合预期,流程就可以继续…...
探索顶级PDF水印API:PDFBlocks(2024年更新)
引言 在一个敏感信息常常面临风险的时代,能够轻松高效地保护文档的能力至关重要。PDF水印已成为企业和个人寻求保护其知识产权、确保文件保密性的基本工具。 PDFBlocks 文字水印 API是什么? PDFBlocks API 提供了一个强大的解决方案,用于在…...
c语言开源库之uthash用法
目录 (1)uthash介绍和下载地址 (2)uthash基本用法 1.定义自己要使用的哈希表结构体 2.初始化哈希表的头指针 3.插入数据(不同key类型对应不同函数) 4.查找数据(不同key类型对应不同函数&a…...
OurTV v3.1.1 — 完全免费,播放流畅的电视直播软件
OurTV是一款专业的魔改大屏版开源电视直播软件,与“我的电视”类似,内含丰富的电视频道,完全免费且无广告,画质清晰,播放流畅,提供良好的观影体验。此外,该软件还提供手机版。 链接:…...
精武杯的部分复现
标红的为答案 计算机手机部分 1、请综合分析计算机和⼿机检材,计算机最近⼀次登录的账户名是?admin 2.请综合分析计算机和⼿机检材,计算机最近⼀次插⼊的USB存储设备串号是?S3JKNX0JA05097Y 3.请综合分析计算机和⼿机检材,谢弘…...
verdaccio搭建npm私服
安装verdaccio npm i verdaccio -g执行命令verdaccio启动私服 verdaccio nrm启动的私 nrm use https://privateservernpm.xxx.com/添加用户 npm adduser --registry https://privateservernpm.xxx.com/发布包到私服 npm publish删除包 npm unpublish <package-nameve…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...
算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...
