网关与auth微服务缓存打通

news2024/11/15 9:29:00

文章目录

  • 🌞 Sun Frame:SpringBoot 的轻量级开发框架(个人开源项目推荐)
    • 🌟 亮点功能
    • 📦 spring cloud模块概览
      • 常用工具
    • 🔗 更多信息
    • 1.缓存一致性问题
        • 1、更新了数据库,再更新缓存
        • 2、更新缓存,更新数据库
        • 3、先删除缓存,再更新数据库
        • 扩展思路
          • 1、消息队列补偿
          • 2、canal
    • 2.auth微服务在用户注册时,将当前用户的角色和权限都放到redis里
        • 1.sun-club-auth-domain
          • 1.pom.xml 引入依赖
          • 2.RedisConfig.java
          • 3.RedisUtil.java
          • 4.AuthUserDomainServiceImpl.java register方法新增逻辑
        • 2.sun-club-auth-infra
          • 1.AuthRolePermissionService.java
          • 2.AuthRolePermissionServiceImpl.java
          • 3.AuthPermissionService.java
          • 4.AuthPermissionServiceImpl.java
          • 5.AuthPermissionDao.java
          • 6.AuthPermissionDao.xml
        • 3.测试
    • 3.gateway鉴权时可以获取权限/角色列表
        • 1.sun-club-
          • 1.复制两个entity到这个模块
            • 1.AuthPermission.java
            • 2.AuthRole.java
          • 2.StpInterfaceImpl.java 根据loginId和前缀获取权限/角色列表
        • 3.sun-club-auth-application-controller
          • 在UserController.java可以设置用户登录时的token对应的loginId,这里设置成鸡翅
        • 3.测试
          • 1.首先登录,生成token和loginId(这里写死为鸡翅)
          • 2.然后携带token进行登录,后端就可以找到对应的loginId,在验证登录成功之后会进行鉴权
            • 1.在gateway的SaTokenConfigure.java可以配置鉴权的类型
            • 2.下面是分别两种方式,从redis中取出的角色列表和权限列表

🌞 Sun Frame:SpringBoot 的轻量级开发框架(个人开源项目推荐)

Sun Frame Banner

轻松高效的现代化开发体验

Sun Frame 是我个人开源的一款基于 SpringBoot 的轻量级框架,专为中小型企业设计。它提供了一种快速、简单且易于扩展的开发方式。

我们的开发文档记录了整个项目从0到1的任何细节,实属不易,请给我们一个Star!🌟
您的支持是我们持续改进的动力。

🌟 亮点功能

  • 组件化开发:灵活选择,简化流程。
  • 高性能:通过异步日志和 Redis 缓存提升性能。
  • 易扩展:支持多种数据库和消息队列。

📦 spring cloud模块概览

  • Nacos 服务:高效的服务注册与发现。
  • Feign 远程调用:简化服务间通信。
  • 强大网关:路由与限流。

常用工具

  • 日志管理:异步处理与链路追踪。
  • Redis 集成:支持分布式锁与缓存。
  • Swagger 文档:便捷的 API 入口。
  • 测试支持:SpringBoot-Test 集成。
  • EasyCode:自定义EasyCode模板引擎,一键生成CRUD。

🔗 更多信息

  • 开源地址:Gitee Sun Frame
  • 详细文档:语雀文档
    在这里插入图片描述

1.缓存一致性问题

1、更新了数据库,再更新缓存

假设数据库更新成功,缓存更新失败,在缓存失效和过期的时候,读取到的都是老数据缓存。

2、更新缓存,更新数据库

缓存更新成功了,数据库更新失败,是不是读取的缓存的都是错误的。

以上两种,全都不推荐。

3、先删除缓存,再更新数据库

有一定的使用量。即使数据库更新失败。缓存也可以会刷。

存在的问题是什么?

高并发情况下!!

比如说有两个线程,一个是 A 线程,一个是 B 线程。

A 线程把数据删了,正在更新数据库,这个时候 B 线程来了,发现缓存没了,又查数据,又放入缓存。缓存里面存的就一直是老数据了。

延迟双删。更新完数据库之后,再删一次。

扩展思路
1、消息队列补偿

删除失败的缓存,作为消息打入 mq,mq 消费者进行监听,再次进行重试刷缓存。

2、canal

监听数据库的变化,做一个公共服务,专门来对接缓存刷新。优点业务解耦,业务太多冗余代码复杂度。

2.auth微服务在用户注册时,将当前用户的角色和权限都放到redis里

1.sun-club-auth-domain
1.pom.xml 引入依赖
        <!-- 序列化 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.12.7</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.7</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.6</version>
        </dependency>
