Java】实现图片验证码2.0【详细代码】

        实际开发过程中经常遇到要实现图片验证码来防止外部使用脚本刷接口,所以说图片验证码是很有必要的一个小功能。

下面这个之前发布的,现在发现生成的图片验证码是可以被自动化工具进行识别的,具有一定的安全性问题。

1.0版本验证码:

【Java】实现图片验证码【详细代码】_图片验证码发送axios怎么弄-CSDN博客

所以在2.0版本使用不同的方式实现图片验证码。

2.0版本验证码:

前端login.tsx:只提供了关键的代码。

const LoginPage = () => {const [image, setImage] = useState<string>("");const [captchaVal, setCaptchaVal] = useState<string>("");const [captchaKey, setCaptchaKey] = useState<string>("");const [captchaLoading, setCaptchaLoading] = useState(true);const fetchImageCaptcha = () => {setCaptchaVal("");setCaptchaLoading(true);system.getImageCaptcha().then((res: any) => {setImage(res.data.image);setCaptchaKey(res.data.key);setCaptchaLoading(false);});};useEffect(() => {fetchImageCaptcha();}, []);return (<div className={styles["login-content"]}> <div className={styles["login-box"]}> <div className={styles["right-box"]}><div className="login-box d-flex mt-50"><Inputvalue={email}onChange={(e) => {setEmail(e.target.value);}}style={{ width: 400, height: 54 }}placeholder="请输入管理员邮箱账号"onKeyUp={(e) => keyUp(e)}allowClear/></div><div className="login-box d-flex mt-50"><Input.Passwordvalue={password}onChange={(e) => {setPassword(e.target.value);}}allowClearstyle={{ width: 400, height: 54 }}placeholder="请输入密码"/></div><div className="d-flex mt-50"><Inputvalue={captchaVal}style={{ width: 260, height: 54 }}placeholder="请输入图形验证码"onChange={(e) => {setCaptchaVal(e.target.value);}}allowClearonKeyUp={(e) => keyUp(e)}/><div className={styles["captcha-box"]}>{captchaLoading && (<div className={styles["catpcha-loading-box"]}><Spin size="small" /></div>)}{!captchaLoading && (<imgclassName={styles["captcha"]}onClick={fetchImageCaptcha}src={image}/>)}</div></div><div className="login-box d-flex mt-50"><Buttonstyle={{ width: 400, height: 54 }}type="primary"onClick={loginSubmit}loading={loading}>立即登录</Button></div></div></div></div>);
};

前端接口:

export function getImageCaptcha() {return client.get("/backend/system/image-captcha", {});
}

引入依赖:

        <!--图形验证码依赖 --><dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId><version>2.3.2</version></dependency>

后端Controller:

@RestController
@RequestMapping("/backend/system")
@Slf4j
public class SystemController {@Autowired private ImageCaptchaService imageCaptchaService;//验证码@GetMapping("/image-captcha")public JsonResponse imageCaptcha() throws IOException {ImageCaptchaResult imageCaptchaResult = imageCaptchaService.generate();HashMap<String, String> data = new HashMap<>();data.put("key", imageCaptchaResult.getKey());data.put("image", imageCaptchaResult.getImage());return JsonResponse.data(data);}
}

Service:

public interface ImageCaptchaService {ImageCaptchaResult generate() throws IOException;boolean verify(String key, String code);
}

ImageCaptchaResult实体类:

@Data
public class ImageCaptchaResult {//图形验证码的keypublic String key;public String image;public String code;
}

ImageCaptchaServiceImpl:

