当前位置: 首页 > news >正文

前端面试题——token安全问题处理与大数据列表展示

1.长时间保存token问题

长时间保存Token涉及多个方面的问题,包括安全性、性能、以及Token的管理策略等。以下是对长时间保存Token问题的详细分析:

一、安全性问题

  1. Token泄露风险
    • Token是用户身份验证的凭证,如果长时间保存且未采取适当的安全措施,一旦泄露,攻击者可能利用Token进行恶意操作。
    • 解决方案:使用加密技术存储Token,确保即使被窃取也无法轻易解密;同时,定期更换Token,降低Token被长期利用的风险。
  2. Token伪造和篡改
    • 长时间有效的Token更容易成为攻击者的目标,他们可能尝试伪造或篡改Token以获取非法访问权限。
    • 解决方案:使用数字签名机制对Token进行签名,确保Token的完整性和真实性;同时,通过HTTPS等安全协议传输Token,防止在传输过程中被篡改。

二、性能问题

  1. 服务器负载
    • 长时间保存Token意味着服务器需要维护更多的会话信息,这可能会增加服务器的负载和存储压力。
    • 解决方案:合理设置Token的有效期,避免过长的有效期导致服务器负载过大;同时,优化Token的存储和检索机制,提高性能。
  2. 客户端性能
    • 在某些情况下,客户端也需要保存Token以便进行身份验证。长时间保存Token可能会对客户端的存储和性能造成一定影响。
    • 解决方案:根据客户端的实际情况选择合适的存储方式(如Cookie、LocalStorage等),并合理设置Token的过期时间。

三、Token管理策略

  1. 定期更换Token
    • 设置Token的有效期,并在到期后自动更换新的Token。这可以降低Token被长期利用的风险,并提高系统的安全性。
    • 解决方案:在服务器端实现Token的定期更换机制,并在客户端进行相应的处理(如自动刷新Token)。
  2. Token失效处理
    • 当Token失效时(如过期、被撤销等),需要确保系统能够正确处理失效的Token,防止用户继续使用失效的Token进行身份验证。
    • 解决方案:在服务器端维护一个Token的失效列表(如黑名单),并在每次身份验证时检查Token是否已失效。
  3. Token绑定(使用令牌绑定)
    • 将Token与特定的用户或设备信息绑定,以增加Token的安全性。即使Token被泄露,攻击者也无法在其他用户或设备上使用它。
    • 解决方案:在生成Token时,将用户或设备的特定信息(如IP地址、设备ID等)作为Token的一部分进行加密处理。
  4. 加密Token

综上所述,长时间保存Token需要综合考虑安全性、性能和Token管理策略等多个方面的问题。为了确保系统的安全性和性能,建议采取适当的安全措施和Token管理策略来降低Token被泄露、伪造和篡改的风险。

 java token 过期 自动刷新 token token过期原理_mob64ca13fb1f2e的技术博客_51CTO博客

四、token过期以及更新策略

Token过期及更新机制是Web开发中常见的安全措施,用于控制用户会话的有效性和安全性。在Spring Boot应用中,这通常通过JWT(JSON Web Tokens)或其他类似的令牌机制来实现。以下是一个简化的示例,展示了如何在Spring Boot中处理JWT Token的过期和更新机制。

1. JWT Token生成

首先,你需要一个方法来生成JWT Token。这通常涉及用户认证信息(如用户名和密码)的验证,并在成功后生成一个包含用户身份和过期时间的Token。

import io.jsonwebtoken.Claims;  
import io.jsonwebtoken.Jwts;  
import io.jsonwebtoken.SignatureAlgorithm;  
import java.util.Date;  
import java.util.HashMap;  
import java.util.Map;  public class JwtUtil {  private String secretKey = "your_secret_key"; // 密钥,用于签名Token  // 生成JWT Token  public String generateToken(String username, long expirationTimeInMilliseconds) {  long nowMillis = System.currentTimeMillis();  Date now = new Date(nowMillis);  // 设置Token的过期时间  Date expiryDate = new Date(nowMillis + expirationTimeInMilliseconds);  // 添加自定义信息到Token  Map<String, Object> claims = new HashMap<>();  claims.put("username", username);  // 生成Token  return Jwts.builder()  .setClaims(claims)  .setExpiration(expiryDate)  .signWith(SignatureAlgorithm.HS512, secretKey)  .compact();  }  // ... 其他JWT相关的方法,如解析Token  
}

