基于JWT跨语言开发分布式业务系统的挑战与实践:多语言协作的最佳方案
在现代分布式架构下,开发团队往往由来自不同技术栈和开发语言的工程师组成。如何有效地管理这些开发人员的协作,尤其是在实现跨语言的认证与授权机制时,成为了开发者面临的一个重大挑战。JSON Web Token(JWT)作为一种轻量级、跨语言的身份认证和授权标准,能够为分布式系统提供有效的解决方案。本文将探讨如何利用JWT在一个多语言开发的分布式业务系统中实现跨语言协作,分析在实际开发过程中可能遇到的技术难题与解决策略,并通过具体的实践案例,展示JWT在保证系统安全性、扩展性和跨语言兼容性方面的优势。
引言:
在当今的软件开发环境中,企业的业务系统往往是分布式的,且不同的模块可能由不同的技术栈和编程语言实现。例如,前端开发可能使用JavaScript或TypeScript,后端开发可能使用Java、Python、Go、Rust等不同的语言进行构建,甚至同一系统的不同服务可能采用不同的语言栈。这种多语言、多技术栈的开发模式带来了前所未有的灵活性,但也引发了一系列挑战,尤其是在系统的身份认证和授权方面。
JWT(JSON Web Token)凭借其轻量级、基于JSON格式、支持跨语言传递的特点,成为了许多分布式系统中实现认证和授权的首选方案。其主要优势在于,JWT能够通过自包含的方式携带用户的认证信息,并且能够在不同语言和平台之间共享。无论是Java、Python还是Node.js,几乎所有主流编程语言都提供了JWT的支持库,使得跨语言的身份认证变得可行且高效。
然而,尽管JWT在跨语言认证方面表现优异,但在分布式业务系统的开发过程中,仍然存在许多需要关注的技术难题。本篇文章将深入探讨如何利用JWT解决跨语言协作中的认证和授权问题,并结合实际的案例分析,提供最佳实践和解决方案。
第一章:分布式系统中的跨语言协作
分布式系统是一种将系统功能分散到多个节点或服务上的架构,它具有更高的可扩展性和容错性。在分布式系统中,不同模块之间的协作至关重要。随着技术的不断发展,许多企业开始采用微服务架构,将系统拆分为多个相对独立的服务。这些服务可能由不同的开发团队开发,而这些团队的开发人员往往使用不同的编程语言。
例如,前端开发人员可能使用JavaScript(Vue、React等框架),而后端开发人员可能使用Java、Python、Go等编程语言。为了实现这些不同语言之间的无缝协作,必须有一种通用的协议或标准来解决不同系统之间的通信和身份认证问题。JWT作为一种跨语言、跨平台的标准,成为了分布式系统中最常用的解决方案之一。
第二章:JWT的基本原理与工作机制
JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用环境中传递声明。JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。这三部分通过“.”分隔,并采用Base64编码进行传输。
- 头部(Header): 通常包含令牌的类型(JWT)和使用的签名算法(如HS256)。
- 载荷(Payload): 包含实际传递的声明信息,通常是用户信息、权限信息、过期时间等。
- 签名(Signature): 用于验证令牌的完整性和来源,防止令牌被篡改。
JWT的工作流程通常包括以下几个步骤:
- 用户登录时,认证服务器验证用户信息,并生成一个JWT令牌,返回给客户端。
- 客户端将JWT令牌存储在本地(如LocalStorage、SessionStorage等),并在每次发送请求时将该令牌附加到HTTP请求头中。
- 服务器接收到请求后,通过验证JWT的签名来确认请求的合法性,如果合法,则继续处理请求。
由于JWT是自包含的,它携带了所有需要的信息,客户端和服务端无需再查询数据库进行验证,这使得JWT非常适合用于分布式系统中的身份认证。
第三章:跨语言开发中的JWT实现与挑战
在多语言开发的分布式系统中,JWT的最大优势之一就是它的跨语言特性。几乎所有流行的编程语言都提供了JWT的支持库。例如,Java使用jjwt
库,Python使用PyJWT
库,Node.js使用jsonwebtoken
库,Go语言使用golang-jwt/jwt
库等。
然而,跨语言开发也带来了一些挑战,特别是在以下几个方面:
-
签名算法的一致性: 不同语言的JWT库可能对签名算法的实现存在差异,导致跨语言验证失败。因此,在设计跨语言系统时,必须确保各个服务之间使用相同的签名算法和密钥。
-
时区和时间戳问题: JWT中通常包含过期时间(exp)和生效时间(nbf),这些时间戳需要保持一致,且要考虑时区的差异。开发人员需要特别注意时区的处理和时间戳的同步。
-
错误处理与兼容性: 各个语言中的JWT库在错误处理机制、异常抛出方式以及异常信息的格式上可能存在差异。这可能导致不同语言的服务在处理无效或过期的JWT时表现不一致。因此,必须在系统设计阶段进行充分的错误处理策略规划。
-
安全性和密钥管理: 由于JWT令牌的签名依赖于密钥,因此在多语言开发的分布式系统中,密钥的管理和共享变得尤为重要。如何确保密钥的安全存储与分发,避免泄漏和滥用,是一个亟待解决的问题。
第四章:跨语言协作的最佳实践
在分布式业务系统的开发中,多个开发团队使用不同的语言协同工作是常见的现象。为了确保系统的高效开发和稳定运行,以下是一些跨语言协作中的最佳实践:
-
标准化协议和规范: 为了确保系统各个部分的兼容性,团队应当在项目开始时就统一确定使用的JWT库和签名算法。通过制定详细的开发规范,可以减少因不同实现导致的问题。
-
统一的时间管理: 对于JWT中的时间戳,建议使用统一的时间源(如UTC时间)进行处理,避免因时区差异引发的问题。
-
密钥管理与共享: 在跨语言开发中,建议使用专门的密钥管理系统(如HashiCorp Vault、AWS KMS等)来管理密钥,确保密钥的安全传递和存储。
-
错误与异常处理机制的统一: 各语言中的错误处理方式应尽量统一。例如,定义统一的错误代码和错误消息格式,并确保所有服务能够一致地响应和处理认证失败的情况。
-
定期的跨团队沟通: 跨语言开发需要高度的协作,定期的跨团队沟通非常重要,尤其是在设计API接口和验证JWT的过程时,确保所有团队对JWT的使用和处理有统一的理解。
第五章:实际案例分析
通过一个具体的案例来展示如何在实际开发中使用JWT进行跨语言协作。假设我们正在开发一个电商平台的分布式系统,该系统由多个服务组成,包括用户服务、订单服务和商品服务等。这些服务分别使用Java、Node.js和Python进行开发,我们将通过JWT实现跨语言的身份认证和授权。
步骤1:设计JWT的结构和验证机制
首先,我们定义了JWT的结构,包括头部、载荷和签名部分。JWT的载荷中包含了用户的ID、角色信息以及权限等。在不同服务之间传递JWT时,所有服务都需要使用相同的密钥和签名算法进行验证。
步骤2:开发各个服务的JWT验证功能
- Java服务: 使用
jjwt
库进行JWT的解析和验证。通过JwtParser
类验证JWT的签名并解析其中的用户信息。 - Node.js服务: 使用
jsonwebtoken
库验证JWT,提取用户信息并根据权限决定是否允许访问资源。 - Python服务: 使用
PyJWT
库进行JWT验证和解析,确保JWT在Python服务中能够正常工作。
步骤3:统一时间戳格式和错误处理
所有服务都统一使用UTC时间格式,并且定义了统一的错误响应格式。当JWT无效或过期时,各个服务返回统一的错误代码和错误信息。
第六章:JWT在分布式业务系统中的安全性问题
尽管JWT在跨语言分布式系统中有诸多优势,但它也带来了一些安全性挑战。随着分布式系统中认证与授权需求的增加,JWT的安全性尤为关键。以下是JWT在分布式系统中可能遇到的安全问题及其解决方案。
1. JWT的泄露问题
由于JWT令牌中携带了用户的认证信息和权限信息,如果JWT令牌被盗取,攻击者可能会伪装成合法用户进行不正当访问。因此,防止JWT令牌泄露至关重要。
解决方案:
- 加密传输: 确保JWT令牌通过HTTPS进行传输,避免在传输过程中被窃取。
- 短生命周期令牌: JWT令牌应设置较短的有效期(例如10分钟),减少攻击者利用盗取令牌的时间窗口。
- 使用Refresh Token机制: 在短期内通过Access Token进行认证,过期后通过Refresh Token进行重新认证。这样,过期的令牌即使被盗也无法长期滥用。
2. JWT的签名问题
JWT的安全性依赖于签名的强度。如果攻击者能够破解签名算法或获得签名密钥,他们就能够伪造JWT令牌,进而绕过认证机制。
解决方案:
- 使用强大的签名算法: 推荐使用HMAC SHA-256或RSA等安全性较高的签名算法。
- 安全存储签名密钥: 不论是对称密钥还是非对称密钥,都必须妥善保管。可以使用专门的密钥管理工具,如HashiCorp Vault、AWS KMS等,来管理和保护密钥。
- 密钥轮换: 定期更换签名密钥,并确保所有服务及时同步更新密钥,避免长期使用同一个密钥。
3. JWT的过期问题
JWT通常包含一个过期时间(exp
字段),当令牌过期时,它应该被拒绝。过期时间的计算和验证至关重要,尤其是在跨时区和多个语言环境中。
解决方案:
- 统一时间源: 使用统一的时间标准(如UTC时间)进行过期时间的计算,避免由于时区差异导致的验证错误。
- 合理设计过期时间: 根据业务需求设置适当的过期时间。对于安全敏感的应用,可以将过期时间设置为较短时间,避免攻击者获得长期有效的令牌。
4. JWT伪造问题
JWT令牌中的载荷部分是Base64编码的,虽然不能直接被修改,但它是可以被解码并读取的。攻击者如果知道JWT的签名算法和密钥,可能会伪造合法的JWT令牌。
解决方案:
- 验证签名: 在每次使用JWT时,必须验证其签名,确保令牌未被篡改。
- 使用非对称加密算法: 如果使用非对称算法(如RSA),可以将公钥暴露给所有服务,只保留私钥在认证服务器,这样可以减少密钥泄漏的风险。
5. JWT的权限泄露问题
JWT中存储了用户的权限信息,如果设计不当,攻击者通过获取JWT令牌,可能会获取到不该拥有的权限。
解决方案:
- 最小权限原则: 在JWT的载荷部分,只包含必要的权限信息,不要将敏感信息或过多的权限信息存储在JWT中。
- 服务端权限验证: 尽管JWT可以在客户端携带用户权限信息,但服务端仍然需要验证用户是否有权限访问某些资源。JWT只是认证信息的载体,最终的权限判断应由服务端完成。
第七章:跨语言协作中的性能优化
在跨语言开发的分布式系统中,除了安全性,性能也是必须关注的问题。由于系统中涉及不同编程语言的服务交互和JWT的验证等操作,如何优化性能,确保系统高效运行,是开发者在实践中必须解决的问题。
1. JWT令牌的存储与传输
JWT令牌作为请求的一部分,需要在客户端和服务器之间传输。由于JWT令牌是自包含的,它的大小可能较大,尤其是在存储了大量用户信息或权限数据时。频繁地传输较大的JWT令牌可能会影响网络性能。
解决方案:
- 减少JWT载荷的大小: 只将最必要的信息存储在JWT的载荷部分。避免将过多的数据(如用户的完整资料或历史记录)放入JWT中。
- 压缩JWT: 对于较大的JWT令牌,可以使用压缩算法(如GZIP)进行压缩,减小其体积,从而减少网络传输开销。
2. JWT验证的性能影响
JWT令牌的验证过程通常包括计算签名、解码和验证过期时间等操作。虽然JWT验证是轻量级的,但在高并发的分布式环境下,频繁的JWT验证可能会影响服务器的性能。
解决方案:
- 缓存JWT验证结果: 可以使用缓存机制(如Redis)缓存JWT的验证结果,避免每次请求都进行重复验证。只需要验证JWT的有效性一次,然后将结果缓存一段时间。
- 并行处理: 在高并发场景下,可以通过多线程或异步处理来提高JWT验证的吞吐量。
3. 跨语言通信的优化
跨语言服务之间的通信可能涉及HTTP、gRPC、Message Queue等协议,不同协议的性能特性有所不同。例如,HTTP可能会带来较大的请求/响应头开销,而gRPC则能够提供更低的延迟和更高的传输效率。
解决方案:
- 选择合适的通信协议: 在分布式系统中,不同的服务之间应该根据业务需求选择合适的通信协议。对于高吞吐量和低延迟的服务,可以考虑使用gRPC或Protobuf等高效的序列化协议。
- 使用负载均衡: 在跨语言开发中,服务的负载均衡策略至关重要。合理配置负载均衡器,确保请求均匀分发到各个服务实例,避免单个服务节点的性能瓶颈。
第八章:开发团队协作与跨语言开发的管理
在跨语言的分布式系统开发中,开发团队的协作尤为重要。不同语言栈的开发人员之间需要紧密配合,确保系统的功能、性能、安全性等方面达到预期目标。
1. 开发流程与规范统一
跨语言开发的团队通常会面临不同编程语言之间的差异,尤其是在编码规范、异常处理、日志管理等方面。为了确保项目进展顺利,团队必须达成一致的开发流程与编码规范。
解决方案:
- 制定跨语言编码规范: 在项目开始时,团队应就代码风格、错误处理、日志格式等方面制定一致的规范,确保跨语言的代码整洁、可维护。
- 统一API设计标准: 在服务间进行通信时,API接口的设计应统一,采用RESTful或gRPC等标准化的API设计方式,减少语言间的差异。
2. 跨语言工具和文档支持
在跨语言协作中,不同语言栈的开发人员可能对其他语言的开发流程和工具不熟悉。因此,团队应提供充分的工具支持和技术文档,帮助开发人员更快上手和理解其他语言的实现。
解决方案:
- 自动化文档生成: 使用Swagger、OpenAPI等工具自动生成接口文档,确保各服务之间的API接口定义保持同步。
- 跨语言测试框架: 为了确保跨语言服务之间的兼容性,团队可以使用如Postman、JUnit、pytest等工具,进行自动化的接口测试。
3. 持续集成与部署
跨语言开发的分布式系统需要高效的持续集成(CI)和持续部署(CD)流程,以确保代码在不同语言栈中的协作能够顺利进行。
解决方案:
- 多语言的CI/CD管道: 在CI/CD过程中,确保每个语言栈的构建、测试和部署流程都得到支持。例如,使用Jenkins、GitLab CI等工具,配置多语言的构建任务。
- 容器化部署: 通过Docker容器化不同语言栈的服务,简化跨语言服务的部署与运行。
结语:
基于JWT的跨语言开发在分布式业务系统中为团队提供了强大的认证和授权支持。虽然跨语言协作在技术上具有一定挑战性,但通过合理的设计、安全措施、性能优化和团队协作,可以克服这些挑战,实现高效、安全的分布式系统。随着技术的不断发展,跨语言开发和JWT技术的结合将越来越成为现代大规模分布式系统架构中不可或缺的一部分。
相关文章:
基于JWT跨语言开发分布式业务系统的挑战与实践:多语言协作的最佳方案
在现代分布式架构下,开发团队往往由来自不同技术栈和开发语言的工程师组成。如何有效地管理这些开发人员的协作,尤其是在实现跨语言的认证与授权机制时,成为了开发者面临的一个重大挑战。JSON Web Token(JWT)作为一种轻…...