2.RedisConfig.java
package com.sunxiansheng.auth.domain.redis;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Description: 原生 redis 的 template 的序列化器会产生乱码问题,重写改为 jackson
 * @Author sun
 * @Create 2024/6/5 14:16
 * @Version 1.0
 */
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(redisSerializer);
        redisTemplate.setHashKeySerializer(redisSerializer);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());
        return redisTemplate;
    }

    private Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
        Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        jsonRedisSerializer.setObjectMapper(objectMapper);
        return jsonRedisSerializer;
    }

}
3.RedisUtil.java
package com.sunxiansheng.auth.domain.redis;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Description: RedisUtil工具类
 * @Author sun
 * @Create 2024/6/5 14:17
 * @Version 1.0
 */
@Component
@Slf4j
public class RedisUtil {

    @Resource
    private RedisTemplate redisTemplate;

    private static final String CACHE_KEY_SEPARATOR = ".";

    /**
     * 构建缓存key
     */
    public String buildKey(String... strObjs) {
        return Stream.of(strObjs).collect(Collectors.joining(CACHE_KEY_SEPARATOR));
    }

    /**
     * 是否存在key
     */
    public boolean exist(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 删除key
     */
    public boolean del(String key) {
        return redisTemplate.delete(key);
    }

    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }

    public boolean setNx(String key, String value, Long time, TimeUnit timeUnit) {
        return redisTemplate.opsForValue().setIfAbsent(key, value, time, timeUnit);
    }

    public String get(String key) {
        return (String) redisTemplate.opsForValue().get(key);
    }

    public Boolean zAdd(String key, String value, Long score) {
        return redisTemplate.opsForZSet().add(key, value, Double.valueOf(String.valueOf(score)));
    }

    public Long countZset(String key) {
        return redisTemplate.opsForZSet().size(key);
    }

    public Set<String> rangeZset(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }

    public Long removeZset(String key, Object value) {
        return redisTemplate.opsForZSet().remove(key, value);
    }

    public void removeZsetList(String key, Set<String> value) {
        value.stream().forEach((val) -> redisTemplate.opsForZSet().remove(key, val));
    }

    public Double score(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }

    public Set<String> rangeByScore(String key, long start, long end) {
        return redisTemplate.opsForZSet().rangeByScore(key, Double.valueOf(String.valueOf(start)), Double.valueOf(String.valueOf(end)));
    }

    public Object addScore(String key, Object obj, double score) {
        return redisTemplate.opsForZSet().incrementScore(key, obj, score);
    }

    public Object rank(String key, Object obj) {
        return redisTemplate.opsForZSet().rank(key, obj);
    }


}
4.AuthUserDomainServiceImpl.java register方法新增逻辑
// 要把当前用户的角色和权限都放到redis里

// 1、存储角色
// 构建一个角色的key
String roleKey = redisUtil.buildKey(authRolePrefix, authUser.getUserName());
// 构建一个角色列表作为value
List<AuthRole> roleList = new ArrayList<>();
// 向角色列表中添加角色
roleList.add(authRole);
// 将角色列表序列化并放到redis中
redisUtil.set(roleKey, new Gson().toJson(roleList));

// 2、存储权限
// 查询当前用户拥有的权限
// 1.注册的时候,用户只有一个角色,先根据这个角色id去角色权限关联表中查询多条关联的记录
AuthRolePermission authRolePermission = new AuthRolePermission();
// 设置逻辑删除
authRolePermission.setIsDeleted(IsDeleteFlagEnum.UN_DELETED.getCode());
// 设置角色id
authRolePermission.setRoleId(roleId);
// 查询出一个角色权限关联的列表
List<AuthRolePermission> authRolePermissionList = authRolePermissionService.queryByCondition(authRolePermission);
// 2.根据查询出来的列表,得到所有的权限id
List<Long> permissionIdList = authRolePermissionList.stream().map(AuthRolePermission::getPermissionId).collect(Collectors.toList());
// 根据权限id,查询所有的权限,就是根据ids批量查询
List<AuthPermission> permissionList = authPermissionService.queryByIds(permissionIdList);
// 将权限列表序列化并放到redis中
String permissionKey = redisUtil.buildKey(authPermissionPrefix, authUser.getUserName());
redisUtil.set(permissionKey, new Gson().toJson(permissionList));

image-20240607164025000

2.sun-club-auth-infra
1.AuthRolePermissionService.java
/**
 * 根据角色id查询角色权限关联表
 * @param authRolePermission
 * @return
 */