2. Token过期处理

在Spring Boot中,你通常会在拦截器(Interceptor)或过滤器(Filter)中检查Token的有效性。如果Token过期,你可以返回一个错误响应,提示用户重新登录。

import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  // 假设这是一个拦截器的方法  
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  String token = // 从请求中获取Token  if (token != null && !token.isEmpty()) {  // 验证Token  try {  Claims claims = Jwts.parser()  .setSigningKey(secretKey)  .parseClaimsJws(token)  .getBody();  // 检查Token是否过期  if (claims.getExpiration().before(new Date())) {  // Token已过期  response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);  response.setContentType("application/json");  response.getWriter().write("{\"error\":\"Token expired\"}");  return false;  }  // Token有效,继续处理请求  return true;  } catch (Exception e) {  // Token无效或已损坏  response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);  response.setContentType("application/json");  response.getWriter().write("{\"error\":\"Invalid token\"}");  return false;  }  }  // 没有Token或Token为空,可能返回401或其他错误  response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);  response.setContentType("application/json");  response.getWriter().write("{\"error\":\"No token found\"}");  return false;  
}

3. Token更新机制

Token更新通常不是由服务端主动完成的,而是由客户端在Token即将过期时请求新的Token。这可以通过刷新Token机制来实现。

  • 客户端:在Token过期之前(例如,Token有效期的一半时间),客户端可以发送一个请求到服务器,请求一个新的Token(通常称为刷新Token)。
  • 服务端:服务端接收到刷新Token的请求后,验证刷新Token的有效性(如果使用了刷新Token),并生成一个新的JWT Token返回给客户端。
刷新Token的工作流程:
  1. 用户认证:用户首次登录时,服务器验证其凭据(如用户名和密码)。验证成功后,服务器生成两个Token:一个访问Token(JWT Token)和一个刷新Token。访问Token用于后续的API请求认证,而刷新Token则用于在访问Token过期时获取新的访问Token。

  2. 发送请求:客户端在发送请求到受保护的API时,需要在请求头或请求体中包含访问Token。

  3. Token验证:服务器验证请求中的访问Token。如果Token有效,则处理请求。如果Token无效(例如,已过期或签名不正确),服务器将拒绝请求。

  4. 刷新Token请求:当访问Token接近过期时(或已经过期),客户端使用刷新Token向服务器发送请求以获取新的访问Token。这个请求通常发送到一个专门的端点(如/api/token/refresh)。

  5. 刷新Token验证:服务器验证刷新Token的有效性。如果Token有效且未过期,服务器将生成一个新的访问Token(以及可能的一个新的刷新Token,取决于你的策略),并将它们返回给客户端。

  6. 更新客户端Token:客户端接收到新的Token后,将旧的访问Token替换为新的访问Token,并(可选地)更新其存储的刷新Token。

  7. 继续使用:客户端现在可以使用新的访问Token继续发送请求到受保护的API。

请注意,刷新Token本身也应该有一个过期时间,并且通常比JWT Token的过期时间要长,但比用户会话的持续时间要短。

2.10万数据的列表如何保证不卡顿

 

 虚拟列表

如果你想要更深入地了解虚拟滚动的原理,或者想要根据自己的需求定制实现,你也可以手动实现虚拟滚动。

1.手动实现 
基本思路
  1. 计算可见项:根据滚动容器的滚动位置和大小,计算出当前应该显示的列表项。
  2. 渲染可见项:只渲染这些可见项,并管理它们的渲染和销毁。
  3. 监听滚动事件:监听滚动容器的滚动事件,以便在滚动时更新可见项。
示例代码框架

由于手动实现虚拟滚动的代码相对复杂且高度依赖于具体的项目需求,这里仅提供一个大致的框架思路:

