三次输错密码后,系统是怎么做到不让我继续尝试的?

1故事背景

忘记密码这件事,相信绝大多数人都遇到过,输一次错一次,错到几次以上,就不允许你继续尝试了。

但当你尝试重置密码,又发现新密码不能和原密码重复:

图片

图片

相信此刻心情只能用一张图形容:

图片

图片

虽然,但是,密码还是很重要的,顺便我有了一个问题:三次输错密码后,系统是怎么做到不让我继续尝试的?

2我想了想,有如下几个问题需要搞定

  • 是只有输错密码才锁定,还是账户名和密码任何一个输错就锁定?

  • 输错之后也不是完全冻结,为啥隔了几分钟又可以重新输了?

  • 技术栈到底麻不麻烦?

去网上搜了搜,也问了下ChatGPT,找到一套解决方案:SpringBoot+Redis+Lua脚本。

这套方案也不算新,很早就有人在用了,不过难得是自己想到的问题和解法,就记录一下吧。

顺便回答一下上面的三个问题:

  • 锁定的是IP,不是输入的账户名或者密码,也就是说任一一个输错3次就会被锁定

  • Redis的Lua脚本中实现了key过期策略,当key消失时锁定自然也就消失了

  • 技术栈同SpringBoot+Redis+Lua脚本

3那么自己动手实现一下

前端部分

首先写一个账密输入页面,使用很简单HTML加表单提交