List<AuthRolePermission> queryByCondition(AuthRolePermission authRolePermission);
2.AuthRolePermissionServiceImpl.java
/**
 * 根据角色id查询角色权限关联表
 * @param authRolePermission
 * @return
 */
@Override
public List<AuthRolePermission> queryByCondition(AuthRolePermission authRolePermission) {
    return this.authRolePermissionDao.queryAllByLimit(authRolePermission);
}
3.AuthPermissionService.java
/**
 * 通过ids查询数据
 * @param ids
 * @return
 */
public List<AuthPermission> queryByIds(List<Long> ids);
4.AuthPermissionServiceImpl.java
/**
 * 通过ids查询数据
 * @param ids
 * @return
 */
public List<AuthPermission> queryByIds(List<Long> ids) {
    return authPermissionDao.queryByIds(ids);
}
5.AuthPermissionDao.java
/**
 * 通过ID批量查询
 *
 * @param ids
 * @return
 */
List<AuthPermission> queryByIds(@Param("ids") List<Long> ids);
6.AuthPermissionDao.xml
<select id="queryByIds" resultMap="AuthPermissionMap">
    select id, name, parent_id, type, menu_url, status, `show`, icon, permission_key, created_by, created_time,
    update_by, update_time, is_deleted
    from auth_permission
    where id in
    <foreach collection="ids" item="id" open="(" close=")" separator=",">
        #{id}
    </foreach>
</select>
3.测试

image-20240607164355210

image-20240607164348873

3.gateway鉴权时可以获取权限/角色列表

1.sun-club-
1.复制两个entity到这个模块
1.AuthPermission.java
package com.sunxiansheng.club.gateway.entity;

import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
 * (AuthPermission)实体类
 *
 * @author makejava
 * @since 2024-06-06 17:16:58
 */
@Data
public class AuthPermission implements Serializable {

    private Long id;
    /**
     * 权限名称
     */
    private String name;
    /**
     * 父id
     */
    private Long parentId;
    /**
     * 权限类型 0菜单 1操作
     */
    private Integer type;
    /**
     * 菜单路由
     */
    private String menuUrl;
    /**
     * 状态 0启用 1禁用
     */
    private Integer status;
    /**
     * 展示状态 0展示 1隐藏
     */
    private Integer show;
    /**
     * 图标
     */
    private String icon;
    /**
     * 权限唯一标识
     */
    private String permissionKey;
    /**
     * 创建人
     */
    private String createdBy;
    /**
     * 创建时间
     */
    private Date createdTime;
    /**
     * 更新人
     */
    private String updateBy;
    /**
     * 更新时间
     */
    private Date updateTime;
    /**
     * 是否被删除 0为删除 1已删除
     */
    private Integer isDeleted;

}
2.AuthRole.java
package com.sunxiansheng.club.gateway.entity;

import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
 * (AuthRole)实体类
 *
 * @author makejava
 * @since 2024-06-06 14:30:38
 */
@Data
public class AuthRole implements Serializable {

    private Long id;
    /**
     * 角色名称
     */
    private String roleName;
    /**
     * 角色唯一标识
     */
    private String roleKey;
    /**
     * 创建人
     */
    private String createdBy;
    /**
     * 创建时间
     */
    private Date createdTime;
    /**
     * 更新人
     */
    private String updateBy;
    /**
     * 更新时间
     */
    private Date updateTime;
    /**
     * 是否被删除 0未删除 1已删除
     */
    private Integer isDeleted;


}
2.StpInterfaceImpl.java 根据loginId和前缀获取权限/角色列表
/**
 * 根据loginId和前缀获取权限/角色列表
 * @param loginId
 * @param prefix
 * @return
 */
private List<String> getAuth(String loginId, String prefix) {
    // 得到该用户在redis中存储的key
    String authKey = redisUtil.buildKey(prefix, loginId.toString());
    // 从redis中获取列表
    String authValue = redisUtil.get(authKey);
    // 判空
    if (StringUtils.isBlank(authValue)) {
        return Collections.emptyList();
    }
    List<String> authList = new LinkedList<>();
    // 根据前缀来决定将内容反序列化为什么形式
    if (authRolePrefix.equals(prefix)) {
        // 如果是角色列表的前缀,就反序列化为角色类型的
        List<AuthRole> authRoleList = new Gson().fromJson(authValue, new TypeToken<List<AuthRole>>() {
        }.getType());
        // 得到roleKey的列表,放到authList中
        authList = authRoleList.stream().map(AuthRole::getRoleKey).collect(Collectors.toList());
    } else if (authPermissionPrefix.equals(prefix)) {
        // 如果是权限列表,就反序列化为权限类型的
        List<AuthPermission> authPermissionList = new Gson().fromJson(authValue, new TypeToken<List<AuthPermission>>() {
        }.getType());
        // 得到permissionKey,放到authList中
        authList = authPermissionList.stream().map(AuthPermission::getPermissionKey).collect(Collectors.toList());
    }
    return authList;
}
3.sun-club-auth-application-controller
在UserController.java可以设置用户登录时的token对应的loginId,这里设置成鸡翅