二分法篇——于上下边界的扭转压缩间,窥见正解辉映之光(2)
前言 上篇介绍了二分法的相关原理并结合具体题目进行讲解运用,本篇将加大难度,进一步强化对二分法的掌握。 一. 寻找峰值 1.1 题目链接:https://leetcode.cn/problems/find-peak-element/description/ 1.2 题目分析: 题目要求返回数组内…...
什么是 Kata Containers?
什么是 Kata Containers? Kata Containers 是一种结合了容器技术和虚拟机技术的轻量级运行时,旨在提供容器的速度和虚拟机的安全性。它将容器运行在一个隔离的虚拟机中,从而大幅提升安全性,同时保持容器的高效性。 Kata Contain…...

SpringMvc项目配置RabbitMq
前言:只有消费者部分,没有记录生产者部分 结构图 配置类 可以xml配置,也可以配置类,二者可以相互转化。两种bean注入的方式。 import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; import org.spring…...

shell编程(4)脚本与用户交互以及if条件判断
shell编程(4)脚本与用户交互以及if条件判断 声明! 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下,如涉及侵权马上删除文章 笔记只是方便各位师傅的学习和探讨,文章所提到的网站以及内容,…...
vue2组件跨层级数据共享provide 和 inject
在 Vue 2 中,provide 和 inject 的功能也是可以使用的,虽然在 Vue 3 中它们成为了组合式 API 的一部分。在 Vue 2 中,provide 和 inject 主要是用于祖先组件和后代组件之间的数据共享,而不是通过 props 和 emit 逐层传递。 Vue 2…...
springboot/ssm校园闲置物品交易系统ava大学生二手闲置交易平台web二手源码
springboot/ssm校园闲置物品交易系统ava大学生二手闲置交易平台web二手源码 基于springboot(可改ssm)htmlvue项目 开发语言:Java 框架:springboot/可改ssm vue JDK版本:JDK1.8(或11) 服务器:tomcat 数…...