<!DOCTYPE html>
<html>
<head><title>登录页面</title><style>body {background-color: #F5F5F5;}form {width: 300px;margin: 0 auto;margin-top: 100px;padding: 20px;background-color: white;border-radius: 5px;box-shadow: 0 0 10px rgba(0,0,0,0.2);}label {display: block;margin-bottom: 10px;}input[type="text"], input[type="password"] {border: none;padding: 10px;margin-bottom: 20px;border-radius: 5px;box-shadow: 0 0 5px rgba(0,0,0,0.1);width: 100%;box-sizing: border-box;font-size: 16px;}input[type="submit"] {background-color: #30B0F0;color: white;border: none;padding: 10px;border-radius: 5px;box-shadow: 0 0 5px rgba(0,0,0,0.1);width: 100%;font-size: 16px;cursor: pointer;}input[type="submit"]:hover {background-color: #1C90D6;}</style>
</head>
<body><form action="http://localhost:8080/login" method="get"><label for="username">用户名</label><input type="text" id="username" name="username" placeholder="请输入用户名" required><label for="password">密码</label><input type="password" id="password" name="password" placeholder="请输入密码" required><input type="submit" value="登录"></form>
</body>
</html>

效果如下:

图片

图片

后端部分

技术选型分析

首先我们画一个流程图来分析一下这个登录限制流程

图片

图片

  • 从流程图上看,首先访问次数的统计与判断不是在登录逻辑执行后,而是执行前就加1了;

  • 其次登录逻辑的成功与失败并不会影响到次数的统计;

  • 最后还有一点流程图上没有体现出来,这个次数的统计是有过期时间的,当过期之后又可以重新登录了。

那为什么是Redis+Lua脚本呢?

Redis的选择不难看出,这个流程比较重要的是存在一个用来计数的变量,这个变量既要满足分布式读写需求,还要满足全局递增或递减的需求,那Redis的incr方法是最优选了。

那为什么需要Lua脚本呢?流程上在验证用户操作前有些操作,如图:

图片

图片

这里至少有3步Redis的操作,get、incr、expire,如果全放到应用里面来操作,有点慢且浪费资源。

Lua脚本的优点如下:

  • 减少网络开销。 可以将多个请求通过脚本的形式一次发送,减少网络时延。

  • 原子操作。 Redis会将整个脚本作为一个整体执行,中间不会被其他请求插入。因此在脚本运行过程中无需担心会出现竞态条件,无需使用事务。

  • 复用。 客户端发送的脚本会永久存在redis中,这样其他客户端可以复用这一脚本,而不需要使用代码完成相同的逻辑。

最后为了增加功能的复用性,我打算使用Java注解的方式实现这个功能。

代码实现

项目结构如下

图片

图片

配置文件

pom.xml

<?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>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.11</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>LoginLimit</artifactId><version>0.0.1-SNAPSHOT</version><name>LoginLimit</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- Jedis --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency><!--切面依赖 --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId></dependency><!-- commons-lang3 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><!-- guava --><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>23.0</version></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

application.properties

## Redis配置
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
spring.redis.timeout=1000
## Jedis配置
spring.redis.jedis.pool.min-idle=0
spring.redis.jedis.pool.max-idle=500
spring.redis.jedis.pool.max-active=2000
spring.redis.jedis.pool.max-wait=10000

注解部分

LimitCount.java

package com.example.loginlimit.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 次数限制注解* 作用在接口方法上*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LimitCount {/*** 资源名称,用于描述接口功能*/String name() default "";/*** 资源 key*/String key() default "";/*** key prefix** @return*/String prefix() default "";/*** 时间的,单位秒* 默认60s过期*/int period() default 60;/*** 限制访问次数* 默认3次*/int count() default 3;
}

核心处理逻辑类:LimitCountAspect.java

package com.example.loginlimit.aspect;import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Objects;import javax.servlet.http.HttpServletRequest;import com.example.loginlimit.annotation.LimitCount;
import com.example.loginlimit.util.IPUtil;
import com.google.common.collect.ImmutableList;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;@Slf4j
@Aspect
@Component
public class LimitCountAspect {private final RedisTemplate<String, Serializable> limitRedisTemplate;@Autowiredpublic LimitCountAspect(RedisTemplate<String, Serializable> limitRedisTemplate) {this.limitRedisTemplate = limitRedisTemplate;}@Pointcut("@annotation(com.example.loginlimit.annotation.LimitCount)")public void pointcut() {// do nothing}@Around("pointcut()")public Object around(ProceedingJoinPoint point) throws Throwable {HttpServletRequest request = ((ServletRequestAttributes)Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();MethodSignature signature = (MethodSignature)point.getSignature();Method method = signature.getMethod();LimitCount annotation = method.getAnnotation(LimitCount.class);//注解名称String name = annotation.name();//注解keyString key = annotation.key();//访问IPString ip = IPUtil.getIpAddr(request);//过期时间int limitPeriod = annotation.period();//过期次数int limitCount = annotation.count();ImmutableList<String> keys = ImmutableList.of(StringUtils.join(annotation.prefix() + "_", key, ip));String luaScript = buildLuaScript();RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);Number count = limitRedisTemplate.execute(redisScript, keys, limitCount, limitPeriod);log.info("IP:{} 第 {} 次访问key为 {},描述为 [{}] 的接口", ip, count, keys, name);if (count != null && count.intValue() <= limitCount) {return point.proceed();} else {return "接口访问超出频率限制";}}/*** 限流脚本* 调用的时候不超过阈值,则直接返回并执行计算器自加。** @return lua脚本*/private String buildLuaScript() {return "local c" +"\nc = redis.call('get',KEYS[1])" +"\nif c and tonumber(c) > tonumber(ARGV[1]) then" +"\nreturn c;" +"\nend" +"\nc = redis.call('incr',KEYS[1])" +"\nif tonumber(c) == 1 then" +"\nredis.call('expire',KEYS[1],ARGV[2])" +"\nend" +"\nreturn c;";}}

获取IP地址的功能我写了一个工具类IPUtil.java,代码如下:

package com.example.loginlimit.util;import javax.servlet.http.HttpServletRequest;public class IPUtil {private static final String UNKNOWN = "unknown";protected IPUtil() {}/*** 获取 IP地址* 使用 Nginx等反向代理软件, 则不能通过 request.getRemoteAddr()获取 IP地址* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,* X-Forwarded-For中第一个非 unknown的有效IP字符串,则为真实IP地址*/public static String getIpAddr(HttpServletRequest request) {String ip = request.getHeader("x-forwarded-for");if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;}}

另外就是Lua限流脚本的说明,脚本代码如下:

  private String buildLuaScript() {return "local c" +"\nc = redis.call('get',KEYS[1])" +"\nif c and tonumber(c) > tonumber(ARGV[1]) then" +"\nreturn c;" +"\nend" +"\nc = redis.call('incr',KEYS[1])" +"\nif tonumber(c) == 1 then" +"\nredis.call('expire',KEYS[1],ARGV[2])" +"\nend" +"\nreturn c;";}

这段脚本有一个判断, tonumber(c) > tonumber(ARGV[1])这行表示如果当前key 的值大于了limitCount,直接返回;否则调用incr方法进行累加1,且调用expire方法设置过期时间。

最后就是RedisConfig.java,代码如下:

package com.example.loginlimit.config;import java.io.IOException;
import java.io.Serializable;
import java.time.Duration;
import java.util.Arrays;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;@Configuration
public class RedisConfig extends CachingConfigurerSupport {@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private int port;@Value("${spring.redis.password}")private String password;@Value("${spring.redis.timeout}")private int timeout;@Value("${spring.redis.jedis.pool.max-idle}")private int maxIdle;@Value("${spring.redis.jedis.pool.max-wait}")private long maxWaitMillis;@Value("${spring.redis.database:0}")private int database;@Beanpublic JedisPool redisPoolFactory() {JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();jedisPoolConfig.setMaxIdle(maxIdle);jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);if (StringUtils.isNotBlank(password)) {return new JedisPool(jedisPoolConfig, host, port, timeout, password, database);} else {return new JedisPool(jedisPoolConfig, host, port, timeout, null, database);}}@BeanJedisConnectionFactory jedisConnectionFactory() {RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();redisStandaloneConfiguration.setHostName(host);redisStandaloneConfiguration.setPort(port);redisStandaloneConfiguration.setPassword(RedisPassword.of(password));redisStandaloneConfiguration.setDatabase(database);JedisClientConfiguration.JedisClientConfigurationBuilder jedisClientConfiguration = JedisClientConfiguration.builder();jedisClientConfiguration.connectTimeout(Duration.ofMillis(timeout));jedisClientConfiguration.usePooling();return new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration.build());}@Bean(name = "redisTemplate")@SuppressWarnings({"rawtypes"})@ConditionalOnMissingBean(name = "redisTemplate")public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate<>();//使用 fastjson 序列化JacksonRedisSerializer jacksonRedisSerializer = new JacksonRedisSerializer<>(Object.class);// value 值的序列化采用 fastJsonRedisSerializertemplate.setValueSerializer(jacksonRedisSerializer);template.setHashValueSerializer(jacksonRedisSerializer);// key 的序列化采用 StringRedisSerializertemplate.setKeySerializer(new StringRedisSerializer());template.setHashKeySerializer(new StringRedisSerializer());template.setConnectionFactory(redisConnectionFactory);return template;}//缓存管理器@Beanpublic CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(redisConnectionFactory);return builder.build();}@Bean@ConditionalOnMissingBean(StringRedisTemplate.class)public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}@Beanpublic KeyGenerator wiselyKeyGenerator() {return (target, method, params) -> {StringBuilder sb = new StringBuilder();sb.append(target.getClass().getName());sb.append(method.getName());Arrays.stream(params).map(Object::toString).forEach(sb::append);return sb.toString();};}@Beanpublic RedisTemplate<String, Serializable> limitRedisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<String, Serializable> template = new RedisTemplate<>();template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(new GenericJackson2JsonRedisSerializer());template.setConnectionFactory(redisConnectionFactory);return template;}
}class JacksonRedisSerializer<T> implements RedisSerializer<T> {private Class<T> clazz;private ObjectMapper mapper;JacksonRedisSerializer(Class<T> clazz) {super();this.clazz = clazz;this.mapper = new ObjectMapper();mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);}@Overridepublic byte[] serialize(T t) throws SerializationException {try {return mapper.writeValueAsBytes(t);} catch (JsonProcessingException e) {e.printStackTrace();return null;}}@Overridepublic T deserialize(byte[] bytes) throws SerializationException {if (bytes.length <= 0) {return null;}try {return mapper.readValue(bytes, clazz);} catch (IOException e) {e.printStackTrace();return null;}}
}

