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

SpringSecurity+jwt使用

参考文章链接

自定义SpringSecurity用户

package com.daben.springsecurityjwt.vo;import com.daben.springsecurityjwt.entity.SysUser;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.Collection;
/*** SpringSecurity用户** @Author JinDongSheng* @Date 2023-11-16 09:21*/
public class SpringSecurityUser extends User {/*** 系统用户*/private SysUser sysUser;/*** 构造函数*/public SpringSecurityUser(SysUser sysUser, Collection<? extends GrantedAuthority> authorities) {super(sysUser.getUsername(), sysUser.getPassword(), authorities);this.sysUser = sysUser;}public SysUser getSysUser() {return sysUser;}public void setSysUser(SysUser sysUser) {this.sysUser = sysUser;}
}

自定义认证失败处理类

package com.daben.springsecurityjwt.handle;import com.alibaba.fastjson2.JSON;
import com.daben.springsecurityjwt.vo.Result;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/*** 未登录处理类** @Author JinDongSheng* @Date 2023-11-15 17:37*/
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {response.setStatus(200);response.setContentType("application/json");response.setCharacterEncoding("utf-8");response.getWriter().print(JSON.toJSONString(Result.error("认证失败,无法访问系统资源!")));}
}

自定义Security用户服务实现类

package com.daben.springsecurityjwt.service;import com.daben.springsecurityjwt.entity.SysUser;
import com.daben.springsecurityjwt.vo.SpringSecurityUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Collections;
/*** Security用户服务实现类** @Author JinDongSheng* @Date 2023-11-16 09:14*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate SysUserServiceImpl sysUserServiceImpl;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 根据用户名称查询用户SysUser sysUser = sysUserServiceImpl.selectSysUserByName(username);// 查询用户权限List<GrantedAuthority> authorities = Collections.emptyList();// 返回Security指定的用户对象return new SpringSecurityUser(sysUser, authorities);}
}

自定义JWT工具类

package com.daben.springsecurityjwt.utils;import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.daben.springsecurityjwt.entity.SysUser;
import io.jsonwebtoken.*;
import org.apache.commons.lang3.StringUtils;
import java.util.Date;
/*** JWT工具类** @Author JinDongSheng* @Date 2023-11-16 15:49*/
public class JwtUtil {/*** Token有效期为30分钟*/public static final int EXPIRE_TIME = 30;/*** 秘钥*/private final static String SECRET_KEY = "ABCDE";/*** 生成token*/public static String createToken(SysUser user) {return Jwts.builder()// 将user放进JWT.setSubject(JSONUtil.toJsonStr(user))// 设置过期时间.setExpiration(DateUtil.offsetMinute(new Date(), EXPIRE_TIME))// 设置加密算法和秘钥.signWith(SignatureAlgorithm.HS512, SECRET_KEY).compact();}public static Claims parseToken(String token) {// 如果是空字符串直接返回nullif (StringUtils.isEmpty(token)) {return null;}// 这个Claims对象包含了许多属性,比如签发时间、过期时间以及存放的数据等Claims claims = null;// 解析失败了会抛出异常,所以我们要捕捉一下。token过期、token非法都会导致解析失败try {claims = Jwts.parser().setSigningKey(SECRET_KEY) // 设置秘钥.parseClaimsJws(token).getBody();} catch (JwtException e) {// 这里应该用日志输出,为了演示方便就直接打印了System.err.println("解析失败!");}return claims;}
}

自定义token认证过滤器

package com.daben.springsecurityjwt.filter;import cn.hutool.json.JSONUtil;
import com.daben.springsecurityjwt.entity.SysUser;
import com.daben.springsecurityjwt.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
/*** 登录过滤器** @Author JinDongSheng* @Date 2023-11-16 14:48*/
@Component
public class LoginFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 解析tokenClaims claims = JwtUtil.parseToken(request.getHeader("Authorization"));if (claims != null) {// 获取用户SysUser sysUser = JSONUtil.toBean(claims.getSubject(), SysUser.class);// 手动组装一个认证对象Authentication authentication = new UsernamePasswordAuthenticationToken(sysUser, null, Collections.emptyList());// 将认证对象放到上下文中SecurityContextHolder.getContext().setAuthentication(authentication);}filterChain.doFilter(request, response);}
}

自定义SpringSecurity配置类

package com.daben.springsecurityjwt.config;import com.daben.springsecurityjwt.filter.LoginFilter;
import com.daben.springsecurityjwt.handle.AuthenticationEntryPointImpl;
import com.daben.springsecurityjwt.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/*** spring security 配置** @Author JinDongSheng* @Date 2023-11-15 17:16*/
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {/*** 登录认证过滤器*/@Autowiredprivate LoginFilter loginFilter;/*** Security用户服务实现类*/@Autowiredprivate UserDetailsServiceImpl userDetailsService;/*** 未登录处理类*/@Autowiredprivate AuthenticationEntryPointImpl authenticationEntryPoint;@Overrideprotected void configure(HttpSecurity http) throws Exception {// 关闭csrfhttp.csrf().disable();// 关闭frameOptionshttp.headers().frameOptions().disable();// 指定未登录处理类http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);// 前置登录认证过滤器http.addFilterBefore(loginFilter, UsernamePasswordAuthenticationFilter.class);// 禁用sessionhttp.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);// 配置认证和授权接口http.authorizeRequests()// 指出放行接口.antMatchers("/sys/port/*").permitAll()// 拦截剩余接口.anyRequest().authenticated();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {// 指定UserDetailService和加密器auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Beanpublic PasswordEncoder passwordEncoder() {// 使用bcrypt加密return new BCryptPasswordEncoder();}
}

登录入口

package com.daben.springsecurityjwt.controller;import com.daben.springsecurityjwt.entity.SysUser;
import com.daben.springsecurityjwt.utils.JwtUtil;
import com.daben.springsecurityjwt.vo.Result;
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.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/*** 登录控制器** @Author JinDongSheng* @Date 2023-11-15 17:25*/
@RestController
@RequestMapping("/sys/port")
public class SysPortController {@Autowiredprivate AuthenticationManager authenticationManager;@PostMapping("/login")public Result login(@RequestBody SysUser sysUser) {// 认证Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(sysUser.getUsername(), sysUser.getPassword()));if (authenticate.isAuthenticated()) {// 生成tokenString token = JwtUtil.createToken(sysUser);// 返回tokenreturn Result.success(token);}return Result.error("用户名或密码错误");}
}

整体调用流程

  1. 服务启动,根据【自定义SpringSecurity配置类】配置信息(指定放行和认证接口、token认证过滤器、认证失败处理器、密码加密器、用户查询服务等。)
  2. 用户发起登录请求。
  3. token认证过滤器过滤,因为不携带token,直接放行调用登录接口,在登录接口中手动调用AuthenticationManager进行用户信息认证。
  4. AuthenticationManager进行用户信息认证时会调用【自定义Security用户服务实现类】查询数据库用户,然后将数据库用户封装成Security指定用户返回。
  5. 认证失败调用【自定义认证失败处理类】,认证成功则返回token
  6. 用户发起非登录请求,token过滤器过滤,token校验成功将认证对象放入上下文中。调用请求接口。token校验失败,调用【自定义认证失败处理类】。

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.vitalframework</groupId><artifactId>vital-framework-dependencies</artifactId><version>1.1.0</version></parent><groupId>com.citic</groupId><artifactId>vital-portal-core</artifactId><version>1.0.0</version><name>vital-portal-core</name><description>系统管理(门面工程)模块</description><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.rep4orting.outputEncoding>UTF-8</project.rep4orting.outputEncoding><java.version>1.8</java.version><maven.compiler.source>${java.version}</maven.compiler.source><maven.compiler.target>${java.version}</maven.compiler.target></properties><dependencies><!--CITIC 一些公共包--><dependency><groupId>com.vitalframework.web</groupId><artifactId>vital-framework-web-common</artifactId><version>1.0.0</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId></dependency><dependency><groupId>commons-net</groupId><artifactId>commons-net</artifactId></dependency><dependency><groupId>com.jcraft</groupId><artifactId>jsch</artifactId></dependency><!--代码生成器--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-scratchpad</artifactId></dependency><dependency><groupId>org.codehaus.castor</groupId><artifactId>castor-xml</artifactId></dependency></dependencies><build>
<!--        <finalName>${project.artifactId}</finalName>--><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><fork>true</fork><skip>true</skip></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-source-plugin</artifactId><version>${maven-source-plugin.version}</version></plugin></plugins></build>
</project>

相关文章:

SpringSecurity+jwt使用

参考文章链接 自定义SpringSecurity用户 package com.daben.springsecurityjwt.vo;import com.daben.springsecurityjwt.entity.SysUser; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; import j…...

html-网站菜单-点击显示导航栏

一、效果图 1.点击显示菜单栏&#xff0c;点击x号关闭&#xff1b; 2.点击一级菜单&#xff0c;展开显示二级&#xff0c;并且加号变为减号&#xff1b; 3.点击其他一级导航&#xff0c;自动收起展开的导航。 二、代码实现 <!DOCTYPE html> <html><head>&…...

【C++函数的进化】函数指针,模板,仿函数,lambda表达式

/*** poject * author jUicE_g2R(qq:3406291309)* file C函数的进化* * language C* EDA Base on VS2022* editor Obsidian&#xff08;黑曜石笔记软件&#xff09;* * copyright 2023* COPYRIGHT 原创学习笔记&#xff1a;转载需获得博…...

云服务器windows service2022 部署git服务器

1 安装 下载地址gitblit 解压到你的一个目录,我这里给的是C:\gitblit 根据官网提示要下载jre or jdk7.0,这里建议使用下载jre (jdk 有时候运行出问题,或者2个都安装),自行安装java,这里不做环境配置的说明 进入c:\gitblit\data 目录里面找到,defaults.properties 文件,编辑主…...

Linux_Docker修改Docker Root Dir

今天遇到需求&#xff0c;要修改一下docker容器和镜像的存储位置&#xff0c;默认位置为/var/lib/docker目录下&#xff0c;要修改到/new/dockerFile目录下。 停止docker服务 sudo service docker stop 备份docker容器镜像 移动/var/lib/docker目录下的文件到/dockerFile目录…...

解决requests 2.28.x版本SSL错误:证书验证失败

1、问题背景 在使用requests 2.28.1版本时&#xff0c;我进行HTTP post传输报告负载时&#xff0c;由于SSL验证设置为True&#xff0c;请求失败&#xff0c;错误如下&#xff1a;(Caused by SSLError(SSLCertVerificationError(1, ‘[SSL: CERTIFICATE_VERIFY_FAILED] certifi…...

【开源】基于Vue.js的开放实验室管理系统的设计和实现

项目编号&#xff1a; S 013 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S013&#xff0c;文末获取源码。} 项目编号&#xff1a;S013&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 实验室类型模块2.2 实验室模块2.3 实…...

使用composer安装ffmpeg的步骤

以下是使用composer安装ffmpeg的步骤&#xff1a; 1.在laravel根目录下执行以下命令安装ffmpeg&#xff1a; composer require php-ffmpeg/php-ffmpeg 2.如果不指定版本号&#xff0c;则默认使用0.14版本。 3.执行以上命令后&#xff0c;composer会自动下载并安装ffmpeg。 …...

RT-DETR优化策略:轻量级Backbone改进 | 高效模型 (Efficient MOdel, EMO),现代倒残差移动模块设计|ICCV2023

🚀🚀🚀本文改进:面向移动端的轻量化网络模型——EMO,它能够以相对较低的参数和 FLOPs 超越了基于 CNN/Transformer 的 SOTA 模型,支持四个版本EMO_1M, EMO_2M, EMO_5M, EMO_6M,参数量如下,相对于自带的rtdetr-l、rtdetr-x有很大提升 layersparametersgradientsEMO_1…...

一些nginx命令

1.停止nginx nginx -s quit systemctl stop nginx.service 立即停止 nginx-s stop 杀死nginx进程 killall nginx 2.启动命令 nginx systemctl start nginx.service 3.查看nginx进程 ps aux | grep nginx 4.重启nginx服务 systemctl restart nginx.service 5.重载…...

WPF自定义控件介绍

在WPF中&#xff0c;自定义控件通常是指从头开始创建一个新控件或从现有控件继承并扩展其功能。自定义控件与用户控件&#xff08;User Control&#xff09;不同&#xff0c;用户控件是通过组合其他控件来构建的&#xff0c;而自定义控件通常涉及对控件的更底层的渲染和行为进行…...

JUNIT使用和注意、以及断言的介绍使用、SpringBoot Test测试类的使用、maven配置使用junit详细介绍

参考文章&#xff1a; https://www.cnblogs.com/zhukaile/p/14514238.html&#xff0c;https://blog.csdn.net/qq_36448800/article/details/126438339 一、什么是单元测试 在平时的开发当中&#xff0c;一个项目往往包含了大量的方法&#xff0c;可能有成千上万个。如何去保…...

强化学习在文生图中的应用:Training Diffusion Models with Reinforcement Learning

论文链接:Training Diffusion Models with Reinforcement Learning项目地址:Training Diffusion Models with Reinforcement Learning官方代码:https://github.com/kvablack/ddpo-pytorch/tree/maintrl实现:https://huggingface.co/docs/trl/ddpo_trainer🤗关注公众号 fu…...

【C语言】数组下标为啥从0开始?下标越界访问一定报错吗?

本篇文章目录 0. 相关文章1. 下标从0开始问题2. 数组下标越界不报错问题 0. 相关文章 指针与指针变量数组名不是首元素地址的的2个例外拨开指针和数组名之间的迷雾 1. 下标从0开始问题 原因是&#xff1a;数组下标访问本质是“指针解引用操作”&#xff0c;而指针又是地址&am…...

机器学习-搜索技术:从技术发展到应用实战的全面指南

在本文中&#xff0c;我们全面探讨了人工智能中搜索技术的发展&#xff0c;从基础算法如DFS和BFS&#xff0c;到高级搜索技术如CSP和优化问题的解决方案&#xff0c;进而探索了机器学习与搜索的融合&#xff0c;最后展望了未来的趋势和挑战&#xff0c;提供了对AI搜索技术深刻的…...

Axelar、J.P.Morgan Onyx、Apollo 完成概念验证,向跨区块链自动化投资领域探索

J.P.Morgan Onyx、Apollo、Axelar、Oasis Pro 以及 Provenance Block Chain 展开合作&#xff0c;共同进行互操作性概念验证&#xff08;Proof-of-Concept&#xff0c;PoC)。 新加坡 — Axelar Inc.、Oasis Pro 、Provenance Blockchain 与 J.P.Morgan Onyx 以及 Apollo 通过新…...

wpf devexpress添加TreeListControl到项目

此教程示范如何添加TreeListControl到项目和绑定控件自引用数据源&#xff1a; 添加数据模型 绑定tree&#xff0c;并添加如下字段到数据源对象&#xff1a; Key字段包含唯一值索引节点 Parent字段包含父索引节点 添加数据模型&#xff08;Employee和Staff类&#xff09;到…...

WPF创建自定义控件编译通过但是找不到资源

报错&#xff1a; 原因: 路径写错了&#xff1a; 不是这样&#xff1a; Source"pack://application:,,,/Controls/Styles/xTabControl.xaml" 而是这样&#xff1a; Source"pack://application:,,,/项目名;component/Controls/Styles/xTabControl.xaml …...

PHP 中传值与传引用的区别,什么时候传值什么时候传引用?

传值&#xff1a;当使用传值的方式时&#xff0c;函数或方法会创建原始变量的一个副本&#xff0c;并将该副本传递给函数或方法。在函数或方法内部&#xff0c;对副本的任何修改都不会影响到原始变量。当函数或方法执行完毕后&#xff0c;副本被销毁&#xff0c;不再使用。 传引…...

es安装方式

es安装方式 1.下载镜像的方式 分词器 kibana和es和容器互通的方式 docker network create es-net开始拉去镜像的方式 docker pull kibana:7.12.1运行镜像的方式 docker run -d \--name es \-e "ES_JAVA_OPTS-Xms512m -Xmx512m" \-e "discovery.typesingle-…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

Linux nano命令的基本使用

参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时&#xff0c;显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

Chrome 浏览器前端与客户端双向通信实战

Chrome 前端&#xff08;即页面 JS / Web UI&#xff09;与客户端&#xff08;C 后端&#xff09;的交互机制&#xff0c;是 Chromium 架构中非常核心的一环。下面我将按常见场景&#xff0c;从通道、流程、技术栈几个角度做一套完整的分析&#xff0c;特别适合你这种在分析和改…...