Redis实现限量优惠券的秒杀
核心:避免超卖问题,保证一人一单 业务逻辑 代码步骤分析 全部代码 Service public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {Resourceprivate ISeckillVoucher…...

Linux centOS 7 安装 rabbitMQ
1.安装前需要了解,rabbitmq安装需要先安装erlang,特别注意的是erlang与rabbitmq的版本之间需要匹配。 el/7/rabbitmq-server-3.10.0-1.el7.noarch.rpm - rabbitmq/rabbitmq-server packagecloud 3.10版本的rabbitmq 对于erlang的版本要求可以看此连接…...
活着就好20241202
亲爱的朋友们,大家早上好!今天是2024年12月2日,第49周的第一天,也是十二月的第二天,农历甲辰[龙]年十月三十。在这个全新月份的开始、阳光初升的清晨,愿第一缕阳光悄悄探进你的房间,带给你满满的…...

自由学习记录(28)
C# 中的流(Stream) 流(Stream)是用于读取和写入数据的抽象基类。 流表示从数据源读取或向数据源写入数据的矢量过程。 C# 中的流类是从 System.IO.Stream 基类派生的,提供了多种具体实现,每种实现都针对…...

操作系统、虚拟化技术与云原生01
操作系统基础 操作系统定义 OS声明了软件怎么调用硬件,同时支持人机交互 人机交互的过程: shell是人机交互转换的虚拟环境,内核只能识别0、1组成的数据流,底层资源只能识别电流的变化 操作系统的组成 1. 进程管理 进程定义&#x…...
linux的挂卸载
挂卸载操作 在 Linux 系统中,挂载(mount)和卸载(umount)是管理文件系统和存储设备的核心操作。通过这两个操作,我们可以将设备(如硬盘、光盘、U盘等)或网络文件系统的内容集成到系统…...
【和春笋一起学C++】OpenCV中数组和指针运用实例
前言:前面学习了数组和指针在C中的处理原理,本文通过自己编写一个图像处理的函数实例来加深对数组和指针的理解。为什么是图像处理呢,因为图像数据是一个二维矩阵,相当于一个二维数组,前面学习了一维数组,现…...
Maya 中创建游戏角色的头发,并将其导出到 Unreal Engine 5
这段视频教程讲解了如何在 Maya 中创建游戏角色的头发,并将其导出到 Unreal Engine 5 中,重点是如何处理头发的物理模拟和材质。 作者 Andrew Giovannini 首先展示了一个已完成的带物理模拟的头发模型,并介绍了他自己的游戏行业背景。然后&a…...

React 路由(React Router):在 React 应用中管理路由
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

SAP-CPI组件Transformation介绍之Converter
1.配置CSV to XML Converter Field Description XML Schema 选择Select按钮,选择合适 XSD 文件. 或者可以选择 Upload from File System 系统中查找合适的XML文件....
Laravel 代理收益排行榜
创建了一个收入表 CREATE TABLE income_logs (id int(11) unsigned NOT NULL AUTO_INCREMENT,order_id int(11) NOT NULL COMMENT 订单ID,type int(11) NOT NULL DEFAULT 0 COMMENT 类型 0 支出 1收入,user_id int(11) NOT NULL COMMENT 消费者用户,price decimal(10,2) NOT…...

LeetCode hot100面试背诵版(自用)
点击题目可以跳转到LeetCode 哈希 两数之和 public int[] twoSum(int[] nums, int target) {int lengthnums.length;int[] ans new int[2];for (int i 0; i <length-1 ; i) {for (int j i1; j < length; j) {if(nums[i]nums[j]target){ans[0]i;ans[1]j;}}}return an…...

常见的Web安全漏洞——XSS
概念 跨站脚本攻击(XSS),指攻击者通过篡改网页,嵌入恶意脚本程序,在用户浏览网页时,控制用户浏览器进行恶意操作。 XXS的分类 反射型XSS存储型XSSDOM型XSS 原理 反射型XSS 接收用户提交的访问者的姓名࿰…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...

java高级——高阶函数、如何定义一个函数式接口类似stream流的filter
java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用(Math::max) 2 函数接口…...
深入浅出WebGL:在浏览器中解锁3D世界的魔法钥匙
WebGL:在浏览器中解锁3D世界的魔法钥匙 引言:网页的边界正在消失 在数字化浪潮的推动下,网页早已不再是静态信息的展示窗口。如今,我们可以在浏览器中体验逼真的3D游戏、交互式数据可视化、虚拟实验室,甚至沉浸式的V…...
跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践
在电商行业蓬勃发展的当下,多平台运营已成为众多商家的必然选择。然而,不同电商平台在商品数据接口方面存在差异,导致商家在跨平台运营时面临诸多挑战,如数据对接困难、运营效率低下、用户体验不一致等。跨平台商品数据接口的标准…...

轻量级Docker管理工具Docker Switchboard
简介 什么是 Docker Switchboard ? Docker Switchboard 是一个轻量级的 Web 应用程序,用于管理 Docker 容器。它提供了一个干净、用户友好的界面来启动、停止和监控主机上运行的容器,使其成为本地开发、家庭实验室或小型服务器设置的理想选择…...
python打卡第47天
昨天代码中注意力热图的部分顺移至今天 知识点回顾: 热力图 作业:对比不同卷积层热图可视化的结果 def visualize_attention_map(model, test_loader, device, class_names, num_samples3):"""可视化模型的注意力热力图,展示模…...
HTML中各种标签的作用
一、HTML文件主要标签结构及说明 1. <!DOCTYPE html> 作用:声明文档类型,告知浏览器这是 HTML5 文档。 必须:是。 2. <html lang“zh”>. </html> 作用:包裹整个网页内容,lang"z…...