SpringSecurity Oauth2 -账号密码实现多因子身份认证
1. 密码策略问题
CREATE TABLE `t_storage` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键',`nameSpace` varchar(64) NOT NULL COMMENT '隔离字段',`groupId` varchar(128) NOT NULL COMMENT '分组,比如不同app',`dataId` varchar(64) NOT NULL COMMENT '数据存储id',`dataValue` mediumtext DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE,UNIQUE KEY `nameSpace` (`nameSpace`,`groupId`,`dataId`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
INSERT INTO `t_storage`(`id`, `nameSpace`, `groupId`, `dataId`, `dataValue`) VALUES (1352, '_system', 'auth', 'access_token_validity_seconds', '43200');INSERT INTO `t_storage`(`id`, `nameSpace`, `groupId`, `dataId`, `dataValue`) VALUES (1353, '_system', 'auth', 'refresh_token_validity_seconds', '86400');INSERT INTO `t_storage`(`id`, `nameSpace`, `groupId`, `dataId`, `dataValue`) VALUES (36, '_system', 'console', 'consoleConfig', '{\r\n \"overTime\": 360,\r\n \"stageName\": \"安全大数据平台\",\r\n \"icmpEnable\": true,\r\n \"sshEnable\": false\r\n }');INSERT INTO `t_storage`(`id`, `nameSpace`, `groupId`, `dataId`, `dataValue`) VALUES (45, '_system', 'auth', 'policy', '{\"passwordPeriod\":90,\"errorTimes\":5,\"lockTime\":5,\"minLength\":8}');
1. JsonStorage 封装处理Storage操作
@Slf4j
public class JsonStorage {private IStorage storage;@Autowiredprivate void setStorage(IStorage storage) {this.storage = storage;}private final ObjectMapper JSON_MAPPER = new ObjectMapper();/*** 通过实体对象设置Json字符串** @param nameSpace 命名空间* @param groupId 分组* @param key 键* @param entity 实体对象* @return* @throws JsonProcessingException*/public boolean setJsonDataWithEntity(String nameSpace, String groupId, String key, Object entity) {try {String jsonString = JSON_MAPPER.writeValueAsString(entity);storage.setDataValue(nameSpace, groupId, key, jsonString);return true;} catch (JsonProcessingException | StorageException e) {log.error("Storage setValue failed, error: {}", e);return false;}}/*** 获取由JsonString转化后的实体对象** @param nameSpace 命名空间* @param groupId 分组* @param key 键* @param classType 实体类型* @param <T> 泛型* @return 通过传入的类型返回对应的实体对象* @throws StorageException*/public <T> T getJsonDataAsEntity(String nameSpace, String groupId, String key, Class<T> classType) throws StorageException {try {String jsonString = storage.getDataValue(nameSpace, groupId, key);if (!StringUtils.isEmpty(jsonString)) {JSON_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);return JSON_MAPPER.readValue(jsonString, classType);} else {log.error("Storage get value failed, value is empty");throw new StorageException("storage.value.empty");}} catch (JsonProcessingException e) {log.error("Storage get value failed, jsonString format error", e);throw new StorageException("storage.getValue.format.error");}}
}
2. StorageDb 数据库数据查询和更新
@Slf4j
public class StorageDb implements IStorage {@Autowiredprivate RedisUtils redisUtils;@Autowired@Qualifier("storageJdbcTemplate")private JdbcTemplate jdbcTemplate;@Autowiredprivate StorageConfig storageConfig;@Override@CheckParampublic void setDataValue(@CheckParam(length = 48) String nameSpace,@CheckParam(length = 96) String groupId,@CheckParam(length = 96) String dataId,@CheckParam(length = 10000, isCheckLegal = false, required = false) String value) throws StorageException {// 同一nameSpace, groupId, dataId下数据不存在则新增,否则更新String updateSql = String.format("insert into %s(nameSpace,groupId,dataId,dataValue) values (?,?,?,?) "+ " on duplicate key update dataValue=values(dataValue)",StorageConstants.TABLE_NAME);int rows = jdbcTemplate.update(updateSql, nameSpace, groupId, dataId, value);if (rows > 0) {// 更新完数据将Redis缓存删除String keyName = generateKey(nameSpace, groupId, dataId);redisUtils.delete(keyName);}}@Override@CheckParampublic String getDataValue(@CheckParam(length = 48) String nameSpace,@CheckParam(length = 96) String groupId,@CheckParam(length = 96) String dataId) {// 拼接redis缓存keyString keyName = generateKey(nameSpace, groupId, dataId);// 先从redis进行获取数据String dataValue = redisUtils.getString(keyName);// 缓存不存在则查询数据库if (Objects.isNull(dataValue)) {String sql = String.format("select dataValue from %s where nameSpace=? and groupId=? and dataId=?", StorageConstants.TABLE_NAME);try {dataValue = jdbcTemplate.queryForObject(sql, String.class, nameSpace, groupId, dataId);} catch (DataAccessException e) {log.error("Failed to query storage, nameSpace = {}, groupId = {}, dataId = {}, error {}", nameSpace, groupId, dataId, e);}// 查询完数据将其写入redis,空值也缓存,防止缓存穿透redisUtils.setWithExpired(keyName, StringUtils.isEmpty(dataValue) ? "" : dataValue, storageConfig.getExpiredTime(), TimeUnit.SECONDS);}return dataValue;}/*** 生成rediskey** @param fields* @return*/private String generateKey(String... fields) {return StorageConstants.REDIS_KEY_PREFIX.concat(String.join(StorageConstants.SEPARATOR, fields));}
}
缓存查询优先:在 getDataValue 方法中,先查询 Redis 缓存,如果没有命中缓存,再查询数据库。这样有效减少了数据库查询次数,提高了性能。
缓存穿透防护:通过将空结果也缓存起来,避免恶意或频繁访问不存在的数据造成缓存穿透,进而影响数据库的负载。
缓存一致性:在 setDataValue 方法中,在更新或插入数据库后,会清除 Redis 缓存中的旧值,确保缓存与数据库的一致性。
3. 获取密码策略
1. PasswordPolicyEntity 密码策略
@ApiModel
@Data
@JsonInclude(value = JsonInclude.Include.NON_NULL)
public class PasswordPolicyEntity {@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)@ApiModelProperty(value = "密码周期", required = true)private Integer passwordPeriod = 90;@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)@ApiModelProperty(value = "登录密码输错次数限制", required = true)private Integer errorTimes = 5;@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)@ApiModelProperty(value = "密码输入错误达到限制后锁定锁定时间", required = true)private Integer lockTime = 5;@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)@ApiModelProperty(value = "密码最小长度", required = true)private Integer minLength = 8;}
密码策略升级通过flyway初始化数据的:
INSERT INTO `t_storage`(`id`, `nameSpace`, `groupId`, `dataId`, `dataValue`) VALUES (45, '_system', 'auth', 'policy', '{\"passwordPeriod\":90,\"errorTimes\":5,\"lockTime\":5,\"minLength\":8}');
2. 获取密码策略并判断客户端IP是否被锁定
读取数据库中的数据并dataValue字段转换为PasswordPolicyEntity对象
@Slf4j
@Service("loginService")
public class LoginServiceImpl implements LoginService {@Overridepublic LoginResult checkLogin(LoginEntity loginEntity, HttpServletRequest request) {LoginResult loginResult = new LoginResult();// 获取客户端真实IPString clientIp = ClientUtils.getClientIp(request);// 读取密码策略PasswordPolicyEntity policyEntity = userService.readPasswordPolicy();// 判断客户端IP是否被锁定if (isLocked(clientIp)) {loginResult.setStatus(I18nUtils.i18n("response.login.lock.time", policyEntity.getErrorTimes(), policyEntity.getLockTime()));return loginResult;}}
}
获取密码策略:
@Slf4j
@Service("userService")
public class UserServiceImpl implements IUserService {private final ReentrantReadWriteLock.ReadLock readLock = new ReentrantReadWriteLock().readLock();@Overridepublic PasswordPolicyEntity readPasswordPolicy() {PasswordPolicyEntity passwordPolicyEntity = null;readLock.lock();try {passwordPolicyEntity = jsonStorage.getJsonDataAsEntity(SettingNameSpaceConstant.Config.SYSTEM_CONFIG, AuthLoginConstant.AUTH_DIRECTORY, AuthLoginConstant.POLICY, PasswordPolicyEntity.class);} catch (StorageException e) {log.error("Error read pwd policy", e);} finally {readLock.unlock();}return passwordPolicyEntity;}
}
判断客户端IP是否被锁定,密码输入错误次数达到5次后就会被锁定,锁定时间为5分钟:
@Override
public boolean isLocked(String ipAddress) {HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();String authIpAddressKey = RedisKeyUtil.getAuthIpAddressKey(ipAddress);String next = hashOperations.get(authIpAddressKey, Constants.NgsocAuth.NEXT);if (next != null) {Integer lockTime = userService.readPasswordPolicy().getLockTime();// 当前时间(分钟)-锁定时间<=5分钟,说明锁定了double differentTime = (LocalDateUtils.getNowMilliseconds() - Long.parseLong(next)) / (1000 * 60 * 1.0);return (int) differentTime <= lockTime;}return false;
}
判断某个 IP 地址是否被锁定。具体来说,它通过从 Redis 中获取该 IP 地址相关的锁定时间(next),并根据当前时间与锁定时间的差值来判断是否仍处于锁定状态。如果锁定时间未超过指定的时长(lockTime),则该 IP 地址被视为仍然锁定。
2. 多因子认证登录
1. 登录实体类 LoginEntity2. 认证方式枚举 AuthMethodEnum
@Getter
@NoArgsConstructor
@AllArgsConstructor
public enum AuthMethodEnum {/*** 密码校验*/PASSWORD("password", "密码校验"),/*** 手机短信验证码*/SMS("sms", "手机短信验证码"),;private String code;private String desc;public static AuthMethodEnum getByCode(String code) {for (AuthMethodEnum authMethodEnum : values()) {if (authMethodEnum.getCode().equals(code)) {return authMethodEnum;}}return null;}
}
3. 身份认证接口 AuthMethod
public interface AuthMethod {/*** 进行认证** @param loginEntity 参数* @return String 用户ID*/String doAuthenticate(LoginEntity loginEntity);
}
4. 密码认证方式 PasswordAuthMethod
利用责任链模式实现账号密码的多因子身份认证机制,在密码认证时利用Redis Hash结构处理登录失败的次数记录和客户端锁定逻辑。
对用户账号和密码进行校验,并在校验通过后生成并返回用户的身份标识 (userId)。同时,它还处理了登录失败的次数记录和账户锁定逻辑。
定义了一个名为 PasswordAuthMethod 的类,该类实现了 AuthMethod 接口,用于处理用户通过密码进行身份认证的逻辑。它使用了 Redis 来管理登录失败次数和客户端 IP 的锁定机制。
密码认证逻辑 (doAuthenticate):
- 获取用户输入的用户名、域 ID、原始密码和客户端 IP 地址。
- 调用
userService.findUserByNameAndDomainId获取用户信息,如果用户不存在,记录登录失败次数并返回。 - 通过
BcryptUtil.bEncryptMatch比较明文密码和数据库中加密的密码,判断密码是否正确。如果密码不正确,记录失败次数并返回。 - 如果密码正确且未超过登录失败次数限制,删除与该 IP 相关的锁定信息,允许登录。
记录登录失败次数 (recordLoginAttempts):
- 通过 Redis 记录 IP 地址对应的登录失败次数,使用
increment方法将登录失败次数加 1。 - 根据从
userService.readPasswordPolicy()获取的密码策略,设置失败次数锁定的过期时间和锁定阈值。 - 当登录失败次数超过或等于设定的错误次数时,锁定客户端 IP,并记录锁定的时间。
/*** 密码登陆认证方式*/
@Slf4j
@Component("passwordAuthMethod")
public class PasswordAuthMethod implements AuthMethod {@Autowiredprivate IUserService userService;@Qualifier("stringRedisTemplate")@Autowiredprivate RedisTemplate<String, String> redisTemplate;/*** 对客户端输入的账号密码校验:先校验账号是否存在,再校验密码是否正确*/@Overridepublic String doAuthenticate(LoginEntity loginEntity) {String clientIp = loginEntity.getClientIp();String username = loginEntity.getAuth().getIdentity().getPassword().getUser().getName();String domainId = loginEntity.getAuth().getIdentity().getPassword().getUser().getDomain().getId();String originalPassword = loginEntity.getAuth().getIdentity().getPassword().getUser().getOriginalPwd();// 根据账号名称查询账号信息UserEntity userInfo = userService.findUserByNameAndDomainId(username, domainId);// 账号不存在登录失败,将尝试登录次数加1,并判断是否锁定客户端IPif (Objects.isNull(userInfo)) {log.info("the domainId:{}, account:{}, not exist", domainId, username);recordLoginAttempts(clientIp);return StringUtils.EMPTY;}// 进行密码的比对,传输过来的是明文String userId = userInfo.getId();String dbEncryptPwd = userInfo.getPassword();boolean isPassed = BcryptUtil.bEncryptMatch(originalPassword, dbEncryptPwd);// 密码错误登录失败,将尝试登录次数加1,并判断是否锁定客户端IPif (!isPassed) {log.info("the user: {} login failed, account or password is wrong", userId);recordLoginAttempts(clientIp);return StringUtils.EMPTY;}// 账号存在且密码正确登陆成功,去除客户端锁定的限制String authIpAddressKey = RedisKeyUtil.getAuthIpAddressKey(clientIp);redisTemplate.delete(authIpAddressKey);// 密码校验成功后返回userIdreturn userId;}/*** 记录客户端登录失败次数*/private void recordLoginAttempts(String ip) {// 获取客户端IP的keyString authIpAddressKey = RedisKeyUtil.getAuthIpAddressKey(ip);// 客户端登录失败次数加1,并设置过期时间为5分钟HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();int attempts = hashOperations.increment(authIpAddressKey, Constants.NgsocAuth.ATTEMPTS, 1).intValue();PasswordPolicyEntity policyEntity = userService.readPasswordPolicy();redisTemplate.expire(authIpAddressKey, policyEntity.getLockTime(), TimeUnit.MINUTES);// 如果客户端登录失败次数大于等于5,则将客户端IP锁定,设置value当前时间,过期时间为5分钟if (attempts >= policyEntity.getErrorTimes()) {hashOperations.put(authIpAddressKey, Constants.NgsocAuth.NEXT, String.valueOf(LocalDateUtils.getNowMilliseconds()));redisTemplate.expire(authIpAddressKey, policyEntity.getLockTime(), TimeUnit.MINUTES);}}
}
记录登录错误次数:
- 通过
redisTemplate.opsForHash().increment方法,将 IP 地址对应的登录错误次数递增 1,并将其存储在 Redis 的哈希结构中,键为authIpAddressKey,字段为Constants.NgsocAuth.ATTEMPTS。
获取密码策略:
- 使用
userService.readPasswordPolicy()获取密码策略,包含锁定时间(lockTime)和允许的最大错误次数(errorTimes)。
设置 Redis 键过期时间:
- 每次记录登录失败后,都会设置或更新该 IP 地址的 Redis 键的过期时间,过期时间是根据密码策略中的
lockTime决定的,单位为分钟。
超过错误次数,记录锁定时间:
- 如果错误次数超过策略中定义的最大次数,则在 Redis 中记录锁定时间(
NEXT),以便在后续检查时根据该时间判断是否锁定 IP 地址。
5. 短信验证码认证方式 SmsAuthMethod
@Component("smsAuthMethod")
public class SmsAuthMethod implements AuthMethod {@Overridepublic String doAuthenticate(LoginEntity loginEntity) {return null;}
}
6. 身份认证责任链 AuthMethodChain
public class AuthMethodChain {private final List<AuthMethod> authMethodList = new ArrayList<>();public AuthMethodChain add(AuthMethod authMethod) {authMethodList.add(authMethod);return this;}/*** 获取用户ID*/public String execute(LoginEntity loginEntity) {String userId = StringUtils.EMPTY;Set<String> userIdSet = new HashSet<>();for (AuthMethod chain : authMethodList) {userId = chain.doAuthenticate(loginEntity);// 只要有一个校验不通过则返回if (StringUtils.isBlank(userId)) {return StringUtils.EMPTY;}// 校验返回的不是同一个用户则不通过userIdSet.add(userId);if (userIdSet.size() > 1) {return StringUtils.EMPTY;}}return userId;}
}
7. 进行多因子登陆认证的校验
在实现多因子登录认证时,使用责任链模式是一种优雅的解决方案。责任链模式通过将一系列的身份认证步骤(例如密码验证、短信验证码验证、二次确认等)串联起来,每个步骤负责处理一部分认证逻辑,若该步骤无法完成认证,则将任务传递给下一个步骤。
通过责任链模式,实现了灵活且可扩展的多因子身份认证机制。每个认证步骤各自负责一部分逻辑,并按照顺序串联起来,如果某一步认证失败,链条会立即中断,从而简化了认证流程的管理逻辑。
可以轻松地增加新的认证步骤(如生物识别、二维码认证等),只需实现 AuthMethod 接口并插入责任链中即可。
@Slf4j
@Service("loginService")
public class LoginServiceImpl implements LoginService {@Resource(name = "passwordAuthMethod")private AuthMethod passwordAuthMethod;@Resource(name = "smsAuthMethod")private AuthMethod smsAuthMethod;/*** 登陆校验*/@Overridepublic LoginResult checkLogin(LoginEntity loginEntity, HttpServletRequest request) {LoginResult loginResult = new LoginResult();String clientIp = ClientUtils.getClientIp(request);// 读取密码策略PasswordPolicyEntity policyEntity = userService.readPasswordPolicy();// 登录错误次数限定if (isLocked(clientIp)) {loginResult.setStatus(I18nUtils.i18n("response.login.lock.time", policyEntity.getErrorTimes(), policyEntity.getLockTime()));return loginResult;}// 进行多因子登陆认证的校验List<String> authMethodList = loginEntity.getAuth().getIdentity().getMethods();AuthMethodChain authMethodChain = new AuthMethodChain();for (String authMethod : authMethodList) {AuthMethodEnum authMethodEnum = AuthMethodEnum.getByCode(authMethod);switch (authMethodEnum) {// 密码校验case PASSWORD:authMethodChain.add(passwordAuthMethod);break;// 短信验证码校验case SMS:authMethodChain.add(smsAuthMethod);break;default:loginResult.setStatus(I18nUtils.i18n("auth.method.undefined"));return loginResult;}}// 执行认证loginEntity.setClientIp(clientIp);String userId = authMethodChain.execute(loginEntity);// userId为空,说明认证失败if (StringUtils.isBlank(userId)) {loginResult.setStatus(I18nUtils.i18n("response.account.login.failed"));return loginResult;}}
}
相关文章:
SpringSecurity Oauth2 -账号密码实现多因子身份认证
1. 密码策略问题 CREATE TABLE t_storage (id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 自增主键,nameSpace varchar(64) NOT NULL COMMENT 隔离字段,groupId varchar(128) NOT NULL COMMENT 分组,比如不同app,dataId varchar(64) NOT NULL COMMENT 数据存储id…...
【CSS in Depth 2 精译_071】11.4 思考字体颜色的对比效果 + 11.5 本章小结
当前内容所在位置(可进入专栏查看其他译好的章节内容) 第四部分 视觉增强技术 ✔️【第 11 章 颜色与对比】 ✔️ 11.1 通过对比进行交流 11.1.1 模式的建立11.1.2 还原设计稿 11.2 颜色的定义 11.2.1 色域与色彩空间11.2.2 CSS 颜色表示法 11.2.2.1 RGB…...
Y3编辑器文档4:触发器1(对话、装备、特效、行为树、排行榜、不同步问题)
文章目录 一、触发器简介1.1 触发器界面1.2 ECA语句编辑及快捷键1.3 参数设置1.4 变量设置1.5 实体触发器1.6 函数库与触发器复用 二、触发器的多层结构2.1 子触发器(在游戏内对新的事件进行注册)2.2 触发器变量作用域2.3 复合条件2.4 循环2.5 计时器2.6…...
趣味编程:猜拳小游戏
1.简介 这个系列的第一篇以猜拳小游戏开始,这是源于我们生活的灵感,在忙碌的时代中,我们每个人都在为自己的生活各自忙碌着,奔赴着自己所走向的那条路上,即使遍体鳞伤。 但是,生活虽然很苦,也不…...
软件工程 概述
软件 不仅仅是一个程序代码。程序是一个可执行的代码,它提供了一些计算的目的。 软件被认为是集合可执行的程序代码,相关库和文档的软件。当满足一个特定的要求,就被称为软件产品。 工程 是所有有关开发的产品,使用良好定义的&…...
CountDownLatch阻塞后countDown未执行会如何?
背景 某项目封装了 Kafka 消费者 API,根据传递的消费者线程数,创建 N 个消费者线程同时消费对应 topic 的数据,并在线程启动后收集到全局列表中,方便在程序调用 stop 流程时逐个停止。 主控类在创建 Kafka 消费线程时使用了 Cou…...
k8s,operator
相对更加灵活和编程友好的管理“有状态应用”的解决方案,它就是:Operator 会议一下有状态应用: 比如数据库集群,数据挂载需要有顺序维护拓扑关系的应用 使用statefulSet这个对象来描述。 CRD又是什么? Operator的工作…...
使用 pyperclip 进行跨平台剪贴板操作
简介:pyperclip 是一个轻量级的 Python 库,支持在不同操作系统(Windows、macOS、Linux)中进行剪贴板的复制和粘贴。这个库的设计简单易用,非常适合需要频繁进行文本复制粘贴操作的场景。 历史攻略: 使用f…...
20 设计模式之职责链模式(问题处理案例)
一、什么是职责链模式 职责链模式是一种行为型设计模式,它允许将请求沿着处理者的链进行传递,直到有一个处理者能够处理它为止。换句话说,它将请求的发送者和接收者解耦,使得多个对象都有机会处理这个请求,从而避免了将…...
SpringBoot3集成MybatisPlus3和knife4j(swagger3兼容增强版)
针对Swagger2规范和OpenAPI3规范的说明: 在Spring Boot框架中,Knife4j对于服务端将Spring的开放接口解析成Swagger2或者OpenAPI3规范的框架,也是依赖的第三方框架组件。说明如下: Swagger2规范:依赖Springfox项目,该项目目前几乎处于停更状态,但很多老项目依然使用的是该…...
【MIT-OS6.S081作业1.3】Lab1-utilities primes
本文记录MIT-OS6.S081 Lab1 utilities 的primes函数的实现过程 文章目录 1. 作业要求primes (moderate)/(hard) 2. 实现过程2.1 代码实现 1. 作业要求 primes (moderate)/(hard) Write a concurrent version of prime sieve using pipes. This idea is due to Doug McIlroy, in…...
游戏引擎学习第35天
开场介绍 今天的任务是继续改进一个虚拟的瓦片地图系统,使其适合处理更大的世界。我们希望这个系统能管理大范围的游戏世界,其中包含按需存储的小区域。昨天,我们介绍了“内存区域”的概念,用于管理持久性存储。我们计划今天继续…...
learn-(Uni-app)输入框u-search父子组件与input输入框(防抖与搜索触发)
1.父子组件u-search (1)父组件 <!-- 父组件 --> <template> <div><searchBar change"change" search"search"></searchBar> </div> </template> <script> // 子组件搜索 import…...
设置IMX6ULL开发板的网卡IP的两种方法(临时生效和永久有效两种方法)
设置开发板网卡的IP,有两种方法。 方法一:临时生效 第一种方式是临时设置,只有本次有效,重启后又要重新设,命令为: ifconfig eth0 192.168.5.9设置成功后可以使用ifconfig命令来查看已设置的 IP 地址。 …...
流量转发利器之Burpsuite概述(1)
目录 一、Burpsuite Burp Suite Spider 的主要特点: 在 Burp Suite 中使用 Spider: Spider 的用例: 限制: 声明:学习视频来自b站up主 泷羽sec,如涉及侵权马上删除文章 声明:本文主要用作技…...
Transformer入门(6)Transformer编码器的前馈网络、加法和归一化模块
文章目录 7.前馈网络8.加法和归一化组件9.组合所有编码器组件构成完整编码器 7.前馈网络 编码器块中的前馈网络子层如下图所示: 图1.32 – 编码器块 前馈网络由两个带有ReLU激活函数的全连接层组成。全连接层(Fully Connected Layer)有时也…...
element-plus中的resetFields()方法
resetFields()确实是Element Plus中的方法,该方法主要用于重置表单,将其值重置为初始值,并移除校验结果。以下是对该方法的详细解释: 一、resetFields方法的作用 在Vue3结合Element Plus开发时࿰…...
【过滤器】.NET开源 ORM 框架 SqlSugar 系列
目录 0、 过滤器介绍 1、表过滤器 (推荐) 1.1 手动添加过滤器 1.2 禁用、清空、备份和还原 1.3 联表查询设置 1.4 动态添加 2、修改和删除用过滤器 2.1 局部设置 2.2 全局设置 (5.1.4.62) 3、子查询用过滤器 4、联表过滤…...
Jmeter Address already in use: connect 解决
做压测接口时,并发一段时间后,会报java.net.BindException: Address already in use: connect 原因: windows提供给TCP/IP链接的端口为 1024-5000,并且要四分钟来循环回收它们,就导致在短时间内跑大量的请求时将端口占…...
C#常见错误—空对象错误
System.NullReferenceException:未将对象引用设置到对象的实例 在C#编程中,System.NullReferenceException是一个常见的运行时异常,其错误信息“未将对象引用设置到对象的实例”意味着代码试图访问一个未被初始化或已被设置为null的对象的成…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...
数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !
我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...
嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...
中科院1区顶刊|IF14+:多组学MR联合单细胞时空分析,锁定心血管代谢疾病的免疫治疗新靶点
中科院1区顶刊|IF14:多组学MR联合单细胞时空分析,锁定心血管代谢疾病的免疫治疗新靶点 当下,免疫与代谢性疾病的关联研究已成为生命科学领域的前沿热点。随着研究的深入,我们愈发清晰地认识到免疫系统与代谢系统之间存在着极为复…...
第14节 Node.js 全局对象
JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。 在浏览器 JavaScript 中,通常 window 是全局对象, 而 Node.js 中的全局…...
