SpringBoot整合SpringScurity权限控制(菜单权限,按钮权限)以及加上SSH实现安全传输
文章目录
- 项目地址:
- 一、md5 与 先进的哈希算法的区别
- 1.1. 安全性问题
- 1.2. 设计目的
- 1.3. 功能特性
- 1.4. 适用性
- 1.5. 总结
- 二、数据传输安全和数据加密实现:
- 2.1 生成证书:
- 2.2、在springboot中进行集成
- 2.2.1 配置证书:
- 2.2.2. 强制使用 HTTPS
- 2.2.2.1 实现过程
- 2.2.3 使用bcrypt 进行密码加密
- 2.2.3.1实现过程
- 2.2.3.2 BCryptPasswordEncoder 的实现逻辑
- 三、权限控制:
- 3.1、菜单权限控制
- 3.1.1具体步骤:
- 3.2、按钮权限:
- 3.2.1 具体步骤:
- 3.2.2 流程图
- 别的
- 在Springboot中使用Argon2进行密码加密 不依靠 SpringScurity
- 1. 添加 Argon2 库的依赖
- 2. 散列密码
- 3. 验证密码
- 4. 使用例子
- 验证逻辑
- 小结
项目地址:
https://github.com/yszhdhy/ItemSet/tree/master/java/secureAndSpringScurity
项目结构:
一、md5 与 先进的哈希算法的区别
先进的hash算法有( bcrypt、PBKDF2 或 Argon2)
实际上,使用 bcrypt、PBKDF2 或 Argon2 进行密码散列与使用 MD5 有着显著的区别,主要在于安全性和适用性。虽然 MD5 曾广泛用于密码散列,但它现在被认为是不安全的选择,主要原因如下:
1.1. 安全性问题
- 快速计算:MD5 设计用于快速计算文件或数据的散列值,这使得它在抵抗暴力破解攻击方面表现不佳。攻击者可以使用现代的硬件在极短的时间内尝试数十亿个密码组合。
- 碰撞弱点:MD5 已经被证明存在碰撞弱点,即可以找到两个不同的输入产生相同的输出。这种特性使其不适合进行密码存储或任何需要高安全性的场景。
1.2. 设计目的
- MD5:最初设计为快速计算数据的散列,用于检查文件完整性,而非密码存储。
- bcrypt/PBKDF2/Argon2:特别为密码存储设计,包括对抗散列速度过快的攻击手段,如密钥拉伸和盐值使用,这些都是为了增加密码破解的难度。
1.3. 功能特性
- 密钥拉伸:bcrypt、PBKDF2 和 Argon2 实现了密钥拉伸功能,可以通过增加计算的时间和资源需求来有效抵抗暴力破解攻击。
- 盐值使用:这些算法在散列密码时使用盐值,可以有效防止彩虹表攻击,即使是两个相同的密码,在加了不同的盐之后,产生的散列值也会不同。
1.4. 适用性
- 广泛适用:虽然你提到“大多数项目都是使用 MD5 进行加密”,但实际上,在安全性意识提高和数据泄露事件频发的当下,许多现代应用和安全标准推荐使用 bcrypt、PBKDF2 或 Argon2 作为密码存储的首选方法。
1.5. 总结
如果你在设计一个需要存储用户密码的系统,强烈建议使用 bcrypt、PBKDF2 或 Argon2 而不是 MD5。这些现代算法提供了更高级别的安全保障,可以帮助你的系统抵御当前的威胁,如暴力破解和彩虹表攻击。选择正确的密码存储策略是保护用户数据安全的关键一步。
二、数据传输安全和数据加密实现:
2.1 生成证书:
-
执行命令
keytool -genkeypair -alias mydomain -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore mykeystore.p12 -validity 3650 -storepass yourpassword
命令详解:
keytool
: 这是 Java 提供的一个用于管理密钥和证书的命令行工具。-genkeypair
: 这个参数告诉 keytool 生成一个密钥对,包括一个公钥和一个相应的私钥。-alias mydomain
: 这个参数指定了密钥对的别名。在之后的操作中,可以使用这个别名来引用这个密钥对。-keyalg RSA
: 这个参数指定了要使用的密钥算法。在这种情况下,RSA 算法被选用。-keysize 2048
: 这个参数指定了生成的密钥的位长度。在这个例子中,密钥的长度为 2048 位。-storetype PKCS12
: 这个参数指定了密钥库的类型。在这里,选择了 PKCS#12 格式。-keystore mykeystore.p12
: 这个参数指定了要生成的密钥库的文件名。在这个例子中,生成的密钥库将被命名为mykeystore.p12
。-validity 3650
: 这个参数指定了生成的证书的有效期限。在这里,证书的有效期限被设置为 3650 天(大约 10 年)。-storepass yourpassword
: 这个参数指定了密钥库的密码。在这个例子中,密码被设置为yourpassword
。请确保将yourpassword
替换为你自己的密码。
2.2、在springboot中进行集成
2.2.1 配置证书:
-
将生成的证书放置在resource目录下
-
配置yaml
spring:application:name: demoserver:ssl:key-store: "src/main/resources/mykeystore.p12"key-store-password: xu123456keyStoreType: PKCS12 # 存储加密信息的标准文件格式keyAlias: mydomain # 别名用于从密钥库中引用具体的密钥port: 8840
-
启动项目并使用cmd 进行测试
总结:
完成以上操作后我们的所有的请求 都需要使用https进行访问。
2.2.2. 强制使用 HTTPS
2.2.2.1 实现过程
-
首先导入本次项目所需要的所有依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</version></dependency><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.3</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-config</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.21</version></dependency></dependencies>
-
配置yaml文件
spring:application:name: demodatasource:password: '123456'driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/ceshi?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghaiusername: rootmvc:format:date: yyyy-MM-dddate-time: yyyy-MM-dd HH:mm:ssserver:port: 8840 # HTTP端口ssl:enabled: trueport: 8843 # HTTPS端口key-store: "src/main/resources/mykeystore.p12"key-store-password: xu123456keyStoreType: PKCS12keyAlias: mydomain
-
搭建security配置文件
WebSecurityConfig
-
考虑在你的应用中强制使用 HTTPS,以避免数据通过不安全的 HTTP 发送。在 Spring Security 中,可以通过配置安全性策略来实现:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.requiresChannel() //指定了需要使用的通道类型.anyRequest() // 指定了所有的请求.requiresSecure(); // 指定了需要使用安全的HTTPS通道} }
-
这样配置后,所有请求都会被重定向到 HTTPS (即使用户输入http)。
2.2.3 使用bcrypt 进行密码加密
2.2.3.1实现过程
-
创建密码加密配置文件
SecurityConfig
在添加用户业务中可以使用此BCryptPasswordEncoder.encode()
方法进行加密用户密码并存储在数据库中package com.example.demo.config.passwordEncoder;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;/*** @author CaptureOrNew* @description //进行密码加密配置* @date 19:29:12 2024/4/19* @return null**/ @Configuration public class SecurityConfig {// 进行编码配置 创建一个BCryptPasswordEncoder的实例@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}// 配置其他安全措施... }
-
创建一个密码编辑器
package com.example.demo.security.custom;import com.example.demo.config.passwordEncoder.SecurityConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component;/*** @author CaptureOrNew* @description //创建一个bcrypt 加密器* @date 19:28:21 2024/4/19* @Param* @return null**/ @Component public class CustomBcryptPasswordEncoder implements PasswordEncoder {@AutowiredSecurityConfig securityConfig;// 进行密码加密操作public String encode(CharSequence rawPassword) {return securityConfig.passwordEncoder().encode(rawPassword.toString());}// 进行密码比对public boolean matches(CharSequence rawPassword, String encodedPassword) {// security 会自动进行密码的比对return securityConfig.passwordEncoder().matches(rawPassword, encodedPassword);} }
-
创建一个 UserDetailsService 接口 以及下面的实现类进行 数据库用户查询和一些鉴权判断
package com.example.demo.security.custom;import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component;@Component public interface UserDetailsService extends org.springframework.security.core.userdetails.UserDetailsService {/*** 根据用户名获取用户对象(获取不到直接抛异常)*/@OverrideUserDetails loadUserByUsername(String username) throws UsernameNotFoundException; }
-
实现类
package com.example.demo.service.impl;import com.example.demo.model.model.SysUser; import com.example.demo.security.custom.CustomUser; import com.example.demo.service.SysUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service;import java.util.ArrayList; import java.util.List;@Service public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate SysUserService sysUserService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//根据用户名进行查询SysUser sysUser = sysUserService.getUserByUserName(username);if(null == sysUser) {throw new UsernameNotFoundException("用户名不存在!");}if(sysUser.getStatus().intValue() == 0) {throw new RuntimeException("账号已停用");}List<SimpleGrantedAuthority> authList = new ArrayList<>();return new CustomUser(sysUser, authList);} }
2.2.3.2 BCryptPasswordEncoder 的实现逻辑
- 加密 (
encode
) 过程:- 用户提供一个明文密码。
- 生成一个随机盐值(通常是每次加密时生成的)。
- 使用明文密码和生成的盐值,通过 BCrypt 算法进行多轮散列处理。
- 将盐值和散列结果合并成一个字符串(通常在字符串中包含盐值、散列值和用于 BCrypt 的成本参数)。
- 返回这个合并后的字符串作为最终的存储散列。
- 验证 (
matches
) 过程:- 用户尝试登录时提供一个明文密码。
- 取出数据库中存储的散列密码,这个散列包含了用于该特定密码的盐值。
- 使用存储的盐值和用户提供的明文密码再次执行相同的 BCrypt 散列过程。
- 比较新生成的散列结果与存储的散列值是否一致。
- 如果两者一致,说明密码匹配,用户验证成功;如果不一致,则验证失败。
- 流程图:
+-----------------------+| 用户注册 |+-----------------------+| 输入明文密码 |+-----------------------+↓+-----------------------+| 生成随机盐值 |+-----------------------+↓+-----------------------+| 使用 BCrypt 加密 || (密码 + 盐值) |+-----------------------+↓+-----------------------+| 存储散列密码到数据库 |+-----------------------+↓+-----------------------+| 用户登录 |+-----------------------+| 输入明文密码 |+-----------------------+↓+-----------------------+| 读取存储的散列密码 || (包含盐值) |+-----------------------+↓+-----------------------+| 使用读取的盐值重新 || 加密输入的明文密码 |+-----------------------+↓+-----------------------+| 比较两个散列值是否相同|+-----------------------+↓+-----------------------+| 相同: 验证成功 || 不同: 验证失败 |+-----------------------+
三、权限控制:
以下操作基于RBAC (角色访问控制) 权限管理模型
给用户分配角色 ——> 给角色分配权限(可对项目进行的操作)
3.1、菜单权限控制
在用户每次登录系统获取信息时,后端通过用户名和密码在数据库中进行查找此用户,如果查询到此用户就根据用户所处的角色及角色所能操作的菜单查询出来将查询到的菜单组装为前端的路由形式进行返回给前端。这样就实现了根据用户的身份角色不同,客户端就会展示相应的菜单等。
3.1.1具体步骤:
- 用户登录后紧接着获取菜单信息
- 从请求头获取用户信息即token
- 从数据在token中解析出登录用户的ID
- 根据ID查询用户信息
- 根据用户信息查询用户可操作的菜单
- 组装菜单并返回
- 前端通过动态路由的形式进行插入到项目中。
3.2、按钮权限:
根据用户可以操作的按钮进行来控制那些后端方法也就是接口是可以被用户所访问的(CURD)。
3.2.1 具体步骤:
-
前端中会根据用户登录后获取信息的接口获取到按钮权限。在前端中使用获取到的按钮权限信息对相应的按钮进行禁用或其他操作
-
我们后端中 security 配置类 TokenAuthenticationFilter 中进行配置获取用户按钮权限的操作并存入到security 身上
@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain chain) throws ServletException, IOException {//如果是登录接口,直接放行if ("/admin/system/index/login".equals(request.getRequestURI())) {chain.doFilter(request, response);return;}// 获取校验信息UsernamePasswordAuthenticationToken authentication = getAuthentication(request);if (null != authentication) {SecurityContextHolder.getContext().setAuthentication(authentication);chain.doFilter(request, response);} else {ResponseUtil.out(response, Result.build(null, ResultCodeEnum.LOGIN_ERROR));}}private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {//请求头是否有tokenString username = request.getHeader("token");if (!StringUtils.isEmpty(username)) {String authListString = request.getHeader("authorities");if (!StringUtils.isEmpty(authListString)) {System.out.println(authListString+"================>");String ccc = authListString;//把redis获取字符串权限数据转换要求集合类型 List<SimpleGrantedAuthority>List<Map> maplist = JSON.parseArray(ccc, Map.class);System.out.println(maplist);List<SimpleGrantedAuthority> authList = new ArrayList<>();for (Map map : maplist) {String authority = (String) map.get("authority");authList.add(new SimpleGrantedAuthority(authority));}return new UsernamePasswordAuthenticationToken(username, null, authList);} else {return new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());}}return null;}
-
这样我们就可以直接调用security中的一个注解进行 校验:
@PreAuthorize("hasAuthority('bnt.sysUser.list')") /*** @author CaptureOrNew* @description //查询用户列表* @date 09:46:22 2024/4/20* @PreAuthorize 标注在方法上,表示访问方法的用户需要有对应的权限* @return com.example.demo.result.Result<java.util.List < com.example.demo.model.model.SysUser>>**/@PreAuthorize("hasAuthority('bnt.sysUser.list')")@GetMapping("getUserList")public Result<List<SysUser>> getUserList() {List<SysUser> list = sysUserService.list();return list != null ? Result.ok(list) : Result.build(null, LOGIN_ERROR);}
-
测试验证数据 这里我们是将权限放在header中了 ,没有使用redis
const axios = require('axios');let config = {method: 'get',maxBodyLength: Infinity,url: 'https://localhost:8840/admin/getUserList',headers: { 'token': 'admin', 'authorities': '[{"authority":"bnt.sysUser.remove"},{"authority":"bnt.sysUser.add"},{"authority":"bnt.sysUser.update"}]'} };axios.request(config) .then((response) => {console.log(JSON.stringify(response.data)); }) .catch((error) => {console.log(error); });
3.2.2 流程图
-
用户登录
-
用户添加
别的
在Springboot中使用Argon2进行密码加密 不依靠 SpringScurity
1. 添加 Argon2 库的依赖
如果你的项目中使用 Java,你可以添加如下依赖(例如使用 argon2-jvm
库):
xmlCopy code<!-- Maven 依赖 -->
<dependency><groupId>de.mkammerer</groupId><artifactId>argon2-jvm</artifactId><version>2.8</version>
</dependency>
2. 散列密码
创建一个服务类,实现密码的散列功能。这里是一个简单的例子,展示如何使用 Argon2 进行密码散列:
javaCopy codeimport de.mkammerer.argon2.Argon2;
import de.mkammerer.argon2.Argon2Factory;public class PasswordService {private Argon2 argon2 = Argon2Factory.create();public String hashPassword(String password) {// 参数解释: 迭代次数, 内存使用量, 并行度表示算法同时使用的线程数或CPU核心数return argon2.hash(2, 65536, 1, password.toCharArray());}
}
3. 验证密码
验证过程中,你需要比较用户输入的密码与数据库中存储的散列值是否匹配。这可以通过 Argon2 的验证功能来实现:
javaCopy codepublic boolean verifyPassword(String inputPassword, String storedHash) {return argon2.verify(storedHash, inputPassword.toCharArray());
}
4. 使用例子
在你的业务逻辑中,可以使用这个服务来处理用户的密码。例如,在用户注册时散列密码,用户登录时验证密码:
javaCopy codepublic class UserService {private PasswordService passwordService = new PasswordService();// 用户注册public void registerUser(String username, String password) {String hashedPassword = passwordService.hashPassword(password);// 存储 username 和 hashedPassword 到数据库}// 用户登录public boolean loginUser(String username, String inputPassword) {// 从数据库获取存储的散列密码String storedHash = ...;return passwordService.verifyPassword(inputPassword, storedHash);}
}
验证逻辑
Argon2 的验证逻辑内部会处理与散列值相关的盐值和参数配置,所以你不需要手动处理盐值。当你调用 argon2.verify
方法时,它会解析存储的散列值,自动提取盐值和散列参数,然后使用这些信息来验证输入密码。
小结
使用 Argon2 手动处理密码散列和验证确实比直接使用 Spring Security 稍复杂一些,但这提供了更高的灵活性和控制,适合不使用 Spring Security 的项目。确保在实际部署中正确管理密码相关的安全细节,如使用安全的参数配置和及时更新依赖库。
相关文章:

SpringBoot整合SpringScurity权限控制(菜单权限,按钮权限)以及加上SSH实现安全传输
文章目录 项目地址: 一、md5 与 先进的哈希算法的区别1.1. 安全性问题1.2. 设计目的1.3. 功能特性1.4. 适用性1.5. 总结 二、数据传输安全和数据加密实现:2.1 生成证书:2.2、在springboot中进行集成2.2.1 配置证书:2.2.2. 强制使用…...

力扣每日一题119:杨辉三角||
题目 简单 给定一个非负索引 rowIndex,返回「杨辉三角」的第 rowIndex 行。 在「杨辉三角」中,每个数是它左上方和右上方的数的和。 示例 1: 输入: rowIndex 3 输出: [1,3,3,1]示例 2: 输入: rowIndex 0 输出: [1]示例 3: 输入: rowIndex 1 输出…...

AI语音模型PaddleSpeech踩坑(安装)指南
PaddleSpeech简介 PaddleSpeech 是基于飞桨 PaddlePaddle 的语音方向的开源模型库,用于语音和音频中的各种关键任务的开发,包含大量基于深度学习前沿和有影响力的模型。 PaddleSpeech安装步骤 提示:要找到一个合适的PaddleSpeech版本与pad…...

如何更好地使用Kafka? - 运行监控篇
要确保Kafka在使用过程中的稳定性,需要从kafka在业务中的使用周期进行依次保障。主要可以分为:事先预防(通过规范的使用、开发,预防问题产生)、运行时监控(保障集群稳定,出问题能及时发现&#…...

数据可视化训练第四天(模拟投掷筛子并且统计频次)
投掷一个筛子 import matplotlib.pyplot as plt from random import randint import numpy as npclass Die:"""模拟投掷筛子"""def __init__(self,num_sides6):self.num_sidesnum_sidesdef roll(self):return randint(1,self.num_sides)num1000…...

4.1 编写程序,从键盘接收一个小写字母,然后找出他的前导字符和后续字符,再按顺序显示这三个字符
方法一: 运行效果: 输入B,输出显示ABC;输入A,输出显示AB 思路: 1、通过键盘输入接收一个字母。 2、将输入的字母减去1,得到前导字符,然后输出。 3、将输入的字母加上1,得…...
(Java)心得:LeetCode——18.四数之和
一、原题 给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复): …...

网络编程套接字详解
目录 1. 预备介绍 2.网络字节序 3.udp网络程序 4.地址转换函数 5.udp网络编程 1.预备介绍 1.1源IP地址和目标IP地址 举个例子: 从北京出发到上海旅游, 那么源IP地址就是北京, 目标IP地址就是上海. 1.2 端口号 作用: 标识一个进程, 告诉OS这个数据交给那个进程来处理; (1)…...
蓝桥杯备战11.歌唱比赛
P5738 【深基7.例4】歌唱比赛 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) #include<bits/stdc.h> #define endl \n #define int long long using namespace std; const int N 2e710,M 1e310; int a[N],sum[N];signed main() {//std::ios::sync_with_stdio(0),cin.…...
微信小程序中的图像奥秘:图片与Base64的华丽变身记
微信小程序中的图像奥秘:图片与Base64的华丽变身记 基本概念解析图片与Base64的关系为何转换 图片转Base64实战微信小程序使用wx.getImageInfo获取图片信息图片转换为Base64注意 Base64转图片直接在小程序页面显示云开发环境转换注意 遇遇问题排查思路结语引发讨论 …...

【35分钟掌握金融风控策略25】定额策略实战2
目录 基于收入和负债的定额策略 确定托底额度和盖帽额度 确定基础额度 基于客户风险评级确定风险系数 计算最终授信额度 确定授信有效期 基于收入和负债的定额策略 在实际生产中,客户的收入和负债数据大多无法直接获得,对于个人的收入和负债数据&…...
我和爬虫的故事
文章目录 爬虫简介个人经历未来总结 爬虫简介 网络爬虫(又称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外…...

常用的简单友好的工单系统(免费)- WGCAT
最近在项目中,有工单系统的需求场景,所以想寻找一款轻量简单的运维工单软件,主要用来记录和处理工作中的一些故障、维护,主要用来记录设备的维护状态,包括服务器、主机、交换机那些 WGCAT,是一款简单轻量的…...

使用Pycharm编写Python程序时对基本类结构中方法的重写的两种初步操作方式
使用Pycharm编写Python程序时对基本类结构中方法的重写的两种初步操作方式 Python和其他一些高级面向对象的编程语言中,子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改&…...
HTTP URL 详解
概述 URL 提供了一种定位因特网上任意资源的手段,大多数 URL 语法都由以下九个结构的通用格式组成: <scheme>://<user>:<password><host>:<port>/<path>;<params>?<query>#<frag> 方案&#…...
Python 原生爬虫
Python 描述代码 描述 爬网站的页面配合正则表达式设置定时任务 仅学习参考,切勿使用其他用途 代码 import re import schedule import timefrom urllib.request import urlopenclass Spider:def __init__(self):# 初始化代码...pass# self.start_schedule()# 需要…...

数据结构---经典链表OJ
乐观学习,乐观生活,才能不断前进啊!!! 我的主页:optimistic_chen 我的专栏:c语言 点击主页:optimistic_chen和专栏:c语言, 创作不易,大佬们点赞鼓…...
HTML_CSS学习:CSS像素与颜色
一、像素 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>像素</title> </head><style>.atguigu1{/*单位可以是cm,但不能是m,dm*/width: 1cm;height: 1cm;background-c…...

华为交换机配置导出备份python脚本
一、脚本编写思路 (一)针对设备型号 主要针对华为(Huawei)和华三(H3C)交换机设备的配置备份 (二)导出前预处理 1.在配置导出前,自动打开crt软件或者MobaXterm软件&am…...

DS:时间复杂度和空间复杂度
欢迎各位来到 Harper.Lee 的学习世界! 博主主页传送门:Harper.Lee的博客主页 想要一起进步的uu欢迎来后台找我哦! 本片博客主要介绍的是数据结构中关于算法的时间复杂度和空间复杂度的概念。 一、算法 1.1 什么是算法? 算法(Alg…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...

WPF八大法则:告别模态窗口卡顿
⚙️ 核心问题:阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程,导致后续逻辑无法执行: var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题:…...

通过MicroSip配置自己的freeswitch服务器进行调试记录
之前用docker安装的freeswitch的,启动是正常的, 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...

【1】跨越技术栈鸿沟:字节跳动开源TRAE AI编程IDE的实战体验
2024年初,人工智能编程工具领域发生了一次静默的变革。当字节跳动宣布退出其TRAE项目(一款融合大型语言模型能力的云端AI编程IDE)时,技术社区曾短暂叹息。然而这一退场并非终点——通过开源社区的接力,TRAE在WayToAGI等…...
raid存储技术
1. 存储技术概念 数据存储架构是对数据存储方式、存储设备及相关组件的组织和规划,涵盖存储系统的布局、数据存储策略等,它明确数据如何存储、管理与访问,为数据的安全、高效使用提供支撑。 由计算机中一组存储设备、控制部件和管理信息调度的…...