LoginController.java

package com.example.loginlimit.controller;import javax.servlet.http.HttpServletRequest;import com.example.loginlimit.annotation.LimitCount;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@Slf4j
@RestController
public class LoginController {@GetMapping("/login")@LimitCount(key = "login", name = "登录接口", prefix = "limit")public String login(@RequestParam(required = true) String username,@RequestParam(required = true) String password, HttpServletRequest request) throws Exception {if (StringUtils.equals("张三", username) && StringUtils.equals("123456", password)) {return "登录成功";}return "账户名或密码错误";}}

LoginLimitApplication.java

package com.example.loginlimit;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class LoginLimitApplication {public static void main(String[] args) {SpringApplication.run(LoginLimitApplication.class, args);}}

4演示一下效果

图片

图片

上面这套限流的逻辑感觉用在小型或中型的项目上应该问题不大,不过目前的登录很少有直接锁定账号不能输入的,一般都是弹出一个验证码框,让你输入验证码再提交。我觉得用我这套逻辑改改应该不成问题,核心还是接口尝试次数的限制嘛!

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

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

相关文章

Mobaxterm 使用lrzsz传输文件(rz/sz)

Mobaxterm 使用lrzsz传输文件报错 1. 现象 最近从xshell切换到Mobaxterm其他一切正常,就是使用rz传输文件时会出现错误,比较苦恼. 会出现以下错误 [rootcentos7 rpmbuild]# rz ▒CCCCCCCCCCC23be50ive.**B0100000023be502. 解决方法 去官网(https://mobaxterm.mobatek.net…