<template>  <div ref="scrollContainer" class="scroll-container" @scroll="handleScroll">  <div v-for="item in visibleItems" :key="item.id" class="item">  <!-- 渲染列表项的内容,例如 item.text -->  {{ item.text }}  </div>  </div>  
</template>  <script>  
export default {  data() {  return {  items: [], // 假设这是从API或其他地方获取的完整数据列表  visibleItems: [], // 当前渲染到DOM中的可见项  itemHeight: 50, // 假设每个列表项的高度是50px  };  },  methods: {  handleScroll() {  this.visibleItems = this.calculateVisibleItems();  },  calculateVisibleItems() {  const start = this.$refs.scrollContainer.scrollTop;  const end = start + this.$refs.scrollContainer.clientHeight;  const startIndex = Math.floor(start / this.itemHeight);  const endIndex = Math.ceil(end / this.itemHeight);  // 确保不会超出items数组的范围  const visibleItems = this.items.slice(startIndex, endIndex).map(item => ({  ...item, // 如果需要,可以在这里添加或修改item的属性  }));  return visibleItems;  },  },  mounted() {  // 假设items在mounted之前已经被填充  this.handleScroll(); // 初始化时计算一次  },  // 如果items是异步获取的,你可能需要在数据到达后调用handleScroll  // watch: {  //   items(newVal) {  //     this.handleScroll();  //   }  // },  
};  
</script>  <style>  
.scroll-container {  height: 300px; /* 设定滚动容器的高度 */  overflow-y: auto; /* 允许垂直滚动 */  
}  
.item {  height: 50px; /* 每个列表项的高度,应与data中的itemHeight一致 */  /* 其他样式 */  
}  
</style>
2.vue库实现 
npm install vue-virtual-scroller
<template>  <div>  <recycler  class="scroller"  :items="items"  :item-size="32"  v-slot="{ item }"  >  <div class="item">{{ item.text }}</div>  </recycler>  </div>  
</template>  <script>  
import { Recycler } from 'vue-virtual-scroller'  export default {  components: {  Recycler  },  data() {  return {  items: Array.from({ length: 10000 }, (_, k) => ({ text: `Item ${k}` }))  }  }  
}  
</script>  <style>  
.scroller {  height: 300px;  overflow-y: auto;  
}  
.item {  height: 32px;  line-height: 32px;  padding-left: 10px;  border-bottom: 1px solid #ccc;  
}  
</style>

2.分页

  1. 后端Spring Boot
    • 定义数据访问层(Repository),使用Spring Data JPA的PageablePage接口实现分页查询。
    • 控制器(Controller)接收前端发送的分页参数(页码和每页数量),调用服务层处理分页查询,并将结果封装为JSON返回给前端。
  2. 前端Vue
    • 使用Vue组件管理分页逻辑和展示数据。
    • 通过Axios或其他HTTP客户端向后端发送分页请求,接收并展示分页数据。
    • 提供分页控件(如页码按钮、上一页/下一页按钮),用于改变分页参数并重新请求数据。
后端Spring Boot代码示例
1. 实体类(Entity)

假设我们有一个Post实体类,代表文章。

@Entity  
public class Post {  @Id  @GeneratedValue(strategy = GenerationType.IDENTITY)  private Long id;  private String title;  private String content;  // getters and setters  
}
2. 仓库接口(Repository)

使用Spring Data JPA的JpaRepositoryPageable接口。

public interface PostRepository extends JpaRepository<Post, Long> {  Page<Post> findAll(Pageable pageable);  
}
3. 服务层(Service)

服务层调用仓库接口实现分页逻辑。

@Service  
public class PostService {  @Autowired  private PostRepository postRepository;  public Page<Post> getPosts(Pageable pageable) {  return postRepository.findAll(pageable);  }  
}
4. 控制器(Controller)

控制器处理HTTP请求,并调用服务层。

@RestController  
@RequestMapping("/api/posts")  
public class PostController {  @Autowired  private PostService postService;  @GetMapping  public ResponseEntity<Page<Post>> getPosts(@RequestParam(value = "page", defaultValue = "0") int page,  @RequestParam(value = "size", defaultValue = "10") int size) {  Pageable pageable = PageRequest.of(page, size);  Page<Post> posts = postService.getPosts(pageable);  return ResponseEntity.ok(posts);  }  
}
前端Vue代码示例
1. Vue组件

使用Axios发送请求,并在Vue组件中处理数据。

<template>  <div>  <ul>  <li v-for="post in posts.content" :key="post.id">{{ post.title }}</li>  </ul>  <div>  <button @click="prevPage">Prev</button>  <button @click="nextPage">Next</button>  </div>  </div>  
</template>  <script>  
import axios from 'axios';  export default {  data() {  return {  posts: null,  currentPage: 0,  pageSize: 10,  };  },  created() {  this.fetchPosts();  },  methods: {  fetchPosts() {  axios.get(`/api/posts?page=${this.currentPage}&size=${this.pageSize}`)  .then(response => {  this.posts = response.data;  this.currentPage = this.posts.number; // 更新当前页码  })  .catch(error => console.error("There was an error!", error));  },  nextPage() {  if (this.posts && this.posts.totalPages > this.currentPage + 1) {  this.currentPage++;  this.fetchPosts();  }  },  prevPage() {  if (this.currentPage > 0) {  this.currentPage--;  this.fetchPosts();  }  }  }  
};  
</script>

相关文章:

前端面试题——token安全问题处理与大数据列表展示

1.长时间保存token问题 长时间保存Token涉及多个方面的问题&#xff0c;包括安全性、性能、以及Token的管理策略等。以下是对长时间保存Token问题的详细分析&#xff1a; 一、安全性问题 Token泄露风险&#xff1a; Token是用户身份验证的凭证&#xff0c;如果长时间保存且未…...

Flask项目入门和视图

1、第一个项目的结构 以示例代码中的入口文件app.py为例子 &#xff08;1&#xff09;引入Flask以及创建Flask对象 from flask import Flask app Flask(__name__)&#xff08;2&#xff09; 路由route 视图函数 app.route(/index/) def hello_world():# 响应&#xff1a;…...

深入理解Lucene:开源全文搜索引擎

目录 引言 Lucene的核心概念 索引 分析器 存储 Lucene的工作流程 创建索引 搜索索引 Lucene核心技术 倒排索引 排序算法 索引压缩与合并 并发控制与实时更新 结论 引言 随着互联网的飞速发展&#xff0c;信息量呈指数级增长&#xff0c;如何有效地管理和检索这些…...

Qt中pro项目文件配置介绍

Qt中&#xff0c;工程文件是以.pro后缀的文件&#xff0c;主要用以包含Qt模块&#xff0c;代码文件&#xff0c;依赖库&#xff0c;以及对项目的一些属性进行配置。 具体看个例子&#xff1a; #这块是添加Qt模块 #.pro文件中使用#号作为注释 QT core gui #QT webengine…...

相亲交友中的用户画像构建方法探讨

随着互联网技术的发展&#xff0c;相亲交友平台成为现代人寻找伴侣的重要渠道之一。在这一过程中&#xff0c;如何精准地为用户推荐合适的对象成为了平台能否成功的关键。本文旨在探讨相亲交友平台中用户画像的构建方法&#xff0c;并分析其对于提高匹配度的重要性&#xff08;…...

总结

本来想把这个写完再写总结的&#xff0c;但是我发现卡了&#xff0c;明天去问问别人。 今天写上传个文件&#xff0c;没上传好&#xff0c;找到问题了&#xff0c;但是还不知道怎么改&#xff0c;我发给前端成功了&#xff0c;刚刚看了下好像是这里的问题&#xff0c;但是不是…...

C# 开发教程-入门基础

1.C# 简介、环境&#xff0c;程序结构 2.C# 基本语法&#xff0c;变量&#xff0c;控制局域&#xff0c;数据类型&#xff0c;类型转换 3.C# 数组、 循环&#xff0c;Linq 4.C# 类&#xff0c;封装&#xff0c;方法 5.C# 枚举、字符串 6.C# 面相对象&#xff0c;继承&#xff0…...

Windows上,使用远程桌面连接Ubuntu

要在 Ubuntu 上设置公网 IP 并通过 Windows 远程桌面连接到 Ubuntu&#xff0c;你需要完成以下步骤&#xff1a; 设置 Ubuntu 公网 IP&#xff1a; 确保你的 Ubuntu 服务器已经配置了一个公网 IP 地址。 你可以通过云服务提供商&#xff08;如 AWS、Azure、Google Cloud&#…...

SharePoint Online 计划 1 部署方案

概述 SharePoint Online 是 Microsoft 365 的一部分,为组织提供了一种高效、灵活的协作平台。SharePoint Online 计划 1(Plan 1)尤其适用于中小型企业,提供了基本的文档管理和协作功能。本文将详细介绍如何部署 SharePoint Online 计划 1,并探讨其配置、管理和最佳实践。…...

kubernetes存储之GlusterFS(GlusterFS for Kubernetes Storage)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…...

网络安全等保培训 ppt

网络安全等级保护怎么做&#xff1f;...

开关磁阻电机(SRM)系统的matlab性能仿真与分析

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 SRM的基本结构 4.2 SRM的电磁关系 4.3 SRM的输出力矩 5.完整工程文件 1.课题概述 开关磁阻电机(SRM)系统的matlab性能仿真与分析&#xff0c;对比平均转矩vs相电流&#xff0c;转矩脉动vs相电流&a…...

最新动态一致的文生视频大模型FancyVideo部署

FancyVideo是一个由360AI团队和中山大学联合开发并开源的视频生成模型。 FancyVideo的创新之处在于它能够实现帧特定的文本指导&#xff0c;使得生成的视频既动态又具有一致性。 FancyVideo模型通过精心设计的跨帧文本引导模块&#xff08;Cross-frame Textual Guidance Modu…...

茴香豆:企业级知识问答工具实践闯关任务

基础任务 在 InternStudio 中利用 Internlm2-7b 搭建标准版茴香豆知识助手&#xff0c;并使用 Gradio 界面完成 2 轮问答&#xff08;问题不可与教程重复&#xff0c;作业截图需包括 gradio 界面问题和茴香豆回答&#xff09;。知识库可根据根据自己工作、学习或感兴趣的内容调…...

英飞凌 PSoC6 RT-Thread 评估板简介

概述 2023年&#xff0c;英飞凌&#xff08;Infineon&#xff09;联合 RT-Thread 发布了一款 PSoC™ 62 with CAPSENSE™ evaluation kit 开发板 &#xff08;以下简称 PSoC 6 RTT 开发板&#xff09;&#xff0c;该开发套件默认内置 RT-Thread 物联网操作系统。PSoC 6 RTT 开…...

深度学习笔记(8)预训练模型

深度学习笔记&#xff08;8&#xff09;预训练模型 文章目录 深度学习笔记&#xff08;8&#xff09;预训练模型一、预训练模型构建一、微调模型&#xff0c;训练自己的数据1.导入数据集2.数据集处理方法3.完形填空训练 使用分词器将文本转换为模型的输入格式参数 return_tenso…...

C#事件的用法

前言 在C#中&#xff0c;事件&#xff08;Event&#xff09;可以实现当类内部发生某些特定的事情时&#xff0c;它可以通知其他类或对象。事件是基于委托&#xff08;Delegate&#xff09;的&#xff0c;委托是一种类型安全的函数指针&#xff0c;它定义了方法的类型&#xff…...

金砖软件测试赛项之Jmeter如何录制脚本!

一、简介 Apache JMeter 是一款开源的性能测试工具&#xff0c;用于测试各种服务的负载能力&#xff0c;包括Web应用、数据库、FTP服务器等。它可以模拟多种用户行为&#xff0c;生成负载以评估系统的性能和稳定性。 JMeter 的主要特点&#xff1a; 图形用户界面&#xff1a;…...

docker-squash镜像压缩

docker-squash 和 docker export docker load 的原理和效果有一些相似之处&#xff0c;但它们的工作方式和适用场景有所不同。 docker-squash docker-squash 是一个工具&#xff0c;它通过分析 Docker 镜像的层&#xff08;layers&#xff09;并将其压缩成更少的层来减小镜像…...

Vue3快速入门+axios的异步请求(基础使用)

学习Vue之前先要学习htmlcssjs的基础使用 Vue其实是js的框架 常用到的Vue指令包括vue-on,vue-for,vue-blind,vue-if&vue-show,v-modul vue的基础模板&#xff1a; <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8&…...

VM16安装macOS11

注意&#xff1a; 本文内容于 2024-09-17 12:08:24 创建&#xff0c;可能不会在此平台上进行更新。如果您希望查看最新版本或更多相关内容&#xff0c;请访问原文地址&#xff1a;VM16安装macOS11。感谢您的关注与支持&#xff01; 使用 Vmware Workstation Pro 16 安装 macOS…...

自定义复杂AntV/G6案例

一、效果图 二、源码 /** * * Author: me * CreatDate: 2024-08-22 * * Description: 复杂G6案例 * */ <template><div class"moreG6-wapper"><div id"graphContainer" ref"graphRef" class"graph-content"></d…...

Golang | Leetcode Golang题解之第419题棋盘上的战舰

题目&#xff1a; 题解&#xff1a; func countBattleships(board [][]byte) (ans int) {for i, row : range board {for j, ch : range row {if ch X && !(i > 0 && board[i-1][j] X || j > 0 && board[i][j-1] X) {ans}}}return }...

CCF刷题计划——LDAP(交集、并集 how to go)

LDAP 计算机软件能力认证考试系统 不知道为什么&#xff0c;直接给我报一个运行错误&#xff0c;得了0分。但是我在Dev里&#xff0c;VS里面都跑的好好的&#xff0c;奇奇怪怪。如果有大佬路过&#xff0c;请帮小弟看看QWQ。本题学到的&#xff1a;交集set_intersection、并集…...

谷歌论文提前揭示o1模型原理:AI大模型竞争或转向硬件

Open AI最强模型o1的护城河已经没有了&#xff1f;仅在OpenAI发布最新推理模型o1几日之后&#xff0c;海外社交平台 Reddit 上有网友发帖称谷歌Deepmind在 8 月发表的一篇论文内容与o1模型原理几乎一致&#xff0c;OpenAI的护城河不复存在。 谷歌DeepMind团队于今年8月6日发布…...

【ShuQiHere】 探索数据挖掘的世界:从概念到应用

&#x1f310; 【ShuQiHere】 数据挖掘&#xff08;Data Mining, DM&#xff09; 是一种从大型数据集中提取有用信息的技术&#xff0c;无论是在商业分析、金融预测&#xff0c;还是医学研究中&#xff0c;数据挖掘都扮演着至关重要的角色。本文将带您深入了解数据挖掘的核心概…...

LabVIEW提高开发效率技巧----使用事件结构优化用户界面响应

事件结构&#xff08;Event Structure&#xff09; 是 LabVIEW 中用于处理用户界面事件的强大工具。通过事件驱动的编程方式&#xff0c;程序可以在用户操作时动态执行特定代码&#xff0c;而不是通过轮询&#xff08;Polling&#xff09;的方式不断检查界面控件状态。这种方式…...

【前端】ES6:Set与Map

文章目录 1 Set结构1.1 初识Set1.2 实例的属性和方法1.3 遍历1.4 复杂数据结构去重 2 Map结构2.1 初识Map2.2 实例的属性和方法2.3 遍历 1 Set结构 它类似于数组&#xff0c;但成员的值都是唯一的&#xff0c;没有重复的值。 1.1 初识Set let s1 new Set([1, 2, 3, 2, 3]) …...

Java 之网络编程小案例

1. 多发多收 描述&#xff1a; 编写一个简单的聊天程序&#xff0c;客户端可以向服务器发送多条消息&#xff0c;服务器可以接收所有消息并回复。 代码示例&#xff1a; 服务器端 (Server.java): import java.io.*; import java.net.*; import java.util.concurrent.Execut…...

Spring Boot:现代化Java应用开发的艺术

目录 什么是Spring Boot&#xff1f; 为什么选择Spring Boot&#xff1f; Spring Boot的核心概念 详细步骤&#xff1a;创建一个Spring Boot应用 步骤1&#xff1a;使用Spring Initializr创建项目 步骤2&#xff1a;解压并导入项目 步骤3&#xff1a;构建和配置项目 po…...