SpringSecurity01

目录

一、权限控制

二、相关框架

1、shiro

2、springsecurity

三、springsecurity使用流程

1、搭建环境实现默认用户名和密码登录

2、使用数据库表中定义好的用户名和密码访问实现等值密码匹配

1)sql文件

2)搭建jdbc或者mybatis或者mybatis-plus环境

3)配置mybatis-plus环境

4)使用mybatisX 生成基本的service和mapper

5)创建一个类实现UserDetailsService接口 ,重写方法(根据用户名到数据库中查找用户对象)

6)创建一个LoginUser实现UserDetails接口,封装用户信息

3、加密机制 自定义密码匹配器

1)创建加密对象

2)使用测试类,生成临时密码,测试匹配方法

4、自定义登录接口

5、token字符串的生成


一、权限控制

1、认证:是否登录成功。

2、授权:权限控制,授予权限、校验权限。

二、相关框架

1、shiro

Shiro 是 apache权限框架,较之 JAAS 和 Spring Security,Shiro 在保持强大功能的同时,还在简单性和灵活性方面拥有巨大优势。 Shiro 是一个强大而灵活的开源安全框架,能够非常清晰的处理认证授权管理会话以及密码加密

2、springsecurity

Spring Security 是 Spring家族中的一个安全管理框架。相比与另外一个安全框架Shiro,它提供了更丰富的功能,社区资源也比Shiro丰富。

一般来说中大型的项目都是使用SpringSecurity来做安全框架。

小项目用Shiro的比较多,因为相比与SpringSecurity,Shiro的上手更加的简单。

springsecurity 和 其他组件的简单交互

前后端分离场景下,登录校验流程的核心思路:token。 Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生 成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。

使用token机制的身份验证方法,在服务器端不需要存储用户的登录记录。

JWT:JWT全面解读、详细使用步骤 - 简书

三、springsecurity使用流程

1、搭建环境实现默认用户名和密码登录

springboot环境+springweb+springsecurity权限jar包

访问目标方法时,自动跳转到/login接口,输入用户名user和默认密码,登录成功后,自动跳转到目标方法。

2、使用数据库表中定义好的用户名和密码访问实现等值密码匹配

1)sql文件

DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`user_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'NULL' COMMENT '用户名',`nick_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'NULL' COMMENT '昵称',`password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'NULL' COMMENT '密码',`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '账号状态(0正常 1停用)',`email` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '邮箱',`phonenumber` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '手机号',`sex` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户性别(0男,1女,2未知)',`avatar` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '头像',`user_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '1' COMMENT '用户类型(0管理员,1普通用户)',`create_by` bigint(20) NULL DEFAULT NULL COMMENT '创建人的用户id',`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',`update_by` bigint(20) NULL DEFAULT NULL COMMENT '更新人',`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',`del_flag` int(11) NULL DEFAULT 0 COMMENT '删除标志(0代表未删除,1代表已删除)',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;
​INSERT INTO `sys_user` VALUES (1, '张三', '张三', '{noop}1234', '0', NULL, NULL, NULL, NULL, '1', NULL, NULL, NULL, NULL, 0);

2)搭建jdbc或者mybatis或者mybatis-plus环境

<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3</version>
</dependency>

3)配置mybatis-plus环境

server:port: 8080
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/springsecurityusername: rootpassword: 123456
mybatis-plus:configuration:map-underscore-to-camel-case: truetype-aliases-package: com.hl.springsecurity01.domainmapper-locations: classpath:/mapper/*.xml

4)使用mybatisX 生成基本的service和mapper

5)创建一个类实现UserDetailsService接口 ,重写方法(根据用户名到数据库中查找用户对象)

package com.hl.springsecurity01.security;
​
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hl.springsecurity01.domain.SysUser;
import com.hl.springsecurity01.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
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.List;
​
@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate SysUserService sysUserService;/*根据用户名查找用户对象*/@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//根据用户名,到数据库表中,查找用户对象QueryWrapper queryWrapper = new QueryWrapper();queryWrapper.eq("user_name", username);List<SysUser> list = sysUserService.list(queryWrapper);//判断用户是否存在LoginUser user = null;if(list != null && list.size() > 0){SysUser sysUser = list.get(0);//封装数据到UserDetails接口实现类对象中user = new LoginUser(sysUser);}return user;}
}

6)创建一个LoginUser实现UserDetails接口,封装用户信息

package com.hl.springsecurity01.security;
​
import com.hl.springsecurity01.domain.SysUser;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
​
import java.util.Collection;
import java.util.Collections;
/*
创建类实现UsersDetail接口,存储当前登录成功的用户信息*/
@Data
public class LoginUser implements UserDetails {
​private SysUser sysUser;
​public LoginUser() {}public LoginUser(SysUser sysUser) {this.sysUser = sysUser;}
​//返回用户权限信息,返回权限列表@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return Collections.emptyList();}
​@Overridepublic String getPassword() {return sysUser.getPassword();}
​@Overridepublic String getUsername() {return sysUser.getUserName();}
​@Overridepublic boolean isAccountNonExpired() {return true;}
​@Overridepublic boolean isAccountNonLocked() {return true;}
​@Overridepublic boolean isCredentialsNonExpired() {return true;}
​@Overridepublic boolean isEnabled() {return true;}
}

3、加密机制 自定义密码匹配器

1)创建加密对象

@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {/*创建加密对象(密码匹配器对象)*/@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}
}

2)使用测试类,生成临时密码,测试匹配方法

@SpringBootTest
class Springsecurity01ApplicationTests {@Autowiredprivate PasswordEncoder passwordEncoder;
​@Testvoid contextLoads() {String pwd = passwordEncoder.encode("1234");System.out.println(pwd);//$2a$10$DpNgEn0fiYStJJ/EoYxf7uhBOMnuqK/UdBKmexX5KrEVPLuGQ0LsS//$2a$10$sLUel1CyTVY1kDWM5BwlNu1WMLMqlys00NIir0SiEgR0A5ItM1Vda//比对密码boolean flag1 = passwordEncoder.matches("1234", "$2a$10$DpNgEn0fiYStJJ/EoYxf7uhBOMnuqK/UdBKmexX5KrEVPLuGQ0LsS");boolean flag2 = passwordEncoder.matches("1234", "$2a$10$sLUel1CyTVY1kDWM5BwlNu1WMLMqlys00NIir0SiEgR0A5ItM1Vda");System.out.println(flag1);System.out.println(flag2);}
​
}

$2a$12$R9h/cIPz0gi.URNNX3kh2OPST9/PgBkqquzi.Ss7KIUgO2t0jWMUW
├─┬─┼──┬─┼──────────────┼───────────────────────────────────
  │ │  │  │      │                    
  │ │  │  │      └─ 哈希值(31字节)
  │ │  │  └─ 盐值(22字符,16字节)
  │ │  └─ cost参数(12 → 2^12=4096轮迭代)
  │ └─ 算法版本(2a)
  └─ 固定前缀

$<算法版本>$<cost>$<salt><hash>

修改数据库的密码为加密后的密码再进行测试 ,填写用户名张三,密码1234

4、自定义登录接口

@RestController
public class LoginController {@Autowiredprivate LoginService loginService;@RequestMapping("/login")public R login(String username, String password) throws AuthenticationException {//调用servicereturn loginService.login(username, password);}
}
public interface LoginService {public R login(String username, String password) throws AuthenticationException;
}
@Service
public class LoginServiceImpl implements LoginService {@Autowiredprivate AuthenticationManager authenticationManager;@Overridepublic R login(String username, String password) throws AuthenticationException {UsernamePasswordAuthenticationToken token =new UsernamePasswordAuthenticationToken(username, password);//调用认证提供器的认证方法,进行用户名,密码认证Authentication authentication = authenticationManager.authenticate(token);//根据返回值判断是否认证成功if(authentication == null){//认证失败throw  new AuthenticationException("用户名或者密码错误");}if(authentication.isAuthenticated()){//认证成功//返回 code ,msg,tokenreturn R.ok("token.............","认证成功");}return null;}
}
package com.hl.springsecurity01.security;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {/*创建加密对象(密码匹配器对象)*/@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 对于登录接口 允许匿名访问.antMatchers("/login").anonymous()// 除上面外的所有请求全部需要鉴权认证.anyRequest().authenticated();}@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}
}

登录成功,看到json返回,失败,没有任何提示。

5、token字符串的生成

<!--生成token-->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>

JwtUtil工具类

package com.hl.springsecurity01.util;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;/*** JWT工具类*/
public class JwtUtil {//有效期为public static final Long JWT_TTL = 60 * 60 *1000L;// 60 * 60 *1000  一个小时//设置秘钥明文public static final String JWT_KEY = "test";public static String getUUID(){String token = UUID.randomUUID().toString().replaceAll("-", "");return token;}/*** 生成jtw字符串* @param subject token中要存放的数据(json格式)* @return*/public static String createJWT(String subject) {JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 设置过期时间return builder.compact();}/*** 生成jtw字符串* @param subject token中要存放的数据(json格式)* @param ttlMillis token超时时间* @return*/public static String createJWT(String subject, Long ttlMillis) {JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 设置过期时间return builder.compact();}private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;SecretKey secretKey = generalKey();long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);if(ttlMillis==null){ttlMillis=JwtUtil.JWT_TTL;}long expMillis = nowMillis + ttlMillis;Date expDate = new Date(expMillis);return Jwts.builder().setId(uuid)              //唯一的ID.setSubject(subject)   // 主题  可以是JSON数据.setIssuer("sg")     // 签发者.setIssuedAt(now)      // 签发时间.signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥.setExpiration(expDate);}/*** 创建token* @param id* @param subject* @param ttlMillis* @return*/public static String createJWT(String id, String subject, Long ttlMillis) {JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 设置过期时间return builder.compact();}/*** 生成加密后的秘钥 secretKey* @return*/public static SecretKey generalKey() {byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");return key;}/*** 解析jwt** @param jwt* @return* @throws Exception*/public static Claims parseJWT(String jwt) throws Exception {SecretKey secretKey = generalKey();return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();}/***   用于测试*/public static void main(String[] args) throws Exception {
//        String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjYWM2ZDVhZi1mNjVlLTQ0MDAtYjcxMi0zYWEwOGIyOTIwYjQiLCJzdWIiOiJzZyIsImlzcyI6InNnIiwiaWF0IjoxNjM4MTA2NzEyLCJleHAiOjE2MzgxMTAzMTJ9.JVsSbkP94wuczb4QryQbAke3ysBDIL5ou8fWsbt_ebg";
//        Claims claims = parseJWT(token);
//        System.out.println(claims);String token = createJWT("1");String token1 = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJlM2UzMGE0ZjhhMGE0OTQ1ODNjMzZlZmNjMWQ3YzQ4YiIsInN1YiI6IjEiLCJpc3MiOiJzZyIsImlhdCI6MTc1MTUzMjI0MiwiZXhwIjoxNzUxNTM1ODQyfQ.ALfU833lUe1bCRlsAwcDd_mwD5j3sCtFCO3Ue1E-WWw";System.out.println(token);Claims claims = parseJWT(token1);System.out.println(claims);}}
package com.hl.springsecurity01.service.impl;import com.hl.springsecurity01.domain.R;
import com.hl.springsecurity01.security.LoginUser;
import com.hl.springsecurity01.service.LoginService;
import com.hl.springsecurity01.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;import javax.security.sasl.AuthenticationException;@Service
public class LoginServiceImpl implements LoginService {@Autowiredprivate AuthenticationManager authenticationManager;@Overridepublic R login(String username, String password) throws AuthenticationException {UsernamePasswordAuthenticationToken token =new UsernamePasswordAuthenticationToken(username, password);//调用认证提供器的认证方法,进行用户名,密码认证Authentication authentication = authenticationManager.authenticate(token);//根据返回值判断是否认证成功if(authentication == null){//认证失败throw  new AuthenticationException("用户名或者密码错误");}if(authentication.isAuthenticated()){//认证成功//获取用户身份 LoginUserLoginUser user = (LoginUser) authentication.getPrincipal();//获取用户idLong id = user.getSysUser().getId();//根据用户id,生成tokenString token2 = JwtUtil.createJWT(id+"");//返回 code ,msg,tokenreturn R.ok(token2,"认证成功");}return null;}
}

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

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

相关文章

解决git clone报错:fatal unable to access xxx. Could not resolve host github.com

作者&#xff1a;唐叔在学习 专栏&#xff1a;问题百宝箱 文章目录 问题描述问题诊断网络连通性测试 解决方案1. 获取GitHub最新IP地址2. 修改系统hosts文件 验证解决方案常见问题解答总结 问题描述 当使用git clone命令克隆GitHub仓库时&#xff0c;可能会遇到如下错误&#…

魔术方法__call__

__call__ 是一个特殊方法&#xff08;也称为魔术方法&#xff09;&#xff0c;用于使一个类的实例能够像函数一样被调用。当定义了这个方法后&#xff0c;实例对象可以后接括号&#xff08;即 ()&#xff09;来触发调用&#xff0c;这会让实例表现得像函数一样。 ​使实例可调…

PHP中的异常处理与错误日志记录

在PHP编程实践中&#xff0c;异常处理是一项至关重要的技能&#xff0c;它能够帮助开发者识别和响应程序执行过程中发生的非预期事件。与此同时&#xff0c;错误日志记录是确保应用程序可靠性和稳定性的关键组成部分。本文将详细介绍如何在PHP中实现这两个方面的技术。 首先&a…

JS去除空格(数组内字符串)

1.JS中去除空格 去除这个数组中每个对象内部参数&#xff08;也就是属性值&#xff09;的空格&#xff0c;可以通过遍历数组&#xff0c;再遍历每个对象的属性&#xff0c;使用 trim() 方法来去除字符串首尾的空格。以下是具体实现代码&#xff1a; let data [{ designHours:…

【Spring篇01】:Bean的线程安全问题总结

文章目录 1. 核心问题&#xff1a;Spring 框架中的 Bean 是线程安全的吗&#xff1f;2. 最佳实践与解决方案禁止方案&#xff1a;滥用prototype作用域推荐方案&#xff08;按优先级排序&#xff09; 3. 生产环境中的典型案例Case 1&#xff1a;订单服务统计Case 2&#xff1a;用…

本地项目上传git

将您本地的项目代码上传到一个私有的、别人看不见的 GitHub 仓库&#xff0c;是进行云端协作&#xff08;如使用 Google Colab&#xff09;、版本控制和代码备份的最佳实践。这是一个非常重要的技能。 整个过程可以分为三个部分&#xff1a; 准备工作&#xff1a;在您的电脑上…

【.NET Framework 窗体应用程序项目结构介绍】

在使用 Visual Studio (VS) 开发 .NET Framework 窗体应用程序&#xff08;Windows Forms App&#xff09; 时&#xff0c;项目结构通常包含以下核心文件夹和文件。以下是详细介绍&#xff1a; 1. 项目根目录下的主要文件 (1) .csproj 文件 作用&#xff1a;C# 项目文件&…

【SpringAI】4.多模态提问

SpringAI多模态提问 概述 SpringAI支持多模态输入&#xff0c;允许AI模型同时处理文本和图像内容。这对于需要视觉理解的AI应用场景非常有用&#xff0c;如图像描述、视觉问答、图像分析等。 核心概念 1. Media类 SpringAI使用Media类来表示多模态内容&#xff0c;支持图…

自动化提示工程:未来AI优化的关键突破

自动化提示工程:未来AI优化的关键突破 自动化提示工程能够自动化或半自动化地生成或优化提示词,以探索大规模的提示词组合,并通过 自动优化技术提升提示词生成的稳定性。 依据自动化提示工程实现形式在逻辑推理和效能导向 两个维度的取舍上,将其分为基于思维链的自动化提示工…

多模态大语言模型arxiv论文略读(148)

A Comprehensive Survey and Guide to Multimodal Large Language Models in Vision-Language Tasks ➡️ 论文标题&#xff1a;A Comprehensive Survey and Guide to Multimodal Large Language Models in Vision-Language Tasks ➡️ 论文作者&#xff1a;Chia Xin Liang, P…

关于.net core开发的实体所有注解详解

以下是对 .NET Core 开发中实体类&#xff08;用于数据模型&#xff09;和 Web API 控制器/方法&#xff08;用于定义接口&#xff09;常用注解属性&#xff08;Attributes&#xff09;的详细说明与示例&#xff0c;涵盖数据验证、API 行为控制、序列化、Swagger/OpenAPI 文档生…

【安全工具】SQLMap 使用详解:从基础到高级技巧

目录 简介 一、安装与基础配置 1. 安装方法 2. 基本语法 二、基础扫描技术 1. 简单检测 2. 指定参数扫描 3. 批量扫描 三、信息收集 1. 获取数据库信息 2. 获取当前数据库 3. 获取数据库用户 4. 获取数据库版本 四、数据提取技术 1. 列出所有表 2. 提取表数据 …

Redis大Key拆分实战指南:从问题定位到落地优化

引言 最近在项目里遇到一个棘手问题&#xff1a;生产环境的Redis突然变“卡”了&#xff01;查询延迟从几毫秒飙升到几百毫秒&#xff0c;监控面板显示某个节点CPU使用率飙到90%。排查半天才发现&#xff0c;原来是某个用户订单的Hash Key太大了——单Key存了100多万个订单字段…

RabbitMQ简单消息发送

RabbitMQ简单消息发送 简单代码实现RabbitMQ消息发送 需要的依赖 <!--rabbitmq--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId><version>x.x.x</version>&l…

【阅读笔记】基于双边滤波改进的空域滤波算法

一、双边滤波空域滤波算法 双边滤波是一种典型的非线性滤波算法。基于高斯滤波&#xff0c;双边滤波利用强度的变化来保存边缘信息&#xff0c;解决了边缘模糊在视觉观感上认为重要信息丢失的问题。双边滤波的滤波效果主要取决于两个参数&#xff1a;两个像素的空间邻近性和灰…

华为交换机堆叠与集群技术深度解析附带脚本

一、引言 在企业园区网、数据中心等网络场景中&#xff0c;为了提升网络的可靠性、扩展性和管理效率&#xff0c;华为交换机提供了堆叠&#xff08;Stack&#xff09;和集群&#xff08;CSS&#xff0c;Cluster Switch System &#xff09;技术。这两种技术能够将多台物理交换…

Python网络爬虫(十三)- 数据解析模块 BeautifulSoup

1、BS4简介 BeautifulSoup(简称 BS4) 是一个用于解析 HTML 和 XML 文档的 Python 第三方库。它能够从网页或其他 HTML/XML 格式的文本中提取数据,并将其转换为结构化的对象,方便开发者快速定位、提取和操作所需信息。它的核心功能是通过解析器将无序的标记语言转换为树形结…

如何使用 Pytorch Lightning 启用早停机制

【PL 基础】如何启用早停机制 摘要1. on_train_batch_start()2. EarlyStopping Callback 摘要 本文介绍了两种在 PyTorch Lightning 中实现早停机制的方法。第一种是通过重写on_train_batch_start()方法手动控制训练流程&#xff1b;第二种是使用内置的EarlyStopping回调&#…

深入理解前缀和与差分算法及其C++实现

前缀和与差分是算法竞赛和编程中非常重要的两种技巧&#xff0c;它们能够高效地处理区间查询和区间更新问题。本文将详细介绍这两种算法的原理、应用场景以及C实现。 一、前缀和算法 1.1 前缀和的基本概念 前缀和&#xff08;Prefix Sum&#xff09;是一种预处理技术&#x…

HugeGraph【部署】Linux单机部署

注: hugegraph从版本 1.5.0 开始&#xff0c;需要 Java11 运行时环境 一、安装JDK11 1.下载JDK11 https://www.oracle.com/java/technologies/downloads/#java11 2.解压缩包 tar -zxvf jdk-11.0.27_linux-x64_bin.tar.gz 3.修改/etc/profile环境变量 export JAVA_HOME/usr…