一、核心知识点总览
本节课聚焦SpringBoot开发的"身份安全+上线部署"两大核心场景,核心分两部分:
- JWT鉴权:理解JSON Web Token的原理,实现用户身份的加密传输与验证,解决传统Session的分布式问题;
- 打包部署:掌握SpringBoot项目两种部署格式(JAR/WAR)的实战步骤,明确两者差异与常见问题;
- 安全重点:JWT的密钥保护、签名验证,以及部署后的源码泄漏风险防御。
二、SpringBoot 身份鉴权:JWT技术
JWT(JSON Web Token)本质是"加密的用户身份凭证",无需服务端存储Session,只需通过Token本身验证身份(适合分布式系统)。可以类比为"带防伪标志的电子身份证"------Header是"证件类型",Payload是"用户信息",Signature是"防伪印章"。
2.1 JWT基础概念:三部分结构
JWT最终是一串以.分隔的字符串,格式为Header.Payload.Signature,三部分均通过Base64编码(注意:Base64是编码不是加密,可解码,安全靠Signature)。
| 部分 | 作用 | 示例(JSON格式) |
|---|---|---|
| Header | 声明Token类型(typ)和签名算法(alg) | json {"alg": "HS256", "typ": "JWT"} (HS256:HMAC-SHA256对称加密算法) |
| Payload | 存储用户信息(自定义字段+标准声明) | json {"userid": 1, "username": "admin", "iat": 1516239022} (iat:Token生成时间) |
| Signature | 对Header+Payload的签名,防止篡改 | 算法:HMACSHA256(Base64Encode(Header) + "." + Base64Encode(Payload), 密钥) |
关键:Payload可解码查看,安全性靠Signature------若篡改Payload,未重新用密钥签名,服务端验证会失败。
2.2 JWT实战步骤(基于com.auth0:java-jwt)
步骤1:引入JWT依赖(pom.xml)
使用auth0的Java-JWT库(轻量、易用),指定版本3.4.0:
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
步骤2:创建JWT Token(生成"电子身份证")
编写JwtController,实现用户信息加密生成Token的接口(模拟用户登录成功后返回Token):
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class JwtController {
// 密钥(核心!必须保密,生产环境用复杂密钥,如32位随机字符串)
private static final String SECRET = "xiaodisec";
/**
* 生成JWT Token
* @param id 用户ID(自定义参数)
* @param user 用户名(自定义参数)
* @param pass 密码(实际开发中不建议存密码,此处仅演示)
* @return 生成的JWT字符串
*/
@PostMapping("/jwtcreate")
@ResponseBody
public String createToken(
@RequestParam Integer id,
@RequestParam String user,
@RequestParam String pass) {
try {
// 1. 指定签名算法(与Header一致,HS256对称算法)
Algorithm algorithm = Algorithm.HMAC256(SECRET);
// 2. 构建JWT Token
String jwtToken = JWT.create()
// 自定义Payload字段(用户信息,可按需添加)
.withClaim("userid", id)
.withClaim("username", user)
.withClaim("password", pass) // 实际开发:禁止存明文密码!
// (可选)设置Token过期时间,如2小时:.withExpiresAt(new Date(System.currentTimeMillis() + 7200000))
// 3. 用密钥签名,生成最终Token
.sign(algorithm);
System.out.println("生成的JWT:" + jwtToken);
return jwtToken;
} catch (Exception e) {
e.printStackTrace();
return "Token生成失败";
}
}
}
步骤3:解析JWT Token(验证"电子身份证")
添加解析接口,验证Token合法性(签名是否正确),并提取用户信息:
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class JwtController {
// 复用之前的密钥(必须与生成Token时一致!)
private static final String SECRET = "xiaodisec";
// 省略createToken方法...
/**
* 解析并验证JWT Token
* @param jwtdata 前端传入的JWT字符串
* @return 验证结果(如用户信息或错误提示)
*/
@PostMapping("/jwtcheck")
@ResponseBody
public String checkToken(@RequestParam String jwtdata) {
try {
// 1. 构建验证器(指定算法和密钥,与生成时一致)
Algorithm algorithm = Algorithm.HMAC256(SECRET);
JWTVerifier verifier = JWT.require(algorithm).build();
// 2. 验证Token(若签名错误/过期,会抛出异常)
DecodedJWT decodedJWT = verifier.verify(jwtdata);
// 3. 提取Payload中的用户信息
Integer userId = decodedJWT.getClaim("userid").asInt();
String username = decodedJWT.getClaim("username").asString();
String password = decodedJWT.getClaim("password").asString();
// 4. 返回验证结果(实际开发:可根据用户信息判断权限)
String result = "验证成功!用户信息:ID=" + userId + ",用户名=" + username;
System.out.println(result);
return result;
} catch (Exception e) {
// Token无效(签名错误/过期/篡改)
return "Token无效:" + e.getMessage();
}
}
}
步骤4:前端页面(模拟用户交互)
在resources/static目录下创建index.html,提供"生成Token"和"验证Token"的表单:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JWT测试页面</title>
</head>
<body>
<h2>1. 生成JWT Token</h2>
<form action="/jwtcreate" method="post">
用户ID:<input type="text" name="id" required><br>
用户名:<input type="text" name="user" required><br>
密码:<input type="text" name="pass" required><br>
<input type="submit" value="生成Token">
</form>
<h2>2. 验证JWT Token</h2>
<form action="/jwtcheck" method="post">
输入Token:<input type="text" name="jwtdata" required style="width: 500px;"><br>
<input type="submit" value="验证Token">
</form>
</body>
</html>
测试流程:
- 访问http://localhost:8080/index.html,输入用户信息(如ID=1,用户=admin,密码=123456);
- 点击"生成Token",获取类似eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXNzd29yZCI6IjEyMzQ1NiIsInVzZXJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiJ9.q__DmCYaffqXmQweBgITek-NmhsSAhgwExNA3lQspQk的字符串;
- 将Token粘贴到"验证Token"表单,点击验证,显示"验证成功"。
2.3 JWT安全风险与防御
JWT的安全核心是"密钥保护"和"签名验证",常见风险及防御措施如下:
| 安全风险 | 表现形式 | 防御措施 |
|---|---|---|
| 密钥爆破 | 攻击者用工具暴力破解简单密钥(如xiaodisec),篡改Token后重新签名 | 1. 使用强密钥(至少32位随机字符串,如a8f5d2b9c7e3f1a4b6c8d0e2f4a6b8c0);2. 定期更换密钥 |
| 未验证签名 | 服务端未执行verifier.verify(),直接解码Payload,攻击者可篡改用户信息 | 1. 强制验证签名(必须调用verify()方法);2. 捕获签名异常,直接拒绝无效Token |
| Token无过期时间 | Token永久有效,泄露后攻击者可长期使用 | 1. 生成Token时添加过期时间(withExpiresAt(new Date(System.currentTimeMillis() + 3600000)),1小时过期);2. 实现Token刷新机制 |
| Payload存敏感信息 | 直接存储明文密码、Token等,Base64解码后可直接查看 | 1. 禁止存敏感信息(如密码,可存用户ID、角色);2. 敏感信息需额外加密(如AES) |
三、SpringBoot 打包部署:JAR&WAR
SpringBoot项目有两种部署格式,核心差异是"是否依赖外部Tomcat"------JAR内置Tomcat,直接运行;WAR需部署到外部Tomcat。
3.1 JAR包部署(推荐:快速、轻量)
JAR是SpringBoot默认打包格式,内置嵌入式Tomcat,无需额外配置服务器,适合单机/小型项目。
步骤1:配置pom.xml(确保打包类型为JAR)
默认即为JAR,无需额外修改,若需指定主类(防止打包后找不到入口),可添加:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- 指定SpringBoot启动类(替换为你的启动类全路径) -->
<mainClass>com.example.testjwt.TestJwtApplication</mainClass>
<!-- 关键:设为false,否则打包会跳过主类(常见报错点!) -->
<skip>false</skip>
</configuration>
</plugin>
</plugins>
</build>
步骤2:执行Maven打包命令
-
方式1:IDE中操作(如IDEA)
右侧"Maven"→ 项目名 → Lifecycle → 双击clean(清理旧包)→ 双击package(生成新包)。
-
方式2:命令行操作
进入项目根目录(含pom.xml的目录),执行:
mvn clean package
步骤3:运行JAR包
打包完成后,包位于target目录下(如testjwt-0.0.1-SNAPSHOT.jar),执行命令运行:
# 格式:java -jar 包名.jar
java -jar testjwt-0.0.1-SNAPSHOT.jar
- 访问:http://服务器IP:8080/index.html(端口在application.properties中配置);
- 停止服务:按Ctrl+C(Linux环境可配合nohup实现后台运行:nohup java -jar 包名.jar &)。
3.2 WAR包部署(需外部Tomcat:适合企业级部署)
WAR格式需排除SpringBoot内置Tomcat,部署到外部Tomcat(如Tomcat 9),适合多项目共享服务器的场景。
步骤1:修改pom.xml(设置为WAR+排除内置Tomcat)
<!-- 1. 打包类型改为WAR -->
<packaging>war</packaging>
<dependencies>
<!-- 2. 排除内置Tomcat(避免与外部Tomcat冲突) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 3. 添加Servlet API依赖(外部Tomcat需要) -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope> <!-- 仅编译时生效,外部Tomcat提供 -->
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.example.testjwt.TestJwtApplication</mainClass>
<skip>false</skip>
</configuration>
</plugin>
</plugins>
<!-- 可选:指定WAR包名(默认是项目名+版本,可简化) -->
<finalName>testjwt</finalName>
</build>
步骤2:修改启动类(继承SpringBootServletInitializer)
让SpringBoot适配外部Tomcat的Servlet容器:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
// 关键:继承SpringBootServletInitializer,重写configure方法
public class TestJwtApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(TestJwtApplication.class, args);
}
// 重写configure,指定启动类
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(TestJwtApplication.class);
}
}
步骤3:打包并部署到外部Tomcat
- 执行Maven打包:同JAR包(mvn clean package),生成的WAR包在target目录下(如testjwt.war);
- 部署到Tomcat:
将WAR包复制到Tomcat的webapps目录下(如apache-tomcat-9.0.27/webapps); - 启动Tomcat:
运行Tomcat的bin/startup.bat(Windows)或bin/startup.sh(Linux); - 访问:
路径格式为http://服务器IP:Tomcat端口/WAR包名/index.html(如http://localhost:8080/testjwt/index.html)。
3.3 部署后安全:源码泄漏风险
SpringBoot打包后的JAR/WAR是压缩文件,若被攻击者获取,可通过工具反编译获取源码,需注意:
-
反编译方式:
- 直接用IDEA打开JAR/WAR包,IDEA会自动反编译class文件,显示源码;
- 用工具(如JD-GUI)打开class文件,查看源码。
-
防御措施:
- 生产环境避免将JAR/WAR包暴露在可下载路径(如Web根目录);
- 使用代码混淆工具(如ProGuard),降低反编译后源码的可读性;
- 敏感配置(如JWT密钥、数据库密码)不硬编码,通过环境变量或配置中心(如Nacos)注入。
四、核心总结
| 模块 | 核心要点 |
|---|---|
| JWT鉴权 | 1. 三部分结构:Header(算法)、Payload(用户信息)、Signature(签名);2. 安全核心是密钥+签名验证;3. 必须加Token过期时间 |
| JAR部署 | 内置Tomcat,mvn clean package打包,java -jar运行,适合快速部署 |
| WAR部署 | 需排除内置Tomcat、继承SpringBootServletInitializer,部署到外部Tomcat,适合企业级场景 |
| 安全防御 | 1. JWT用强密钥+过期时间;2. 禁止Payload存敏感信息;3. 保护JAR/WAR包,避免源码泄漏 |