2021年03月 Scratch(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

Scratch等级考试(1~4级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 在《采矿》游戏中,当角色捡到黄金时财富值加1分,捡到钻石时财富值加2分,下面哪个程序实现这个功能? A: B: C: D: 答案:D A将变量值固定,BC为双重判断

练习七-在Verilog中使用任务task

在Verilog中使用任务task 1&#xff0c;任务目的2&#xff0c;RTL代码&#xff0c;交换3&#xff0c;测试代码4&#xff0c;波形显示 1&#xff0c;任务目的 &#xff08;1&#xff09;掌握任务在verilog模块设计中的应用&#xff1b; &#xff08;2&#xff09;学会在电平敏感…

Android Studio记录一个错误:Execution failed for task ‘:app:lintVitalRelease‘.

Android出现Execution failed for task :app:lintVitalRelease.> Lint found fatal errors while assembling a release target. Execution failed for task :app:lintVitalRelease解决方法 Execution failed for task ‘:app:lintVitalRelease’ build project 可以正常执…

〖大前端 - 基础入门三大核心之JS篇㊷〗- DOM事件对象及它的属性

说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费&#xff0c;如需要项目实战或者是体系化资源&#xff0c;文末名片加V&#xff01;作者&#xff1a;不渴望力量的哈士奇(哈哥)&#xff0c;十余年工作经验, 从事过全栈研发、产品经理等工作&#xf…

进程已结束,退出代码-1073741571 (0xC00000FD)

今天遇到了一个很邪门的问题&#xff0c;没有报错&#xff0c;只是提示“进程已结束,退出代码-1073741571 (0xC00000FD)”。后来查资料说是栈溢出。 出问题的应该是上面这段代码。 这里我想把一个128*128的矩阵进行剪枝操作。 传入的128*128的矩阵太大了&#xff0c;两组for循…

【Flink】状态管理

目录 1、状态概述 1.1 无状态算子 1.2 有状态算子 2、状态分类 ​编辑 2.1 算子状态 2.1.1 列表状态&#xff08;ListState&#xff09; 2.1.2 联合列表状态&#xff08;UnionListState&#xff09; 2.1.3 广播状态&#xff08;BroadcastState&#xff09; 2.2 按键分…

图像标记上线,描点信息尽在掌握丨三叠云

图像标记 路径 表单设计 >> 组件 >> 增强组件 功能简介 「图像标记」字段是「增强字段」类型字段。用户通过上传图片的方式构建一个背景图片&#xff0c;并在构建的图片背景上添加描点信息。搭配「仪表盘」中的「图像轨迹」&#xff0c;可绘制出相应的数据轨迹…

界面组件DevExpress Reporting v23.1 - Web报表设计器功能升级

DevExpress Reporting是.NET Framework下功能完善的报表平台&#xff0c;它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集&#xff0c;包括数据透视表、图表&#xff0c;因此您可以构建无与伦比、信息清晰的报表 界面组件DevExpress Reporting v23.1已经发布一段…

基于JavaWeb+SSM+Vue微信阅读小程序的设计和实现

基于JavaWebSSMVue微信阅读小程序的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏[Java 源码获取 源码获取入口 Lun文目录 第1章 绪论 1 1.1 课题背景 1 1.2 课题意义 1 1.3 研究内容 1 第2章 开发环境与技术 3 2.1 MYSQL数据库 3 2.2 JSP技…

2016年8月15日 Go生态洞察:Go 1.7版本发布

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

解决traefik/nginx-ingress-controller配置正确的情况访问域名仍然报错: Connection Refused的问题

最近碰到一个很奇怪的问题&#xff1a; traefik/nginx-ingress-controller配置正确&#xff0c;但是访问ingress配置的host域名就是死活报错&#xff1a; Connection Refused 这样怎么也找不到原因&#xff0c;然后一咬牙直接在其中一台节点yum安装nginx, 通过直接反向代理的方…

微信小程序开发资源汇总

本文收集了微信小程序开发过程中会使用到的资料、问题以及第三方组件库。本文不是一篇关于如何学习微信小程序的入门指南&#xff0c;也非参考手册&#xff0c;只是一些资料的整理。 本仓库中的资料整理自网络&#xff0c;也有一些来自网友的推荐。 官方文档 小程序设计指南…

UE5 UI教程学习笔记

参考资料&#xff1a;https://item.taobao.com/item.htm?spma21n57.1.0.0.2b4f523cAV5i43&id716635137219&ns1&abbucket15#detail 基础工程&#xff1a;https://download.csdn.net/download/qq_17523181/88559312 1. 介绍 工程素材 2. 创建Widget UE5 UI系统的…

那些被玩烂了的设计模式

单例模式 单例模式是指一个类在一个进程中只有一个实例对象&#xff08;但也不一定&#xff0c;比如Spring中的Bean的单例是指在一个容器中是单例的&#xff09; 单例模式创建分为饿汉式和懒汉式&#xff0c;总共大概有8种写法。但是在开源项目中使用最多的主要有两种写法&am…

【MISRA-C 2012】浓缩版解读

文章目录 1、前言2、简介2.1、如何看待MISRA-C 20122.2、准则(guidelines)里面的指示(Directive)和规则(Rule)2.3、准则(guidelines)的级别(Category) 3、若干重要的Directive和Rule3.1、指示(Directive)Dir 2.1&#xff08;必要&#xff09; 所有的源文件编译过程不得有编译错…

聚类笔记/sklearn笔记:Affinity Propagation亲和力传播

1 算法原理 1.1 基本思想 将全部数据点都当作潜在的聚类中心(称之为 exemplar )然后数据点两两之间连线构成一个网络( 相似度矩阵 )再通过网络中各条边的消息( responsibility 和 availability )传递计算出各样本的聚类中心。 1.2 主要概念 Examplar聚类中心similarity S(i…

【ARM CoreLink 系列 3.2 -- CCI-400,CCI-500, CCI-550 差异】

文章目录 CCI-400 和 CCI-500 差异ARM CCI-400ARM CCI-500ARM CCI-550CCI-400 和 CCI-500 差异 ARM的 CCI(Cache Coherent Interconnect)系列产品是用于多核处理器之间的高性能缓存一致性互连。CCI-400 和 CCI-500 是该系列中的两种设计,它们旨在允许多个处理器核心和其他资…

TopNet-(CVPR2023)前背景图像合成

文章目录 摘要引言算法架构结构损失函数 实验数据集评估SOTA比较模型是否过拟合到修复区域泛化到真实图片消融实验 讨论及结论限制 参考文献 摘要 作者调研自动放置目标到背景进行图像合成的问题。提供背景图、分割的目标&#xff0c;训练模型预测合理放置信息&#xff08;位置…

程序员接单,宝藏好平台抄底攻略清单!五大平台精选。

前阵子“双十一”购物节狂欢促销&#xff0c;各种好货清单席卷而来。 程序员购不购物我不知道&#xff0c;但是这个兼职、接单清单相信你一定用得着。 搜罗海量信息&#xff0c;整理大量数据与评价&#xff0c;挖出了5个宝藏平台&#xff0c;绝对个个精选&#xff0c;保证量大…