image-20240607171955303

3.测试
1.首先登录,生成token和loginId(这里写死为鸡翅)

image-20240607172214503

2.然后携带token进行登录,后端就可以找到对应的loginId,在验证登录成功之后会进行鉴权
1.在gateway的SaTokenConfigure.java可以配置鉴权的类型

image-20240607172332923

2.下面是分别两种方式,从redis中取出的角色列表和权限列表

image-20240607171208574

image-20240607171451314

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1984526.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大数据-68 Kafka 高级特性 物理存储 日志存储概述

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

文献翻译软件哪个好?6个工具让你轻松看懂外语文献

在医学研究领域&#xff0c;文献翻译软件扮演着至关重要的角色。 医学论文、研究报告和临床指南等&#xff0c;往往包含了大量的专业术语和复杂的句子结构&#xff0c;对于非母语者来说&#xff0c;理解这些内容无疑是一项挑战。 幸运的是&#xff0c;随着技术的发展&#xf…

巴黎奥运会8K转播科技为国产品牌自主研发设计

这个夏天&#xff0c;顶流是属于巴黎奥运会中国队的。 20枚金牌、15枚银牌、12枚铜牌......这个数字正随着赛事推进而不停在增加。赛场之上&#xff0c;中国健儿奋力拼搏、捷报频传&#xff0c;令人热血沸腾&#xff1b;赛场之外&#xff0c;另一支来自中国企业的“奥运选手”…

Linux OS:基于阻塞队列的生产者消费者模型

Linux OS&#xff1a;基于阻塞队列的生产者消费者模型 前言一、阻塞队列的大致框架二、生产者向阻塞队列中生产数据三、消费者获取阻塞队列中数据四、总体生产和消费思路及测试代码4.1 单生产单消费4.2 多生产多消费 五、所以代码 前言 阻塞队列是一种常用于实现生产者消费者模…

大模型微调技术PEFT

1. 横向对比 总体概览&#xff1a; 方法Transformer 中如何应用特点Prompt Tuning在输入到Transformer的input文本中添加Prompthard 模式prompt CO-STARP-tuning在输入到Transformer的embedding 层添加Promptsoft 模式 promptAdapter Tuning在 Transformer 中间层插入 Adapt…

MATLAB代码|蚁群算法|计算二元函数最大值

总述 蚁群方法求解二元函数的最大值。蚂蚁群先随机分布在定义域内&#xff0c;如下&#xff1a; 计算结束后&#xff0c;得到如下的分布&#xff0c;再计算分布均值&#xff0c;得到此时的自变量取值&#xff0c;因为是二元函数&#xff0c;所以有两个自变量&#xff0c;带入…

Qt 实战(9)窗体 | 9.1、QWidget

文章目录 一、QWidget1、定义与概念2、继承体系3、常用特性3.1、事件处理3.2、布局管理3.3、子控件与父子关系3.4、右键菜单 4、注意事项5、总结 前言&#xff1a; 在Qt这一强大的跨平台C图形用户界面应用程序开发框架中&#xff0c;QWidget扮演着至关重要的角色。作为所有用户…

【日记】为啥家族原发性高血压的人还喜欢喝酒啊……(442 字)

正文 今天跟人吵了一下午架&#xff0c;因为有一张报表换了新表&#xff0c;所有人都不知道怎么报。上级行一个想法&#xff0c;我一个想法。吵完都发现对方说得有道理&#xff0c;于是决定明天问省分行。难绷。草台班子。 鱼儿说他最近喜欢上了喝酒。我们劝他的同时&#xff0…

太阳光度计CE-318数据处理

太阳光度计CE-318数据处理 备注&#xff1a;处理公式 在我国近海&#xff0c;α的值在0到3之间&#xff0c;所以他们相对误差最大不超过25%&#xff0c;而通过查阅相关资料&#xff0c;北京地区α的值可以近似的取1.665。 大气是不断运动的&#xff0c;气溶胶在短时间内也可…

【网络安全】https协议的加密方案避免中间人攻击(MITM攻击)导致的数据泄露风险