@Slf4j
@Service
public class ImageCaptchaServiceImpl implements ImageCaptchaService {@Value("${edu.captcha.cache-prefix}")private String ConfigCachePrefix;@Value("${edu.captcha.expire}")private Long ConfigExpire;@Resourceprivate DefaultKaptcha defaultKaptcha;@Overridepublic ImageCaptchaResult generate() throws IOException {ImageCaptchaResult imageCaptcha = new ImageCaptchaResult();BufferedImage image;// 图形验证码的key[api是无状态的需要key来锁定验证码的值]String randomKey = HelperUtil.randomString(16);imageCaptcha.setKey(randomKey);// 生成验证码imageCaptcha.setCode(defaultKaptcha.createText());image = defaultKaptcha.createImage(imageCaptcha.getCode());// 写入到redis中RedisUtil.set(getCacheKey(randomKey), imageCaptcha.getCode(), ConfigExpire);FastByteArrayOutputStream os = new FastByteArrayOutputStream();ImageIO.write(image, "png", os);String base64 = "data:image/png;base64," + Base64Util.encode(os.toByteArray());imageCaptcha.setImage(base64);return imageCaptcha;}
}

application.ymal: @Value注解需要读取到的

edu:# 图形验证码captcha:expire: 300 #有效期[单位:秒,默认5分钟]cache-prefix: "captcha:key:" #存储key的前缀

RedisUtil:


import jakarta.annotation.Resource;import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;import java.util.*;
import java.util.concurrent.TimeUnit;@Component
public class RedisUtil {private static RedisTemplate<String, Object> redisTemplate;private static final String redisPrefix = "EDU";/*** 注入Redis** @param redisTemplate Redis对象* @author fzr*/@Resourcepublic void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {RedisUtil.redisTemplate = redisTemplate;}/*** 对象句柄** @return RedisTemplate* @author fzr*/public static RedisTemplate<String, Object> handler() {return redisTemplate;}/*** 指定缓存失效时间** @param key 键* @param second 时间(秒)* @author fzr*/public static void expire(String key, Long second) {key = redisPrefix + key;redisTemplate.expire(key, second, TimeUnit.SECONDS);}/*** 指定缓存失效时间** @param key 键* @param millisecond 时间(毫秒)* @author fzr*/public static void pExpire(String key, Long millisecond) {key = redisPrefix + key;redisTemplate.expire(key, millisecond, TimeUnit.MILLISECONDS);}/*** 指定缓存永久有效** @param key 键* @author fzr*/public static void persist(String key) {key = redisPrefix + key;redisTemplate.persist(key);}/*** 根据key获取过期时间** @param key 键不能为null* @return 返回0代表为永久有效(秒)* @author fzr*/public static Long ttl(String key) {key = redisPrefix + key;return redisTemplate.getExpire(key, TimeUnit.SECONDS);}/*** 根据key获取过期时间** @param key 键不能为null* @return 返回0代表为永久有效(毫秒)* @author fzr*/public static Long pTtl(String key) {key = redisPrefix + key;return redisTemplate.getExpire(key, TimeUnit.MILLISECONDS);}/*** 判断key是否存在** @param key 键* @return true=存在,false=不存在* @author fzr*/public static Boolean exists(String key) {key = redisPrefix + key;return redisTemplate.hasKey(key);}/*** 删除1个或多个键** @param key 键(一个或多个)* @author fzr*/@SuppressWarnings("unchecked")public static void del(String... key) {if (key.length == 1) {key[0] = redisPrefix + key[0];redisTemplate.delete(key[0]);} else {for (int i = 0; key.length > i; i++) {key[i] = redisPrefix + key[i];}redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));}}/*** 给key赋值一个新的key名** @param oldKey 旧的key* @param newKey 新的key* @author fzr*/public static void rename(String oldKey, String newKey) {oldKey = redisPrefix + oldKey;newKey = redisPrefix + newKey;redisTemplate.rename(oldKey, newKey);}/*** 将当前数据库的key移动到给定的数据库db当中** @param key 键* @param db 库* @return Boolean* @author fzr*/public static Boolean move(String key, int db) {key = redisPrefix + key;return redisTemplate.move(key, db);}/*** 获取匹配的key值** @param pattern 通配符(*, ?, [])* @return Set* @author fzr* @author fzr*/public static Set<String> keys(String pattern) {return redisTemplate.keys(pattern);}/*** 随机返回一个key** @return String* @author fzr* @author fzr*/public static String randomKey() {return redisTemplate.randomKey();}/* ***************** common end *************** *//*** 按匹配获取或有KEY** @param pattern 规则* @return Set<String>* @author fzr*/public static Set<String> matchSet(String pattern) {Set<String> keys = new LinkedHashSet<>();RedisUtil.handler().execute((RedisConnection connection) -> {try (Cursor<byte[]> cursor =connection.scan(ScanOptions.scanOptions().count(Long.MAX_VALUE).match(pattern).build())) {cursor.forEachRemaining(item -> {keys.add(RedisSerializer.string().deserialize(item));});return null;} catch (Exception e) {throw new RuntimeException(e);}});return keys;}/*** 获取key的值** @param key 键* @return Object* @author fzr*/public static Object get(String key) {key = redisPrefix + key;return redisTemplate.opsForValue().get(key);}/*** 获取旧值并设置新值** @param key 键* @param newVal 新值* @return Object* @author fzr*/public static Object getSet(String key, Object newVal) {key = redisPrefix + key;return redisTemplate.opsForValue().getAndSet(key, newVal);}/*** 设置键值对** @param key 键* @param value 值* @author fzr*/public static void set(String key, Object value) {key = redisPrefix + key;redisTemplate.opsForValue().set(key, value);}/*** 设置键值对并设置时间** @param key 键* @param value 值* @param time time要大于0 如果time小于等于0 将设置无限期* @author fzr*/public static void set(String key, Object value, long time) {key = redisPrefix + key;if (time > 0) {redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);} else {set(key, value);}}/*** 递增** @param key 键* @param delta 要增加几(大于0)* @return Long* @author fzr*/public static Long incr(String key, long delta) {if (delta < 0) {throw new RuntimeException("递增因子必须大于0");}key = redisPrefix + key;return redisTemplate.opsForValue().increment(key, delta);}/*** 递减** @param key 键* @param delta 要减少几(小于0)* @return Long* @author fzr*/public static Long decr(String key, long delta) {if (delta < 0) {throw new RuntimeException("递减因子必须大于0");}key = redisPrefix + key;return redisTemplate.opsForValue().increment(key, -delta);}/* ***************** String end *************** *//*** 获取key中field域的值** @param key 键 不能为null* @param field 项 不能为null* @return 值* @author fzr*/public static Object hGet(String key, String field) {key = redisPrefix + key;return redisTemplate.opsForHash().get(key, field);}/*** 判断key中有没有field域名** @param key 键* @param field 字段* @return Boolean* @author fzr*/public static Boolean hExists(String key, Object field) {key = redisPrefix + key;return redisTemplate.opsForHash().hasKey(key, field);}/*** 获取hashKey对应的所有键值** @param key 键* @return 对应的多个键值* @author fzr*/public Map<Object, Object> hmGet(String key) {key = redisPrefix + key;return redisTemplate.opsForHash().entries(key);}/*** 设置field1->N个域,对应的值是value1->N** @param key 键* @param map 对应多个键值* @author fzr*/public static void hmSet(String key, Map<String, Object> map) {key = redisPrefix + key;redisTemplate.opsForHash().putAll(key, map);}/*** HashSet 并设置时间** @param key 键* @param map 对应多个键值* @param time 时间(秒)* @author fzr*/public static void hmSet(String key, Map<String, Object> map, long time) {key = redisPrefix + key;redisTemplate.opsForHash().putAll(key, map);if (time > 0) {expire(key, time);}}/*** 向一张hash表中放入数据,如果不存在将创建** @param key 键* @param item 项* @param value 值* @author fzr*/public static void hSet(String key, String item, Object value) {key = redisPrefix + key;redisTemplate.opsForHash().put(key, item, value);}/*** 向一张hash表中放入数据,如果不存在将创建** @param key 键* @param item 项* @param value 值* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间* @return true 成功 false失败* @author fzr*/public static boolean hSet(String key, String item, Object value, long time) {key = redisPrefix + key;redisTemplate.opsForHash().put(key, item, value);if (time > 0) {expire(key, time);}return true;}/*** 删除hash表中的值** @param key 键 不能为null* @param item 项 可以使多个 不能为null* @author fzr*/public static void hDel(String key, Object... item) {key = redisPrefix + key;redisTemplate.opsForHash().delete(key, item);}/*** 判断hash表中是否有该项的值** @param key 键 不能为null* @param item 项 不能为null* @return true 存在 false不存在* @author fzr*/public static boolean hHasKey(String key, String item) {key = redisPrefix + key;return redisTemplate.opsForHash().hasKey(key, item);}/*** hash递增 如果不存在,就会创建一个并把新增后的值返回** @param key 键* @param item 项* @param by 要增加几(大于0)* @return double* @author fzr*/public static double hIncr(String key, String item, long by) {key = redisPrefix + key;return redisTemplate.opsForHash().increment(key, item, by);}/*** hash递减** @param key 键* @param item 项* @param by 要减少记(小于0)* @return double* @author fzr*/public static double hDecr(String key, String item, long by) {key = redisPrefix + key;return redisTemplate.opsForHash().increment(key, item, -by);}/* ***************** Map end *************** *//*** 根据key获取Set中的所有值** @param key 键* @return Set* @author fzr*/public static Set<Object> sGet(String key) {key = redisPrefix + key;return redisTemplate.opsForSet().members(key);}/*** 根据value从一个set中查询,是否存在** @param key 键* @param value 值* @return true 存在 false不存在* @author fzr*/public Boolean sHasKey(String key, Object value) {key = redisPrefix + key;return redisTemplate.opsForSet().isMember(key, value);}/*** 将数据放入set缓存** @param key 键* @param values 值 可以是多个* @return 成功个数* @author fzr*/public static Long sSet(String key, Object... values) {key = redisPrefix + key;return redisTemplate.opsForSet().add(key, values);}/*** 将set数据放入缓存** @param key 键* @param time 时间(秒)* @param values 值 可以是多个* @return 成功个数* @author fzr*/public Long sSetAndTime(String key, long time, Object... values) {key = redisPrefix + key;return redisTemplate.opsForSet().add(key, values);}/*** 获取set缓存的长度** @param key 键* @return Long* @author fzr*/public Long sGetSetSize(String key) {key = redisPrefix + key;return redisTemplate.opsForSet().size(key);}/*** 移除值为value的** @param key 键* @param values 值 可以是多个* @return 移除的个数* @author fzr*/public Long setRemove(String key, Object... values) {key = redisPrefix + key;return redisTemplate.opsForSet().remove(key, values);}/* ***************** Set end *************** *//*** 获取list缓存的内容** @param key 键* @param start 开始* @param end 结束 0 到 -1代表所有值* @return List* @author fzr*/public List<Object> lGet(String key, long start, long end) {key = redisPrefix + key;return redisTemplate.opsForList().range(key, start, end);}/*** 获取list缓存的长度** @param key 键* @return Long* @author fzr*/public Long lGetListSize(String key) {key = redisPrefix + key;return redisTemplate.opsForList().size(key);}/*** 通过索引获取list中的值** @param key 键* @param index 索引 index>=0时,0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推* @return Object* @author fzr*/public Object lGetIndex(String key, long index) {key = redisPrefix + key;return redisTemplate.opsForList().index(key, index);}/*** 将list放入缓存** @param key 键* @param value 值* @return boolean* @author fzr*/public boolean lSet(String key, Object value) {key = redisPrefix + key;redisTemplate.opsForList().rightPush(key, value);return true;}/*** 将list放入缓存** @param key 键* @param value 值* @param second 时间(秒)* @return boolean* @author fzr*/public boolean lSet(String key, Object value, long second) {key = redisPrefix + key;redisTemplate.opsForList().rightPush(key, value);if (second > 0) expire(key, second);return true;}/*** 将list放入缓存** @param key 键* @param value 值* @return boolean* @author fzr*/public boolean lSet(String key, List<Object> value) {key = redisPrefix + key;redisTemplate.opsForList().rightPushAll(key, value);return true;}/*** 将list放入缓存** @param key 键* @param value 值* @param time 时间(秒)* @return boolean* @author fzr*/public boolean lSet(String key, List<Object> value, Long time) {key = redisPrefix + key;redisTemplate.opsForList().rightPushAll(key, value);if (time > 0) expire(key, time);return true;}/*** 根据索引修改list中的某条数据** @param key 键* @param index 索引* @param value 值* @return boolean* @author fzr*/public boolean lUpdateIndex(String key, Long index, Object value) {key = redisPrefix + key;redisTemplate.opsForList().set(key, index, value);return true;}/*** 移除N个值为value** @param key 键* @param count 移除多少个* @param value 值* @return 移除的个数* @author fzr*/public Long lRemove(String key, Long count, Object value) {key = redisPrefix + key;return redisTemplate.opsForList().remove(key, count, value);}
}

