shiro学习五:使用springboot整合shiro。在前面学习四的基础上,增加shiro的缓存机制,源码讲解:认证缓存、授权缓存。
文章目录
- 前言
- 1. 直接上代码最后在讲解
- 1.1 新增的pom依赖
- 1.2 RedisCache.java
- 1.3 RedisCacheManager.java
- 1.4 jwt的三个类
- 1.5 ShiroConfig.java新增Bean
- 2. 源码讲解。
- 2.1 shiro 缓存的代码流程。
- 2.2 缓存流程
- 2.2.1 认证和授权简述
- 2.2.2 AuthenticatingRealm.getAuthenticationInfo() 认证缓存。
- 2.2.3 AuthorizingRealm.getAuthorizationInfo() 授权缓存。
- 2.3 redis缓存的设置入口(个人认为是重点)
- 3. 问题解决
- 3.1 授权的流程是怎么做的
- 3.2 @RequiresPermissions("sys:user:list")是如何工作的
- **1. 基本原理**
- **2. 具体流程**
- **1. 用户登录阶段**
- **2. 调用 `listUsers` 方法时**
- **3. 匹配机制**
前言
-
shiro学习一:了解shiro,学习执行shiro的流程。使用springboot的测试模块学习shiro单应用(demo 6个)
-
shiro学习二:shiro的加密认证详解,加盐与不加盐两个版本。
-
shiro学习三:shiro的源码分析
-
密码专辑:对密码加盐加密,对密码进行md5加密,封装成密码工具类
-
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
-
代码所在地:https://github.com/fengfanli/springboot-shiro 记得给个星哦。
-
本文详细介绍了在Java Spring Boot项目中使用Apache Shiro进行权限管理的实现方式。通过整合Redis作为缓存管理器,实现了用户权限的缓存,提高了权限验证的效率。文章还解析了@RequiresPermissions注解的工作原理,说明了如何通过AOP机制拦截方法,并进行权限字符串的匹配校验,确保只有具备相应权限的用户能够访问受保护的方法。整体上,文章为Shiro在Spring Boot项目中的应用提供了全面的技术指导。
1. 直接上代码最后在讲解
1.1 新增的pom依赖
<!--JWT--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>
1.2 RedisCache.java
package com.feng.shiro;import com.alibaba.fastjson.JSON;
import com.feng.constant.Constant;
import com.feng.jwt.JwtTokenUtil;
import com.feng.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.util.CollectionUtils;import java.util.*;
import java.util.concurrent.TimeUnit;/*** @ClassName: RedisCache* @Description: 缓存工具类* @createTime: 2020/2/9 20:42* @Author: 冯凡利* @UpdateUser: 冯凡利* @Version: 0.0.1*//*** 这里的 RedisUtils 不能注入** @param <K>* @param <V>*/
@Slf4j
public class RedisCache<K, V> implements Cache<K, V> {private final static String PREFIX = "shiro-cache:";private String cacheKey;private long expire = 24; // 24 小时private RedisUtil redisUtil;public RedisCache(String name, RedisUtil redisUtil) {
// this.cacheKey=PREFIX+name+":";this.cacheKey = Constant.IDENTIFY_CACHE_KEY;this.redisUtil = redisUtil;}/*** 根据 key 值 获取 权限信息** @param key jwt* @return* @throws CacheException*/@Overridepublic V get(K key) throws CacheException {log.info("Shiro 从缓存中获取数据 KEY 值[{}]", key);if (key == null) {return null;}try {String redisCacheKey = getRedisCacheKey(key);Object rawValue = redisUtil.get(redisCacheKey); // 根据key 获取 数据if (rawValue == null) {return null;}SimpleAuthorizationInfo info = JSON.parseObject(rawValue.toString(), SimpleAuthorizationInfo.class);V value = (V) info;return value;} catch (Exception e) {throw new CacheException(e);}}/*** 存值** @param key jwt* @param value* @return* @throws CacheException*/@Overridepublic V put(K key, V value) throws CacheException {log.info("put key [{}]", key);if (key == null) {log.warn("Saving a null key is meaningless, return value directly without call Redis.");return value;}try {String redisCacheKey = getRedisCacheKey(key); // cacheKey + userIdredisUtil.set(redisCacheKey, value != null ? value : null, expire, TimeUnit.HOURS);return value;} catch (Exception e) {throw new CacheException(e);}}/*** 根据 key 值 删除缓存的值** @param key* @return* @throws CacheException*/@Overridepublic V remove(K key) throws CacheException {log.info("remove key [{}]", key);if (key == null) {return null;}try {String redisCacheKey = getRedisCacheKey(key);Object rawValue = redisUtil.get(redisCacheKey);V previous = (V) rawValue;redisUtil.delete(redisCacheKey);return previous;} catch (Exception e) {throw new CacheException(e);}}/*** 清除 所有的值** @throws CacheException*/@Overridepublic void clear() throws CacheException {log.debug("clear cache");Set<String> keys = null;try {keys = redisUtil.keys(this.cacheKey + "*");} catch (Exception e) {log.error("get keys error", e);}if (keys == null || keys.size() == 0) {return;}for (String key : keys) {redisUtil.delete(key);}}/*** 获取 redis 所存的 缓存数的大小** @return*/@Overridepublic int size() {int result = 0;try {result = redisUtil.keys(this.cacheKey + "*").size();} catch (Exception e) {log.error("get keys error", e);}return result;}/*** 获取key值** @return*/@SuppressWarnings("unchecked")@Overridepublic Set<K> keys() {Set<String> keys = null;try {keys = redisUtil.keys(this.cacheKey + "*");} catch (Exception e) {log.error("get keys error", e);return Collections.emptySet();}if (CollectionUtils.isEmpty(keys)) {return Collections.emptySet();}Set<K> convertedKeys = new HashSet<>();for (String key : keys) {try {convertedKeys.add((K) key);} catch (Exception e) {log.error("deserialize keys error", e);}}return convertedKeys;}/*** 获取 value值** @return*/@Overridepublic Collection<V> values() {Set<String> keys = null;try {keys = redisUtil.keys(this.cacheKey + "*");} catch (Exception e) {log.error("get values error", e);return Collections.emptySet();}if (CollectionUtils.isEmpty(keys)) {return Collections.emptySet();}List<V> values = new ArrayList<V>(keys.size());for (String key : keys) {V value = null;try {value = (V) redisUtil.get(key);} catch (Exception e) {log.error("deserialize values= error", e);}if (value != null) {values.add(value);}}return Collections.unmodifiableList(values);}/*** 获取 redis 中的 缓存 key ,很重要,** @param key* @return*/private String getRedisCacheKey(K key) {if (null == key) {return null;} else {return this.cacheKey + JwtTokenUtil.getUserId(key.toString());}}
}
1.3 RedisCacheManager.java
package com.feng.shiro;import com.feng.utils.RedisUtil;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.springframework.beans.factory.annotation.Autowired;/*** @ClassName: RedisCacheManager* @Description: 描述* @createTime: 2020/2/9 21:14* @Author: 冯凡利* @UpdateUser: 冯凡利* @Version: 0.0.1*/
public class RedisCacheManager implements CacheManager {@Autowiredprivate RedisUtil redisUtil;@Overridepublic <K, V> Cache<K, V> getCache(String s) throws CacheException {return new RedisCache<>(s, redisUtil);}
}
1.4 jwt的三个类
我不贴了,不是核心代码,可直接看GitHub。
四个部分:
- JwtTokenUtil.java:jwt的工具类
- JwtPropertiesConfig.java:application.properties 中的jwt的属性读取类
- InitializerJwtPropertiesConfig.java:将 JwtPropertiesConfig.java进行注入。
- application.properties 中的jwt的自定义属性配置。
1.5 ShiroConfig.java新增Bean
新增了redisCacheManager bean,然后再 getShiroRealm() 函数中 ,通过 Realm(ShiroRealm) 进行设置 缓存。
/*** shiro 的 缓存管理器* 需要在 ShiroRealm bean 中进行设置* @return*/@Bean(name = "redisCacheManager")public RedisCacheManager redisCacheManager() {return new RedisCacheManager();}/*** 登录的 认证域** @param hashedCredentialsMatcher* @return*/@Bean(name = "shiroRealm")public ShiroRealm getShiroRealm(@Qualifier("shiroHashedCredentialsMatcher") HashedCredentialsMatcher hashedCredentialsMatcher,@Qualifier("redisCacheManager") RedisCacheManager redisCacheManager) {ShiroRealm shiroRealm = new ShiroRealm();// 自定义 处理 token 过滤shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);// 自定义 缓存管理器shiroRealm.setCacheManager(redisCacheManager);return shiroRealm;}
shiro 新增的 shiro 缓存 代码到此结束。
其他代码没有展示,是因为 shiro 缓存的代码只有这些。
2. 源码讲解。
2.1 shiro 缓存的代码流程。
- 首先编写 RedisCache.java:实现 Cache 接口,再这里重写redis的一些操作函数
- 编写RedisCacheManager.java 类:实现 CacheManager 接口,将RedisCache.java 类加入到 缓存管理中。
- 编写ShiroConfig.java 类:将RedisCacheManager注入生Bean,然后再Realm(本案例是ShiroRealm)中设置
缓存管理
,同时也是设置资格匹配
的地方。
2.2 缓存流程
shiro的核心是再 数据域中,也就是Realm,本案例的是ShiroRealm,继承 AuthorizingRealm
授权域, 授权域 继承了 AuthenticatingRealm
认证域,认证域 又继承了 CachingRealm
缓存域。这三个类是shiro 认证、授权、缓存 三个最重要的类。
2.2.1 认证和授权简述
继承了 AuthorizingRealm
授权域,就要重写两个最重要的方法
-
认证函数 doGetAuthenticationInfo(),既然认证,重写的就是
AuthenticatingRealm
认证域 中的函数。在函数getAuthenticationInfo()
中。源码截图如下:
-
授权函数 doGetAuthorizationInfo(),既然授权,重写的就是
AuthorizingRealm
授权域 中的函数。在函数getAuthorizationInfo()
中,源码截图如下:
在 Shiro 中,认证缓存(authentication cache) 和 授权缓存(authorization cache) 是两个不同的缓存,它们用于不同的目的:
- 认证缓存 (authenticationCache):用于缓存用户的认证信息(如,用户是否存在,凭证是否正确等)。这通常是一个比较频繁更新的缓存,因为用户登录的凭证(如密码)是动态变化的,可能会变得无效。
- 授权缓存 (authorizationCache):用于缓存用户的授权信息(如,用户的角色、权限等)。授权信息通常不太会频繁变动,因此可以缓存很长时间。它一般是用于存储用户的角色和权限信息,以减少每次请求时对数据库或外部系统的查询。
2.2.2 AuthenticatingRealm.getAuthenticationInfo() 认证缓存。
该函数的第一句就是:AuthenticationInfo info = this.getCachedAuthenticationInfo(token);
该函数就是从缓存中获取认证信息。
先说答案:shiro不推荐、默认不支持 认证从缓存中获取。
代码中可以看到 在AuthenticatingRealm类属性 this.authenticationCachingEnabled = false;
在构造函数中 默认为 false,不开启缓存。
究其原因:用于缓存用户的认证信息(如,用户是否存在,凭证是否正确等)。这通常是一个比较频繁更新的缓存,因为用户登录的凭证(如密码)是动态变化的,可能会变得无效。
所以不推荐缓存,通过debug,也可以看到。
2.2.3 AuthorizingRealm.getAuthorizationInfo() 授权缓存。
授权缓存 在shiro 中是支持的。因为 AuthorizingRealm 授权域类中的 类属性 this.authorizationCachingEnabled = true;
在构造函数中默认为true的。
getAuthorizationInfo() 函数就是从缓存中进行获取的,没有获取到在 进入 到 doGetAuthorizationInfo() 函数中进行获取、授权。
可以通过debug的方式一点点去查看。
如果没有设置redis作为缓存管理,默认可以配置缓存,但是shiro并没有帮我指定缓存,估计是怕默认指定之后怕内存使用过高,需要自行设定,接下来讲解。
2.3 redis缓存的设置入口(个人认为是重点)
前边讲的都是逻辑流程,如何从缓存中获取。我在读源码的时候我就很好奇,那设置redis作为缓存的地方在哪里呢?默认是用内存作为缓存的设置又在哪里呢?抱着这些疑问继续走。
答案:入口就在ShiroConfig中 Realm 的配置中。
ShiroRealm shiroRealm = new ShiroRealm();// 自定义 处理 token 过滤shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);// 自定义 缓存管理器shiroRealm.setCacheManager(redisCacheManager);
new ShiroRealm()时 ;会执行 AuthorizingRealm、AuthenticatingRealm的构造函数。构造函数中会设置 缓存启动情况、缓存的配置情况。
-
其中
AuthenticatingRealm
认证域的构造函数中,会默认指定SimpleCredentialsMatcher
作为 凭证匹配器 的 类,shiro还提供了HashedCredentialsMatcher
作为 凭证匹配器 类(具体有几个,看CredentialsMatcher
类的实现类有几个即可,下图所示)。后面我们自定义了 凭证匹配器 类CustomHashedCredentialsMatcher
,其中的doCredentialsMatch()
函数就是默认 认证的最核心的函数。
-
AuthorizingRealm 授权域的构造函数中,会默认指定 缓存管理(在CachingRealm 类中)、凭证匹配器(认证域类中) 的初始化,默认都是null。
-
shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);
:调用 认证域类 中的方法设置凭证匹配器
。 -
shiroRealm.setCacheManager(redisCacheManager);
:调用缓存域类
中的方法设置缓存管理器 以及 实现缓存的方式(第二行语句),该方法是个接口,有两个实现方法:一个是 认证域,一个是授权域。往下走。 -
这里就是 授权域 中的
afterCacheManagerSet()
函数,- 第一个函数调用父类也就是认证域上的函数,会继续调用
this.getAvailableAuthenticationCache()
。认证域前边说了,认证缓存shiro默认是关闭的,debug走一边流程就明白了;重点就是授权域了。 - 在第二个函数上,先判断是否有缓存,为null,然后再看 授权域 是否支持 授权缓存(默认支持,前边说了);然后进入懒加载缓存(下面第二张图);到获取缓存的地方,缓存管理不为null,是redis的,接着从158行,获取缓存,158行往下debug,就是上面的1.3小节的
RedisCacheManager.java
,将redis返回。
- 第一个函数调用父类也就是认证域上的函数,会继续调用
-
我又来问题了,如果没有redis,走的是哪个呢?首先我也不知道,得先把redis缓存管理给去掉。看看使用的哪个。CacheManager.java 是接口,其实现类截图如下,第一个是我实现的,其余三个都是shiro自带的(第二个应该不是,看源码)。
-
通过源码查看,shiro并没有默认使用缓存配置,仅仅是打开了缓存配置,可以供我们使用,如果想用使用缓存需要自行配置缓存,想上面配置 redis一样,将下面的第二个或者第四个配置到缓存管理中去。
到这里为止源码讲解完啦。
3. 问题解决
3.1 授权的流程是怎么做的
这个需要有背景的。
我基于这个项目来说一下
前端 LayUI+thymeleaf
后端使用springboot、shiro作为权限管理。
根据请求 /api/users,进行举例。
前端发请求后,会先认证,此处略。
然后会先从前端标签获取 权限字符串 sys:user:list,这里使用到了 thymeleaf,然后 通过 doGetAuthorizationInfo()获取所有权限字符串和角色,然后与从前端拿到的 权限字符串 sys:user:list进行对比,若包含,这通过。否则失败
3.2 @RequiresPermissions(“sys:user:list”)是如何工作的
1. 基本原理
@RequiresPermissions("sys:user:list")
是 Apache Shiro 提供的权限控制注解,用于声明访问某个方法或类需要的特定权限。
它的工作机制如下:
-
拦截注解:
- Shiro 提供了
AuthorizationAttributeSourceAdvisor
和 AOP(切面编程)机制来拦截被注解标记的类或方法。 - 当调用被标记的方法时,Shiro 拦截并执行权限校验。
- Shiro 提供了
-
获取当前用户的权限:
- Shiro 从当前登录用户的
Subject
(主体)中获取用户的权限信息(通常是一组权限字符串)。
- Shiro 从当前登录用户的
-
权限字符串匹配:
- Shiro 将注解中的权限字符串(如
"sys:user:list"
)与当前用户的权限列表进行比对。 - 如果用户的权限列表中包含注解指定的权限字符串,则校验通过;否则抛出
AuthorizationException
,拒绝访问。
- Shiro 将注解中的权限字符串(如
2. 具体流程
假设你有以下代码:
@RequiresPermissions("sys:user:list")
public void listUsers() {// 获取用户列表逻辑
}
1. 用户登录阶段
- 用户通过登录接口认证成功后,Shiro 会根据用户身份(如用户名)从数据库或缓存中加载用户的所有权限,并存储在
Subject
中。 - 示例权限列表:
["sys:user:list", "sys:user:edit", "sys:user:delete"]
2. 调用 listUsers
方法时
- Shiro 拦截
@RequiresPermissions
注解,通过当前用户的Subject
获取权限列表。 - 调用
Subject.isPermitted("sys:user:list")
方法,具体逻辑如下:- Shiro 将注解中的权限字符串
"sys:user:list"
传递给AuthorizingRealm
的doGetAuthorizationInfo
方法。 doGetAuthorizationInfo
返回用户的权限列表。- Shiro 比较
"sys:user:list"
是否存在于用户的权限列表中:- 存在:校验通过,继续执行方法。
- 不存在:抛出异常
AuthorizationException
。
- Shiro 将注解中的权限字符串
3. 匹配机制
- 权限字符串可以使用通配符(Wildcard)进行匹配:
- 完全匹配:
"sys:user:list"
对比"sys:user:list"
,通过。 - 通配符匹配:
"sys:user:*"
对比"sys:user:list"
,通过。 - 多级通配符:
"sys:*:*"
对比"sys:user:list"
,通过。
- 完全匹配:
相关文章:

shiro学习五:使用springboot整合shiro。在前面学习四的基础上,增加shiro的缓存机制,源码讲解:认证缓存、授权缓存。
文章目录 前言1. 直接上代码最后在讲解1.1 新增的pom依赖1.2 RedisCache.java1.3 RedisCacheManager.java1.4 jwt的三个类1.5 ShiroConfig.java新增Bean 2. 源码讲解。2.1 shiro 缓存的代码流程。2.2 缓存流程2.2.1 认证和授权简述2.2.2 AuthenticatingRealm.getAuthentication…...

大数据Hadoop入门1
目录 相关资料 第一部分 1.课程内容大纲和学习目标 2.数据分析和企业数据分析方向 3.数据分析基本流程步骤 4.大数据时代 5.分布式和集群 6.Linux操作系统概述 7.VMware虚拟机概念与安装 8.centos操作系统的虚拟机导入 9.VMware虚拟机常规使用、快照 第二部分 1.课…...

《智能家居“孤岛危机”:设备孤立如何拖垮系统优化后腿》
在科技飞速发展的今天,智能家居不再是遥不可及的概念,它正逐渐走进千家万户,为我们描绘出舒适便捷的未来生活蓝图。想象一下,下班回家前,你可以通过手机远程开启空调,让室内温度恰到好处;到家时…...

DeepSeek介绍及使用ollama本地化部署DeepSeek-R1大模型
DeepSeek 中文名深度求索人工智能基础技术研究有限公司(简称“深度求索”或“DeepSeek”),成立于2023年,是一家专注于实现AGI的中国公司。 在本月初推出 DeepSeek-R1后,该公司宣称其在执行数学、编码和自然语言推理等任务时“性能可与OpenAI…...

网络安全攻防实战:从基础防护到高级对抗
📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 引言 在信息化时代,网络安全已经成为企业、政府和个人必须重视的问题。从数据泄露到勒索软件攻击,每一次…...

9【如何面对他人学习和生活中的刁难】
我们在学习的过程中,会遇到很多来自于他人的刁难与嘲讽,如果处理不好,这会大大影响我们的心情,从而影响学习的效率 我建议,如果你学习或生活中也遇到了类似的问题,不要去生气,更不要发生冲突&a…...

kafka消费者详细介绍(超级详细)
文章目录 一、Kafka 消费者与消费者组1.1 Kafka 消费者(Consumer)概述1.1.1 消费者工作流程1.1.2 消费者的关键配置 1.2 Kafka 消费者组(Consumer Group)概述1.2.1 消费者组的工作原理1.2.2 消费者组的优点1.2.3 消费者组的再均衡…...

数据结构选讲 (更新中)
参考 smWCDay7 数据结构选讲2 by yyc 。 可能会补充的: AT_cf17_final_j TreeMST 的 F2 Boruvka算法 目录 AT_cf17_final_j Tree MST AT_cf17_final_j Tree MST link 题意 给定一棵 n n n 个点的树,点有点权 w i w_i wi,边有边权。建立…...

OpenBMC:简介
通常在服务器主板上,有一个独立的微处理器,叫作BMC(Baseboard Manager Controller),用于与主机(host)进行通信,提供带外的方式查询服务器的状态和信息,并进行管理服务器。 OpenBMC是Linux Foundation的开源BMC项目&am…...

java 正则表达式匹配Matcher 类
Matcher 类 用法 在 Java 中,Matcher 类是用于匹配正则表达式的工具,而 group() 方法是 Matcher 类中的一个重要方法,用于提取匹配结果中的捕获组(captured groups)。以下是对 group() 方法的详细解释: 1.…...

【HarmonyOS之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(三)
目录 1 -> 生命周期 1.1 -> 应用生命周期 1.2 -> 页面生命周期 2 -> 资源限定与访问 2.1 -> 资源限定词 2.2 -> 资源限定词的命名要求 2.3 -> 限定词与设备状态的匹配规则 2.4 -> 引用JS模块内resources资源 3 -> 多语言支持 3.1 -> 定…...

CSS(快速入门)
欢迎大家来到我的博客~欢迎大家对我的博客提出指导,有错误的地方会改进的哦~点击这里了解更多内容 目录 一、什么是CSS?二、基本语法规范三、CSS选择器3.1 标签选择器3.2 id选择器3.3 class选择器3.4 通配符选择器3.5 复合选择器 四、常用CSS样式4.1 color4.2 font…...

使用 concurrently 实现前后端一键启动
使用 concurrently 实现前后端一键启动 本文适合: 前后端分离项目(如 React Node.js),希望通过一条命令同时启动前端和后端服务。 工具链: Node.js、npm、concurrently。 耗时: 3 分钟。 文章目录 使用 c…...

常见端口的攻击思路
端口号端口说明攻击方向21/22/69FTP/TFTP文件传输协议匿名上传/下载、嗅探、爆破2049NFS服务配置不当139Sanba服务爆破、远程代码执行389Ldap目录访问协议注入、匿名访问、弱口令22SSH远程连接爆破、SSH映射隧道搭建、文件传输23Telnet远程连接爆破、嗅探、弱口令3389RDP远程桌…...

大数据治理实战:架构、方法与最佳实践
📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 1. 引言 大数据治理是确保数据质量、合规性和安全性的重要手段,尤其在数据驱动决策和人工智能应用日益普及的背景下&…...

忘记宝塔的访问地址怎么找
在linux中安装宝塔面板后会生成网址、账号和密码 如果网址忘记了那将进不去宝塔面板该怎么办呢? bt命令 我们输入 bt 命令的时候,是在根目录里面进行操作的。 / bt 我们根据自己的需要,选择对应的数字就可以了。 bt 14 输入 14 查看面板默…...

SQL教程-基础语法
INSERT INTO 新增数据 INSERT INTO 数据表名 VALUES (值1,值2,值3,...) DELETE 删除数据 DELETE FROM 数据表名 WHERE 查询条件 UPDATE 修改数据 UPDATE 数据表名 SET 字段1 值1, 字段2值2, ... WHERE 查询条件 SELECT 查询数据 #查询数据 SELECT 字段1, 字段2, ... FROM 数…...

shell脚本批量修改文件名之方法(The Method of Batch Modifying File Names in Shell Scripts)
shell脚本批量修改文件名方法 我们可以使用Shell脚本来实现这个功能。Shell脚本是一种用于自动化任务的编程语言,它可以在Unix/Linux操作系统上运行。在这个脚本中,我们将使用一个for循环来遍历目标目录下的所有文件,并使用mv命令将每个文件…...

组合模式 - 组合模式的实现
引言 组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次结构。组合模式使得客户端可以统一地处理单个对象和组合对象,从而简化了代码的复杂性。本文将详细介绍如何在C中实…...

视频外绘技术总结:Be-Your-Outpainter、Follow-Your-Canvas、M3DDM
Diffusion Models专栏文章汇总:入门与实战 前言:视频Inpaint的技术很火,但是OutPaint却热度不高,这篇博客总结比较经典的几篇视频Outpaint技术。其实Outpaint在runway等工具上很火,可是学术界对此关注比较少,博主从这三年的顶会中找到了最具代表性的三篇论文解读。 目录 …...

【硬件测试】基于FPGA的QPSK+帧同步系统开发与硬件片内测试,包含高斯信道,误码统计,可设置SNR
目录 1.算法仿真效果 2.算法涉及理论知识概要 2.1QPSK 2.2 帧同步 3.Verilog核心程序 4.开发板使用说明和如何移植不同的开发板 5.完整算法代码文件获得 1.算法仿真效果 本文是之前写的文章 《基于FPGA的QPSK帧同步系统verilog开发,包含testbench,高斯信道,误码统计,可…...

c++面试:类定义为什么可以放到头文件中
这个问题是刚了解预编译的时候产生的疑惑。 声明是指向编译器告知某个变量、函数或类的存在及其类型,但并不分配实际的存储空间。声明的主要目的是让编译器知道如何解析程序中的符号引用。定义不仅告诉编译器实体的存在,还会为该实体分配存储空间&#…...

PythonFlask框架
文章目录 处理 Get 请求处理 POST 请求应用 app.route(/tpost, methods[POST]) def testp():json_data request.get_json()if json_data:username json_data.get(username)age json_data.get(age)return jsonify({username: username测试,age: age})从 flask 中导入了 Flask…...

Kotlin开发(六):Kotlin 数据类,密封类与枚举类
引言 想象一下,你是个 Kotlin 开发者,敲着代码忽然发现业务代码中需要一堆冗长的 POJO 类来传递数据。烦得很?别急,Kotlin 贴心的 数据类 能帮你自动生成 equals、hashCode,直接省时省力!再想想需要多种状…...

冬天适合养什么鱼?
各位鱼友们,冬天来了,是不是还在为养什么鱼而烦恼?别担心,今天就来给大家好好推荐一些适合冬天养的鱼,让你的水族箱在寒冷的冬天也能生机勃勃! 一、金鱼:冬日里的“小暖男” 金鱼绝对是冬季养鱼…...

【C++动态规划 状态压缩】2597. 美丽子集的数目|2033
本文涉及知识点 C动态规划 LeetCode2597. 美丽子集的数目 给你一个由正整数组成的数组 nums 和一个 正 整数 k 。 如果 nums 的子集中,任意两个整数的绝对差均不等于 k ,则认为该子数组是一个 美丽 子集。 返回数组 nums 中 非空 且 美丽 的子集数目。…...

前端-Rollup
Rollup 是一个用于 JavaScript 的模块打包工具,它将小的代码片段编译成更大、更复杂的代码,例如库或应用程序。它使用 JavaScript 的 ES6 版本中包含的新标准化代码模块格式,而不是以前的 CommonJS 和 AMD 等特殊解决方案。ES 模块允许你自由…...

20【变量的深度理解】
一说起变量,懂点编程的都知道,但是在理解上可能还不够深 变量就是存储空间,电脑上的存储空间有永久(硬盘)和临时(内存条)两种,永久数据重启电脑后依旧存在,临时数据只…...

大数据学习之Kafka消息队列、Spark分布式计算框架一
Kafka消息队列 章节一.kafka入门 4.kafka入门_消息队列两种模式 5.kafka入门_架构相关名词 Kafka 入门 _ 架构相关名词 事件 记录了世界或您的业务中 “ 发生了某事 ” 的事实。在文档中 也称为记录或消息。当您向 Kafka 读取或写入数据时,您以事件的 形式执行…...

基于Flask的旅游系统的设计与实现
【Flask】基于Flask的旅游系统的设计与实现(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统采用Python作为后端开发语言,结合前端Bootstrap框架,为用户提供了丰富…...