目录 引言 概念准备 中间人 加密 数据摘要 && 数据指纹 数字签名 密钥加密 中间人攻击 CA证书 https加密的解决方案 个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 引言 http在应用层协议中是明文传输协议&#xff0c;它是通信双方传输数据时的一种约定。【…

超声波便携式气象站:精准监测,随身携带

在追求高效与便捷的今天&#xff0c;超声波便携式气象站以其独特魅力脱颖而出&#xff0c;成为众多行业及户外爱好者的理想选择。超声波便携式气象站采用多采集装置一体式设计&#xff0c;不仅实现了体积的小型化与重量的轻量化&#xff0c;更便于用户随身携带&#xff0c;随时…

期权杠杆与期货杠杆的区别是什么?

期权与股指期货在杠杆性上展现出截然不同的特性&#xff0c;这些特性对投资者的策略选择具有深远影响。首先&#xff0c;股指期货采用保证金制度&#xff0c;其杠杆比例是恒定的&#xff0c;无论市场如何波动&#xff0c;投资者在月初设定的十倍杠杆到月尾仍保持不变。相比之下…

STM32常见的下载方式有三种

经过对比&#xff0c;推荐使用 SWD下载&#xff0c;只需要一个仿真器&#xff08;如jLINK、ST LINK、 CMSIS DAP 等&#xff09;&#xff0c;比较方便。 不推荐使用串口下载&#xff08;速度慢、无法仿真和调试&#xff09;和 JTAG 下载&#xff08;占用 IO 多&#xff09;。

Java语言程序设计基础篇_编程练习题*16.7 (设置时钟的时间)

*16.7 (设置时钟的时间) 编写一个程序&#xff0c;显示一个时钟&#xff0c;并通过在三个文本域中输入小时、分钟和秒 钟来设置时钟的时间&#xff0c;如图16-38b 所示。使用程序清单14-21的ClockPane改变时钟大小使其居于面板中央 习题思路 实例化一个ClockPane(在程序清单1…

【网络安全渗透测试零基础入门必知必会】之什么是文件包含漏洞分类(非常详细)零基础入门到精通,收藏这一篇就够了

一、前言 这是大白给粉丝盆友们整理的网络安全渗透测试入门阶段文件包含渗透与防御第1篇。 本文主要讲解什么是文件包含漏洞、本地文件包含漏洞 喜欢的朋友们&#xff0c;记得给大白点赞支持和收藏一下&#xff0c;关注我&#xff0c;学习黑客技术。 一、什么是文件包含漏洞…

the request was rejected because no multipart boundary was found

文章目录 1. 需求描述2. 报错信息3. 探索过程1. 使用postman 排除后端错误2. 搜索网上的解决方法3. 解决方法 1. 需求描述 想要在前端上传一个PDF 发票&#xff0c;经过后端解析PDF之后&#xff0c;将想要的值自动回填到对应的输入框中 2. 报错信息 org.apache.tomcat.util.…

VSCode远程调试Linux程序

VS 安装CodeRunner插件 菜单→添加配置→lunch.json中设置如下&#xff1a; program填入要调试的文件 {"version": "0.2.0","configurations": [{"name": "gdb renderPng","type": "cppdbg","re…

为什么某央企可以抓到红队攻击,而你不行?

国家HVV行动从2016年到2024年已经是第9年了&#xff0c;HVV行动目的就是保卫国家关键基础设施的网络安全行动&#xff0c;更是一场实战化的网络攻击与防御实战&#xff0c;这些年来红队攻击手段层出不穷&#xff0c;最为典型的就是 0/N Day、弱口令、社工钓鱼等&#xff0c;也极…

北京青蓝智慧科技ITSS服务经理:长安链ChainBridge“链桥”问世 加速国家级区块链网络互联互通

8月5日&#xff0c;据国家区块链技术创新中心消息&#xff0c;我国首个完全自主控制的区块链软硬件技术系统——长安链&#xff0c;正式推出了全场景技术平台ChainBridge“链桥”。 此平台能够支持所有异构和同构的区块链进行协作&#xff0c;满足跨领域、跨地域、跨行业及跨层…

用Java手写jvm之模拟方法调用指令invokexxx和方法返回指令xreturn

写在前面 源码 。 本文一起看下方法调用相关的指令invokexxx以及方法返回&#xff08;栈帧弹出线程栈&#xff09;相关的指令xReturn 。 1&#xff1a;正文 因为invokexxx指令和普通的指令不同&#xff0c;会创建一个新的栈帧&#xff0c;并压倒操作数栈中&#xff0c;所以我…