KaptchaConfig:用于配置 Kaptcha 验证码生成器的属性,给DefaultKaptcha进行配置属性,可以说是给图片验证码增加属性。

/*** 图片验证码的配置类* */
@Configuration
public class KaptchaConfig {@Bean(name = "captchaProducer")public DefaultKaptcha getKaptchaBean() {DefaultKaptcha defaultKaptcha = new DefaultKaptcha();Properties properties = new Properties();// 是否边框properties.setProperty(KAPTCHA_BORDER, "no");// 字符颜色properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "red");// 干扰线颜色properties.setProperty(KAPTCHA_NOISE_COLOR, "red");// 字符间距properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "5");// 图片宽度properties.setProperty(KAPTCHA_IMAGE_WIDTH, "150");// 图片高度properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "50");// 字符大小properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "40");// 字符长度properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");// 字体样式properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");/// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpyproperties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.FishEyeGimpy");defaultKaptcha.setConfig(new Config(properties));return defaultKaptcha;}
}

如果你想要设置图片验证码的相关属性可以对properties进行对应的设置和修改即可。

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

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

相关文章

大数据开发中的秘密武器:探索Hadoop纠删码的奇妙世界

随着大数据技术的发展&#xff0c;HDFS作为Hadoop的核心模块之一得到了广泛的应用。为了系统的可靠性&#xff0c;HDFS通过复制来实现这种机制。但在HDFS中每一份数据都有两个副本&#xff0c;这也使得存储利用率仅为1/3&#xff0c;每TB数据都需要占用3TB的存储空间。因此&…

极智项目 | 实战人脸识别签到系统

欢迎关注我的公众号 [极智视界]&#xff0c;获取我的更多经验分享 大家好&#xff0c;我是极智视界&#xff0c;本文来介绍 实战人脸识别签到系统。 本文介绍的 实战人脸识别签到系统&#xff0c;提供完整的可以一键执行的项目工程源码&#xff0c;获取方式有两个&#xff1a…

新晋技术管理者如何推动组织变革?

技术管理者需要不断地努力改善团队状况&#xff0c;比如提升研发效能、帮助成员成长&#xff0c;或者优化组织结构等等。可以说&#xff0c;推动变革是「技术管理者」这一角色的重要使命之一。 关于变革的挑战总是复杂&#xff0c;而如何在不同的环境和问题中影响团队也是一项…

2023年【起重机械指挥】考试题库及起重机械指挥证考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 起重机械指挥考试题库是安全生产模拟考试一点通总题库中生成的一套起重机械指挥证考试&#xff0c;安全生产模拟考试一点通上起重机械指挥作业手机同步练习。2023年【起重机械指挥】考试题库及起重机械指挥证考试 1、…

Tengine 边缘AI计算框架移植RV1126(包括opencv的交叉编译)

目录 1.编译opencv 2.拷贝SDK源码到虚拟机 3. 拉取TIM-VX代码 4.拉取Tengine源码并配置 1.编译opencv 编译opencv是为了&#xff0c;在编译Tengine时指定OpenCVConfig.cmake,以便寻找特定的opencv动态库 01.从github拉取opencv源代码 git clone -b 4.5.5 https://github.co…

22.项目开发之量化交易抓取数据QuantTradeData(一)

项目创建及后端业务&#xff1a;定时更新“股票列表基础信息”数据 项目创建 该量化交易数据平台用于数据库的数据抓取、分析等操作。 和QuantTrade使用同一个数据库&#xff0c;无需重新创建 pom.xml <properties><java.version>1.8</java.version><pro…

华为数通方向HCIP-DataCom H12-831题库(单选题:301-310)

第301题 关于配置防火墙安全区域的安全级别的描述,错误的是 A、同一系统中,两个安全区域不允许配置相同的安全级别 B、只能为自定义的安全区域设定安全级别 C、安全级别一旦设定不允许更改 D、新建的安全区域,系统默认其安全级别为1 答案:D 解析: 新创建的安全区域缺省未…

在UniApp中使用uni.makePhoneCall方法调起电话拨打功能

目录 1.在manifest.json文件中添加权限 2. 组件中如何定义 3.如何授权 4.相关知识点总结 1.在manifest.json文件中添加权限 {"permissions": {"makePhoneCall": {"desc": "用于拨打电话"}} }2. 组件中如何定义 <template>…

成都瀚网科技有限公司:开抖音店铺有哪些注意事项?

成功经营一个小店不仅仅是发布产品视频那么简单&#xff0c;还需要注意一些重要的事情。开抖音店铺需要注意以下几点&#xff1a; 1、开抖音店铺有哪些注意事项&#xff1f; 合规管理&#xff1a;在抖音开店&#xff0c;首先要确保自己的运营合规。遵守相关法律法规及平台规定&…

分享一下怎么开发一个陪诊小程序

开发一个陪诊小程序需要综合考虑许多方面&#xff0c;包括但不限于市场需求、用户体验、技术实现和运营策略。以下是一篇以开发陪诊小程序为主题的文章。 一、背景介绍 随着社会的发展和人口老龄化的加剧&#xff0c;越来越多的老年人、病患和孕妇需要就医&#xff0c;而由于各…

地理知识笔记:Haversine距离

1 介绍 Haversine距离用于计算地球上两点之间的大圆距离当考虑地球的真实曲率时&#xff0c;它特别适用于计算两个经纬度坐标之间的距离 其中&#xff1a; 2 python 实现 def haversine_distance(lat1, lon1, lat2, lon2):R 6371 # Earth radius in kilometersdlat np.r…

斯坦福JSKarel编程机器人使用介绍

斯坦福JSKarel编程机器人使用介绍 为了避免被编程语言固有的复杂性所困扰&#xff0c;有一个被称为卡雷尔&#xff08;Karel&#xff09;机器人的微型世界&#xff08;microworld&#xff09;的简化环境&#xff0c;可以让编程初学者从中学习理解编程的基本概念&#xff0c;而…

限频差分探头N2060Apro

N2060Apro 差分探头提供一个安全的仪器给所有的示波器使用&#xff0c;它可以转换由高输入的差动电压进入一个低电压(≤7V)&#xff0c;并且显示波形在示波器上&#xff0c;使用频宽高达 200MHz&#xff0c;非常适合大电力测试、研发、维修使用。差分探头输出标示是设计在操作示…

【六、docker中hyperf项目怎么进行跨域设置】

1、第一步就是新建跨域文件,即跨域中间件 跨域中间件的代码如下 <?phpdeclare(strict_types=1);namespace App\Middleware; namespace App\Middleware; namespace App\Middleware;use Hyperf\Context\Context; use Psr\Http\Message\ResponseInterface;

【网络编程】从网络编程、TCP/IP开始到BIO、NIO入门知识(未完待续...)

目录 前言前置知识一、计算机网络体系结构二、TCP/IP协议族2.1 简介*2.2 TCP/IP网络传输中的数据2.3 地址和端口号2.4 小总结 三、TCP/UDP特性3.1 TCP特性TCP 3次握手TCP 4次挥手TCP头部结构体 3.2 UDP特性 四、总结 课程内容一、网络通信编程基础知识1.1 什么是Socket1.2 长连…

python代码调用文件或数据库中保存的脚本

这里采用的读取excel 1、先写一个测试方法 def demo5():import xlrdimport randomwb xlrd.open_workbook("code.xls")st wb.sheet_by_index(0)code st.cell_value(0, 0)list ["6666", asd, 1ad23, 1f23, 12g3, 1b3, 12r3]code2 st.cell_value(0, 1)…

ubuntu20.04安装FTP服务

安装 sudo apt-get install vsftpd# 设置开机启动并启动ftp服务 systemctl enable vsftpd systemctl start vsftpd#查看其运行状态 systemctl status vsftpd #重启服务 systemctl restart vsftpdftp用户 sudo useradd -d /home/ftp/ftptest -m ftptest sudo passwd ftptest…

蓝桥杯双周赛算法心得——三带一(暴力枚举)

大家好&#xff0c;我是晴天学长&#xff0c;枚举思想&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。 1) .三带一 2) .算法思路 1.通过Scanner读取输入的整数n&#xff0c;表示接下来有n个字符串需要处理。 2.使用循环遍历每个字符串&#xff1a;…

面试算法29:排序的循环链表

问题 在一个循环链表中节点的值递增排序&#xff0c;请设计一个算法在该循环链表中插入节点&#xff0c;并保证插入节点之后的循环链表仍然是排序的。 分析 首先分析在排序的循环链表中插入节点的规律。当在图4.15&#xff08;a&#xff09;的链表中插入值为4的节点时&…

【精选】目前我国网络安全人才市场状况

网络安全人才市场状况 本章以智联招聘多年来形成的丰富的招聘、求职信息大数据为基础&#xff0c;结合了奇安信集团 在网络安全领域多年来的专业研究经验&#xff0c;相关研究成果具有很强的代表性。对涉及安全人才 的全平台招聘需求与求职简历进行分析&#xff08;注&#xf…