目录
一、系统架构设计
1.1 整体架构概览
抖音这样的短视频系统,整体架构可采用当下流行的微服务架构 ,将系统拆分为多个独立的、可独立部署和扩展的微服务模块,以此来应对海量用户请求和高并发场景。以下是几个核心的微服务模块及其职责:
- 用户模块:负责用户的注册、登录、个人信息管理、密码找回等功能。同时,还处理用户的关注、粉丝关系,以及用户隐私设置等社交相关的用户操作。比如,当用户注册时,该模块验证用户输入的信息,将用户数据存储到数据库,并为用户生成唯一标识。
- 视频模块:承担视频的上传、存储、转码、审核、下架等操作。在视频上传后,对视频进行转码处理,生成不同分辨率和格式的视频版本,以适配不同设备和网络环境。像抖音上的视频能在手机、平板等各种设备流畅播放,就离不开视频模块的转码工作。
- 社交模块:实现用户之间的互动功能,包括点赞、评论、分享视频,以及用户之间的私信聊天等。当用户对某个视频点赞时,社交模块会记录点赞行为,并实时更新视频的点赞数,同时通知视频发布者。
- 推荐模块:基于用户的行为数据、兴趣偏好、视频标签等信息,运用机器学习和深度学习算法,为用户推荐个性化的视频内容。例如,通过分析用户的历史观看记录、点赞和评论的视频类型,推荐模块为用户精准推送符合其兴趣的新视频。
- 搜索模块:提供视频、用户、话题等内容的搜索功能,支持关键词搜索、模糊搜索和热门搜索推荐。当用户在搜索框输入关键词时,搜索模块快速从海量数据中检索相关内容,并按照相关性和热度进行排序展示。
这些微服务模块之间通过轻量级的通信机制,如 HTTP/RESTful API 或消息队列(如 Kafka)进行通信,实现数据交互和业务流程的协同。例如,当用户发布一个新视频时,视频模块完成视频存储和初步处理后,通过消息队列通知社交模块和推荐模块,社交模块更新相关用户的动态,推荐模块则将新视频纳入推荐候选集。
1.2 关键组件选型
- 后端框架:选择 Spring Boot 框架,它基于 Spring 框架,遵循 “约定大于配置” 的原则,能极大简化开发过程,提高开发效率。Spring Boot 提供了大量的自动配置,减少了繁琐的 XML 配置文件,使开发者可以更专注于业务逻辑的实现。例如,在配置数据源时,Spring Boot 只需简单的属性配置即可连接到 MySQL 数据库,无需像传统 Spring 项目那样编写大量配置代码。同时,Spring Boot 内置了 Tomcat、Jetty 等 Servlet 容器,可直接以 jar 包形式运行项目,方便部署和运维。
- 数据库:选用 MySQL 作为主要的关系型数据库,用于存储结构化数据,如用户信息、视频信息、评论信息、点赞信息等。MySQL 具有成熟稳定、性能高效、开源免费、社区支持丰富等优点,能够满足抖音系统对数据存储和复杂查询的需求。以用户信息表为例,可设计包含用户 ID、用户名、密码、手机号、邮箱、注册时间等字段,通过 SQL 语句可以方便地进行用户数据的增删改查操作。同时,为了应对高并发读写场景,可采用分库分表、读写分离等技术对 MySQL 进行优化。
- 缓存:引入 Redis 作为缓存组件,利用其高性能的内存存储和丰富的数据结构,提高系统的响应速度和并发处理能力。Redis 可以缓存热门视频信息、用户的登录状态、频繁访问的用户数据等。比如,将热门视频的基本信息(如视频 ID、标题、封面图、点赞数等)缓存到 Redis 中,当大量用户请求热门视频列表时,直接从 Redis 中读取数据,减少对 MySQL 数据库的压力,大大提升系统的响应速度。此外,Redis 还支持分布式缓存,可通过集群部署的方式扩展缓存容量和性能。
- 视频处理:借助 JavaCV 和 FFmpeg 进行视频处理。JavaCV 是一个基于 Java 的计算机视觉库,它封装了 OpenCV 等多个计算机视觉库,提供了丰富的图像和视频处理功能,方便在 Java 代码中进行视频的读取、写入、剪辑、特效添加等操作。FFmpeg 是一个强大的音视频处理工具,支持多种视频格式的编解码、转码、裁剪、合并等操作。在抖音系统中,使用 JavaCV 结合 FFmpeg,可实现视频上传后的格式转换、分辨率调整、视频剪辑、添加水印等功能。例如,将用户上传的各种格式视频统一转码为 MP4 格式,以适配不同平台的播放需求。
- 视频传输:采用 HTTP/2 协议进行视频传输,HTTP/2 协议相比 HTTP/1.1 协议具有多路复用、头部压缩、服务器推送等特性,能够显著提高视频传输的效率和性能,减少视频加载的延迟,提升用户观看体验。同时,使用 Nginx 服务器作为反向代理和负载均衡器,一方面,Nginx 可以将客户端的请求转发到后端的视频服务器,实现动静分离,提高网站的访问速度;另一方面,通过 Nginx 的负载均衡功能,将视频请求均匀分配到多个后端服务器上,避免单个服务器负载过高,确保系统的高可用性和稳定性。
二、功能模块实现
2.1 用户模块
2.1.1 用户注册与登录
使用 Spring Boot 实现用户注册和登录功能,首先需要定义用户实体类User,包含用户名、密码、手机号等字段。
public class User {
private Long id;
private String username;
private String password;
private String phone;
// 其他字段及getter、setter方法
}
在注册功能中,需要对用户输入的信息进行验证,确保用户名、密码等必填字段不为空,密码强度符合要求等。例如,使用正则表达式验证密码强度:
import java.util.regex.Pattern;
public class UserRegistrationService {
private static final Pattern PASSWORD_PATTERN = Pattern.compile("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).{8,}$");
public boolean validatePassword(String password) {
return PASSWORD_PATTERN.matcher(password).matches();
}
public boolean registerUser(User user) {
if (!validatePassword(user.getPassword())) {
return false;
}
// 此处省略保存用户到数据库的逻辑,假设使用JPA
userRepository.save(user);
return true;
}
}
在登录功能中,需要根据用户输入的用户名或手机号查询数据库,验证密码是否正确。以下是使用 Spring Security 进行登录验证的示例代码:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
@Service
public class UserLoginService {
@Autowired
private AuthenticationManager authenticationManager;
public boolean login(String username, String password) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(username, password));
if (authentication.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(authentication);
return true;
}
return false;
}
}
2.1.2 用户信息管理
用户信息修改功能可通过 MyBatis 操作数据库实现。首先定义UserMapper接口:
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Update;
@Mapper
public interface UserMapper {
@Update("UPDATE user SET username = #{username}, phone = #{phone} WHERE id = #{id}")
int updateUser(User user);
}
在业务层调用UserMapper的updateUser方法来实现用户信息修改:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserInfoService {
@Autowired
private UserMapper userMapper;
public boolean updateUserInfo(User user) {
return userMapper.updateUser(user) > 0;
}
}
用户信息查询功能同样可以通过 MyBatis 实现。例如,根据用户 ID 查询用户信息:
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User getUserById(Long id);
}
在业务层调用UserMapper的getUserById方法获取用户信息:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserInfoService {
@Autowired
private UserMapper userMapper;
public User getUserInfo(Long id) {
return userMapper.getUserById(id);
}
}
2.2 视频模块
2.2.1 视频上传
使用 Java 实现视频上传,可借助 Spring Boot 的MultipartFile来处理文件上传。首先在控制器中接收上传的视频文件:
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
@RestController
public class VideoUploadController {
private static final String UPLOAD_DIR = "uploads/videos/";
@PostMapping("/uploadVideo")
public String uploadVideo(@RequestParam("video") MultipartFile videoFile) {
try {
String originalFileName = videoFile.getOriginalFilename();
String fileExtension = originalFileName.substring(originalFileName.lastIndexOf("."));
String uniqueFileName = UUID.randomUUID().toString() + fileExtension;
Path filePath = Paths.get(UPLOAD_DIR + uniqueFileName);
Files.write(filePath, videoFile.getBytes());
// 保存文件路径到数据库,假设使用JPA
Video video = new Video();
video.setFilePath(UPLOAD_DIR + uniqueFileName);
videoRepository.save(video);
return "Video uploaded successfully";
} catch (IOException e) {
e.printStackTrace();
return "Video upload failed";
}
}
}
2.2.2 视频播放
视频播放功能实现涉及视频流处理和格式转换。在 Java 中,可调用 FFmpeg 实现视频格式转换。以下是一个简单的 Java 代码示例,用于将上传的视频转换为 MP4 格式:
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class VideoTranscoding {
private static final String FFMPEG_PATH = "ffmpeg"; // 假设ffmpeg已配置环境变量
public static void convertToMp4(String inputFilePath, String outputFilePath) {
List<String> command = new ArrayList<>();
command.add(FFMPEG_PATH);
command.add("-i");
command.add(inputFilePath);
command.add("-c:v");
command.add("libx264");
command.add("-preset");
command.add("ultrafast");
command.add("-profile:v");
command.add("baseline");
command.add("-c:a");
command.add("aac");
command.add("-strict");
command.add("experimental");
command.add(outputFilePath);
try {
ProcessBuilder processBuilder = new ProcessBuilder(command);
Process process = processBuilder.start();
process.waitFor();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
在视频播放时,可通过 HTTP 协议将视频文件流传输给客户端。例如,使用 Spring Boot 的ResponseBody将视频文件以流的形式返回:
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.io.File;
@RestController
public class VideoPlayController {
@GetMapping("/playVideo/{videoId}")
public ResponseEntity<FileSystemResource> playVideo(@PathVariable Long videoId) {
// 根据videoId从数据库获取视频文件路径
String videoFilePath = videoRepository.findById(videoId).get().getFilePath();
File videoFile = new File(videoFilePath);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("video/mp4"))
.body(new FileSystemResource(videoFile));
}
}
2.2.3 视频推荐
基于用户行为数据和机器学习算法实现个性化视频推荐功能。算法原理主要是通过分析用户的历史观看记录、点赞、评论、收藏等行为数据,构建用户兴趣画像。例如,使用协同过滤算法,寻找具有相似兴趣爱好的用户群体,将这些用户喜欢的视频推荐给目标用户;或者使用内容过滤算法,根据视频的标签、类别、主题等内容特征,推荐与用户历史观看视频内容相似的视频。
实现思路上,首先需要收集和存储用户行为数据到数据库中。然后定期对这些数据进行分析和处理,训练机器学习模型。在用户请求视频推荐时,调用训练好的模型,根据用户的 ID 或当前行为,计算出推荐视频列表,并返回给用户。具体实现中,可使用 Apache Mahout、Spark MLlib 等机器学习库来辅助实现推荐算法 。例如,使用 Mahout 的协同过滤算法实现视频推荐:
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.UserBasedRecommender;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;
import java.io.File;
import java.util.List;
public class VideoRecommendation {
public static List<RecommendedItem> recommendVideos(long userId, int numRecommendations) throws TasteException {
DataModel model = new FileDataModel(new File("user_behavior_data.csv"));
UserSimilarity similarity = new PearsonCorrelationSimilarity(model);
UserNeighborhood neighborhood = new NearestNUserNeighborhood(10, similarity, model);
UserBasedRecommender recommender = new GenericUserBasedRecommender(model, neighborhood, similarity);
return recommender.recommend(userId, numRecommendations);
}
}
2.3 社交模块
2.3.1 点赞、评论与分享
点赞和评论功能实现首先需要设计数据库表。以点赞表为例,可包含点赞 ID、用户 ID、视频 ID、点赞时间等字段;评论表可包含评论 ID、用户 ID、视频 ID、评论内容、评论时间等字段。
在业务逻辑处理上,点赞功能实现代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class LikeService {
@Autowired
private LikeMapper likeMapper;
public boolean likeVideo(Long userId, Long videoId) {
Like like = new Like();
like.setUserId(userId);
like.setVideoId(videoId);
like.setLikeTime(System.currentTimeMillis());
return likeMapper.insertLike(like) > 0;
}
public boolean unlikeVideo(Long userId, Long videoId) {
return likeMapper.deleteLike(userId, videoId) > 0;
}
}
评论功能实现代码类似:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CommentService {
@Autowired
private CommentMapper commentMapper;
public boolean addComment(Comment comment) {
comment.setCommentTime(System.currentTimeMillis());
return commentMapper.insertComment(comment) > 0;
}
public List<Comment> getCommentsByVideoId(Long videoId) {
return commentMapper.selectCommentsByVideoId(videoId);
}
}
分享功能实现可通过调用第三方分享接口,如微信分享、QQ 分享等。以微信分享为例,可使用微信开放平台提供的 SDK,在前端页面中调用微信分享 API,将视频链接、标题、描述等信息传递给微信客户端,实现视频分享:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>视频分享</title>
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<script>
wx.config({
debug: false,
appId: 'your_app_id',
timestamp: 'your_timestamp',
nonceStr: 'your_nonce_str',
signature: 'your_signature',
jsApiList: ['onMenuShareAppMessage']
});
wx.ready(function () {
wx.onMenuShareAppMessage({
title: '精彩视频分享',
desc: '快来观看这个有趣的视频',
link: 'https://your_video_url',
imgUrl: 'https://your_video_cover_url',
success: function () {
alert('分享成功');
},
cancel: function () {
alert('分享取消');
}
});
});
</script>
</head>
<body>
</body>
</html>
2.3.2 关注与粉丝
关注和粉丝功能实现需要设计关注表,包含关注 ID、关注者 ID、被关注者 ID、关注时间等字段。关注操作数据库代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class FollowService {
@Autowired
private FollowMapper followMapper;
public boolean followUser(Long followerId, Long followeeId) {
Follow follow = new Follow();
follow.setFollowerId(followerId);
follow.setFolloweeId(followeeId);
follow.setFollowTime(System.currentTimeMillis());
return followMapper.insertFollow(follow) > 0;
}
public boolean unfollowUser(Long followerId, Long followeeId) {
return followMapper.deleteFollow(followerId, followeeId) > 0;
}
}
粉丝列表获取实现可通过查询关注表,根据被关注者 ID 查询所有关注者信息:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class FollowService {
@Autowired
private FollowMapper followMapper;
public List<User> getFollowers(Long followeeId) {
return followMapper.selectFollowersByFolloweeId(followeeId);
}
}
三、性能优化与安全保障
3.1 性能优化
3.1.1 缓存机制
为了提高系统响应速度,减少数据库的压力,我们引入 Redis 作为缓存组件。Redis 是一种高性能的内存数据库,能够快速存储和读取数据。在抖音系统中,我们可以使用 Redis 缓存热门视频、用户信息、点赞数、评论数等频繁访问的数据。
以下是使用 Jedis 库操作 Redis 缓存热门视频的代码示例:
import redis.clients.jedis.Jedis;
public class VideoCache {
private static final String REDIS_HOST = "localhost";
private static final int REDIS_PORT = 6379;
public static void setVideoToCache(String videoId, String videoInfo) {
try (Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT)) {
jedis.setex("video:" + videoId, 3600, videoInfo); // 缓存1小时
}
}
public static String getVideoFromCache(String videoId) {
try (Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT)) {
return jedis.get("video:" + videoId);
}
}
}
在上述代码中,setVideoToCache方法将视频信息存入 Redis 缓存,设置过期时间为 1 小时;getVideoFromCache方法从 Redis 缓存中获取视频信息。当用户请求热门视频时,首先从 Redis 缓存中获取,如果缓存中不存在,则从数据库中查询,然后将查询结果存入缓存,以便下次快速访问。
3.1.2 数据库优化
数据库优化是提高系统性能的关键环节,以下是几种常见的数据库优化措施:
- 索引优化:在数据库表的常用查询字段上创建索引,可以大大提高查询效率。例如,在用户表的username字段上创建索引:
CREATE INDEX idx_username ON user (username);
在 Java 中,使用 JPA(Java Persistence API)可以在实体类上定义索引:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.Table;
@Entity
@Table(name = "user", indexes = {
@Index(name = "idx_username", columnList = "username")
})
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
// 其他字段及getter、setter方法
}
- 分页查询优化:在实现分页功能时,传统的limit offset, limit方式在查询偏移量较大的数据时性能较低。以 MySQL 数据库为例,当查询大表中靠后的数据时,如select * from video limit 10000, 10,会先读取 10010 条记录,然后抛弃前 10000 条,效率低下。可采用根据自增主键优化的方式,若主键自增且连续,原查询可改写为select * from video where id > 10000 limit 10,这样走索引且扫描行数大大减少,效率更高。但该方法要求主键自增连续且结果按主键排序。若按非主键字段排序分页,如select * from video ORDER BY title limit 90000, 5,因扫描索引找无索引行成本高,优化器可能放弃索引。此时可利用覆盖索引优化,先查主键再关联查记录,如select * from video v inner join (select id from video order by title limit 90000, 5) vi on v.id = vi.id,查询和排序在覆盖索引树上进行,效率大幅提升。
- 连接池使用:使用数据库连接池可以复用数据库连接,减少连接的建立和关闭开销,提高系统性能。HikariCP 是一个高性能的 JDBC 连接池,在 Spring Boot 项目中使用 HikariCP 作为连接池的配置示例如下:
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Value;
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource(
@Value("${spring.datasource.url}") String url,
@Value("${spring.datasource.username}") String username,
@Value("${spring.datasource.password}") String password
) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
config.setUsername(username);
config.setPassword(password);
config.setMaximumPoolSize(10); // 设置最大连接数
return new HikariDataSource(config);
}
}
通过上述优化措施,系统在处理大量数据和高并发请求时,性能得到显著提升。例如,在进行复杂查询时,优化前可能需要数秒才能返回结果,优化后通过索引优化和合理的查询语句调整,响应时间缩短至几百毫秒;在高并发场景下,使用连接池复用连接,系统能够更快地处理用户请求,吞吐量大幅提高 ,极大提升了用户体验。
3.2 安全保障
3.2.1 用户数据加密
在用户数据安全方面,我们采取了以下措施:
- 密码加密存储:使用 BCrypt 算法对用户密码进行加密存储,该算法使用了随机生成的盐,使得每次哈希的结果都不同,并且哈希计算非常耗时,增加了破解密码的难度。示例代码如下
import org.mindrot.jbcrypt.BCrypt;
public class PasswordUtil {
public static String hashPassword(String plainPassword) {
return BCrypt.hashpw(plainPassword, BCrypt.gensalt());
}
public static boolean verifyPassword(String plainPassword, String hashedPassword) {
return BCrypt.checkpw(plainPassword, hashedPassword);
}
}
在用户注册时,调用hashPassword方法对用户输入的密码进行加密,然后将加密后的密码存储到数据库中;在用户登录时,调用verifyPassword方法验证用户输入的密码与数据库中存储的加密密码是否匹配。
- 数据传输加密:使用 HTTPS 协议加密数据传输,确保用户数据在网络传输过程中的安全性。在 Spring Boot 项目中,可以通过配置 Tomcat 的 SSL 连接器来启用 HTTPS。首先,需要准备 SSL 证书,假设证书文件为keystore.jks,密码为password,在application.properties文件中添加如下配置:
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=password
server.ssl.key-password=password
server.ssl.protocol=TLS
上述配置告诉 Spring Boot 应用程序使用指定的密钥库文件和密码来启用 HTTPS 协议,确保数据在传输过程中被加密,防止数据被窃取或篡改。
3.2.2 防止恶意攻击
为了防止恶意攻击,我们采取了以下措施:
- 防止 SQL 注入:使用预编译语句(PreparedStatement)来执行 SQL 查询,将 SQL 语句与数据分离,避免用户输入的数据被当作 SQL 代码执行。例如,在查询用户信息时,使用预编译语句的示例代码如下:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class UserQuery {
public static User getUserById(Long userId) {
String sql = "SELECT * FROM user WHERE id =?";
try (Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");
PreparedStatement statement = connection.prepareStatement(sql)) {
statement.setLong(1, userId);
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
User user = new User();
user.setId(resultSet.getLong("id"));
user.setUsername(resultSet.getString("username"));
// 设置其他字段
return user;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
在上述代码中,?是占位符,用户输入的userId作为参数传递给预编译语句,而不是直接拼接到 SQL 字符串中,从而有效防止了 SQL 注入攻击。
- 防止 XSS 攻击:对用户输入进行过滤和转义,避免恶意脚本注入。使用 Apache Commons Text 库的StringEscapeUtils类对用户输入进行 HTML 转义,示例代码如下:
import org.apache.commons.text.StringEscapeUtils;
public class XssProtection {
public static String escapeHtml(String input) {
return StringEscapeUtils.escapeHtml4(input);
}
}
在将用户输入的数据展示到前端页面之前,调用escapeHtml方法对数据进行转义,将特殊字符转换为 HTML 实体,防止恶意脚本在浏览器中执行。例如,当用户发表评论时,对评论内容进行转义后再存储到数据库,并在展示评论时保持转义后的状态,确保页面的安全性。