当前位置: 首页 > news >正文

Spring Security 认证流程,长话简说

一、代码先行

1、设计模式

SpringSecurity 采用的是 责任链 的设计模式,是一堆过滤器链的组合,它有一条很长的过滤器链。

不过我们不需要去仔细了解每一个过滤器的含义和用法,只需要搞定以下几个问题即可:怎么登录、怎么校验账户、认证失败处理。

2、 POM依赖

没啥好说的,maven导入即可。
不写版本号,默认就会下载 最新的 版本。

<!--springSecurity-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency> 

3、登录

不管你用哪种权限框架,第一个要解决的问题就是登录。就是在我们的登录接口中,将账户密码委托给权限框架接管,让权限框架帮我们做校验和权限认证。

新建一个登录方法,目的是 验证用户的 用户名密码,并在验证成功后为该用户生成一个JWT。

@GetMapping("/login")
public String securityLogin(String username,String password){UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username,password);// 使用authenticationManager调用loadUserByUsername获取数据库中的用户信息,Authentication authentication = authenticationManager.authenticate(authToken);if(authentication == null) {throw new RuntimeException("登录失败啦呀");}//获取符合security规范的UserSecurityUser securityUser = (SecurityUser) authentication.getPrincipal();String token = jwtUtil.createToken(securityUser.getUser());return token;
}

UsernamePasswordAuthenticationToken就是 我们委托 框架 帮我们托管 的 登录凭证

然后是:

单词重点说明: authenticate (v) 证明…是事实; authentication (n)证明

Authentication authentication = authenticationManager.authenticate(authToken);   

这就是spring security帮我们 执行 认证授权 的方法,最终返回一个 认证结果

大家思考一下,正常登录的逻辑无非是 4步走:

  1. 输入账号密码
  2. 根据账号从 DB数据库 中获取用户实体
  3. 校验密码是否正确
  4. 校验成功,将用户生成token后返回

我们再回过来看 上面的代码,第2步和第3步没见到。
肯定是spring security已经帮我们做了,但是,这并不代表我们可以省略这两步,只是需要我们写在别的地方,仅此而已。

4、根据账号从DB中获取用户实体

这个步骤是不可能不写的,只是写到了别处。
说明一点,spring security中的 用户概念,有自己的一套规则,不能直接用 我们系统里面的 User类。

因此如果我们要用spring security,就得 实现 他的 用户接口UserDetails
所以,我们新建一个SecurityUser类:

@Data
@NoArgsConstructor
public class SecurityUser implements UserDetails {// 使用聚合模式,将我们自己的User对象聚合到SecurityUser中// user字段存储了用户的一些信息,例如用户名和密码等private User user;// 覆盖UserDetails接口中的getAuthorities方法,返回用户的权限集合// 这里返回null,表示未实现获取权限的逻辑@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}// 覆盖UserDetails接口中的getPassword方法,返回用户的密码// 这里通过调用user对象的getPwd方法获取密码@Overridepublic String getPassword() {return user.getPwd();}// 覆盖UserDetails接口中的getUsername方法,返回用户的用户名// 这里通过调用user对象的getUserName方法获取用户名,并注释说明有的地方可能会用email作为用户名,这里还是使用userName@Overridepublic String getUsername() {// 用户名:有的地方可能会用email作为用户名,我们这还是userNamereturn user.getUserName();}// 覆盖UserDetails接口中的isAccountNonExpired方法,判断账户是否过期// 这里直接返回true,表示账户不过期@Overridepublic boolean isAccountNonExpired() {return true;}// 覆盖UserDetails接口中的isAccountNonLocked方法,判断账户是否被锁定// 这里直接返回true,表示账户未被锁定@Overridepublic boolean isAccountNonLocked() {return true;}// 覆盖UserDetails接口中的isCredentialsNonExpired方法,判断凭证是否过期// 这里直接返回true,表示凭证不过期@Overridepublic boolean isCredentialsNonExpired() {return true;}// 覆盖UserDetails接口中的isEnabled方法,判断用户是否启用// 这里直接返回true,表示用户启用状态为true@Overridepublic boolean isEnabled() {return true;}
}

虽然我们系统里面有自己的 user 了,但是为了适配,所以就 聚合 进来。

然后是如何查询DB,肯定得有个 Service 去查询,我们已经有自己的UserService了,但是很可惜,spring security 这个地方也有自己的规范,我们自己写的user Service,人家不认,唉。

没办法,重新写个Service,自己写都写了,也不能丢了,所以 依照上面的操作,还是聚合进来。

@Service
@Slf4j
public class UserDetailService implements UserDetailsService {@ResourceUserService userService;/*** 根据用户名直接从DB中查询用户数据,作为登录校验的依据* @param username* @return* @throws UsernameNotFoundException*/@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userService.getByUsername(username);if (user == null) {log.info("username not found");throw new UsernameNotFoundException("username not found");}SecurityUser securityUser = new SecurityUser();securityUser.setUser(user);return securityUser;}
}

核心逻辑 是,我们还是用之前的方法拿到 User,但为了适配,就塞到 SecurityUser 里面去。

UserDetailsServicespring security认可的接口,我们得实现这个接口,并且实现loadUserByUsername方法,这个方法在spring security的认证逻辑里面会用到。目的就是拿到DB中真实的User,跟我们登录的账号密码进行比对。

5、校验密码是否正确

很多时候我们的密码是要进行加密的,但是我们登录肯定传的是明文密码,所以需要转换后再去比对。

这个 校验密码 的逻辑,需要写在spring security配置类 中。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@ResourceUserDetailService userService;/*** 新增security账户* @param auth* @throws Exception*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userService).passwordEncoder(new PasswordEncoder() {@Overridepublic String encode(CharSequence rawPassword) {return rawPassword.toString();}@Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {return rawPassword.equals(encodedPassword);}});}}

因为我们的项目并没有对密码加密,所以就直接比较了。

在 LoginController 中,我们用到了AuthenticationManager这个对象,需要在配置类中注册。

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {// 身份验证管理器, 直接继承即可.return super.authenticationManagerBean();
} 

AuthenticationManager 是 Spring Security框架中的一个核心接口,它负责处理身份验证请求。
在认证过程中,用户提交身份验证信息(如用户名和密码),AuthenticationManager 会验证这些信息的有效性。

最后是 路由 的相关配置

    @Override  protected void configure(HttpSecurity http) throws Exception {  http  // 禁用跨站请求伪造保护  .csrf().disable()   // 设置会话管理策略为无会话,因为我们使用token作为信息传递介质  .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)   .and()  // 进行认证请求的配置  .authorizeRequests()   // 将所有登入和注册的接口放开,这些都是无需认证就访问的  .antMatchers("/security/login").anonymous()   // 除了上面的那些,剩下的任何接口请求都需要经过认证  .anyRequest().authenticated()   .and()  // 允许跨域请求  .cors()   ;  // 在UsernamePasswordAuthenticationFilter之前添加JWT认证过滤器  http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);  }

这段代码是Spring Security框架中用于配置HTTP安全的部分。它主要涉及到跨站请求伪造(CSRF)的禁用、会话管理策略的设置、认证请求的配置以及跨域请求的允许。同时,还添加了一个JWT(JSON Web Token)认证过滤器。

因为我们项目用到了jwt,所以在进行账号密码验证之前,要先走jwt的过滤器。

jwt过滤器代码如下:

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@ResourceJWTUtil jwtUtil;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException, ServletException, IOException {//获取tokenString token = request.getHeader("token");if (!StringUtils.hasText(token)) {//token为空的话, 就不管它, 让SpringSecurity中的其他过滤器处理请求//请求放行filterChain.doFilter(request, response);return;}//token不为空时, 解析tokenUser user = null;try {user = jwtUtil.verify(token);} catch (Exception e) {// 过滤器中抛出的异常,无法被统一异常捕获,所以在这里直接返回e.printStackTrace();Result result = new Result();result.setCode(403);result.setMsg("Token无效:" + e.getMessage());WebUtils.response(response,result);return;}SecurityUser securityUser = new SecurityUser();securityUser.setUser(user);//将用户安全信息存入SecurityContextHolder, 在之后SpringSecurity的过滤器就不会拦截UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(securityUser, null, null);SecurityContextHolder.getContext().setAuthentication(authenticationToken);//放行filterChain.doFilter(request, response);}
}  

流程参考这个图:
在这里插入图片描述

如果校验成功了,那么在登录方法中就会往下走,生成token返回,结束。

6、全局异常返回

认证过程中,难免出现各种异常,我们一般会做一个通用的返回

Result

@Data
public class Result implements Serializable {private int code;private String msg;private Object data;public static Result succ(Object data) {return success(200, "操作成功", data);}public static Result error(String msg) {return error(400, msg, null);}public static Result success (int code, String msg, Object data) {Result result = new Result();result.setCode(code);result.setMsg(msg);result.setData(data);return result;}public static Result error (int code, String msg, Object data) {Result result = new Result();result.setCode(code);result.setMsg(msg);result.setData(data);return result;}
}

GlobalExceptionHandler

/*** 全局异常处理*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {/*** 400 错误:运行时异常* @param e* @return*/@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(value = RuntimeException.class)public Result handler(RuntimeException e) {log.error("运行时异常:----------------{}", e.getMessage());return Result.error(e.getMessage());}/*** 403 错误:权限不足* @param e* @return*/@ResponseStatus(HttpStatus.FORBIDDEN)@ExceptionHandler(value = AccessDeniedException.class)public Result handler(AccessDeniedException e) {log.info("security权限不足:----------------{}", e.getMessage());return Result.error("权限不足");}/*** 400 错误:异常请求-方法参数不匹配* @param e* @return*/@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(value = MethodArgumentNotValidException.class)public Result handler(MethodArgumentNotValidException e) {log.info("实体校验异常:----------------{}", e.getMessage());BindingResult bindingResult = e.getBindingResult();ObjectError objectError = bindingResult.getAllErrors().stream().findFirst().get();return Result.error(objectError.getDefaultMessage());}/*** 400 错误:异常请求-非法参数* @param e* @return*/@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(value = IllegalArgumentException.class)public Result handler(IllegalArgumentException e) {log.error("Assert异常:----------------{}", e.getMessage());return Result.error(e.getMessage());}}

二、原理亮明

Spring Security 它专注于身份验证和授权,并可根据需求进行配置和自定义。在实际应用中,了解身份验证组件的概念对于使用 Spring Security 进行实现和自定义非常有帮助。

1、没有使用 Spring Security

如果我们没有使用 Spring Security,那么请求将被 DispatcherServlet 拦截。

  • DispatcherServlet 服务分发器
    它拦截任何 HTTP 请求并将其转发到正确的控制器。
    在 Spring Boot 中,DispatcherServlet 会被自动配置。
    在这里插入图片描述

2、DispatcherServlet 是如何工作的

在深入了解 Spring Security 之前,让我们先了解一下 DispatcherServlet 如何分发请求。
当我们在一个端点(比如 /hello/world)发出请求时,DispatcherServlet 会如何处理它?

DispatcherServlet 创建了一个 IOC 容器,它是 Spring Framework 的核心组件之一,用于管理 bean 的创建和依赖关系。DispatcherServlet 创建的是 WebApplicationContext,这是一个专门用于 Web 应用程序的 IOC 容器。WebApplicationContext 是根据配置文件由 DispatcherServlet 进行配置的。

该 IOC 容器 会创建控制器 bean 的实例。当请求到达时,DispatcherServlet 会使用 IOC 容器 查找适当的 控制器 bean,并 委托 给它来 处理请求。

3、使用 Spring Security

当将 Spring Security 添加到 Spring Boot 应用程序中时,所有请求在到达 DispatcherServlet 之前 都会被 Spring Security 拦截。

在这里插入图片描述

4、认证 流程

身份验证请求和响应

虽然 Spring Security 可以与不同类型的身份验证方法一起使用,但在本文中,我们讨论的是 用户名和密码 方式的 身份验证,以便深入了解完整的身份验证流程。

  1. Filter Chain 在将请求转发给 Dispatcher Servlet 之前拦截传入请求。

  2. 请求进入认证过滤器(其中一个过滤器是 UsernamePasswordAuthenticationFilter)。

  3. 过滤器从请求(HttpServletRequest 对象)中提取用户名和密码。

  4. 然后使用凭据创建 UsernamePasswordAuthenticationToken

  5. 调用 AuthenticationManager 的 authenticate() 方法。

  6. AuthenticationManager 的 authenticate() 方法实现将尝试使用其拥有的 AuthenticationProvider 之一进行身份验证。

  7. 如果一个身份验证提供程序能够成功进行身份验证,它将返回一个包含凭据和权限的完整的 UsernamePasswordAuthenticationToken。

  8. 提供程序返回的此令牌用于在 Spring Security 上下文中将用户设置为已认证。

  9. 一旦用户通过身份验证,请求就会被转发到处理请求的 DispatcherServlet。

5、认证流程 中涉及到的 组件

5.1 什么是过滤器

Spring Filter 是一个组件,可以拦截任何传入请求,并在将其传递给 DispatcherServlet 之前执行某些操作。
过滤器可以处理请求,然后将其转发到 Filter Chain 中的下一个过滤器,或者可以停止并发送回 HTTP 响应。其中一个过滤器是存在于 FilterChain 中的 UsernamePasswordAuthenticationFilter。此过滤器尝试查找 HTTP Post 请求中传递的用户名和密码,如果找到,则尝试使用凭据对用户进行身份验证。
我们可以创建自己的 Filter 并将其添加到 SecurityFilterChain 中,以提供自己的逻辑来处理请求。

5.2 什么是 AuthenticationManager

AuthenticationManager 是一个接口,用于处理 身份验证 的过程。它只有一个方法 authenticate(Authentication authentication),该方法将一个身份验证对象作为参数,并返回 已经认证的 身份验证对象。身份验证对象通常是一个包含用户名和密码等凭据的 AuthenticationToken 对象。

Authentication authenticate(Authentication authentication) throws AuthenticationException;   

AuthenticationManager 的 实现类 是 ProviderManager 类,它提供了 authenticate() 方法的逻辑。
我们可以提供我们自己的 AuthenticationProvider 实现类 或 使用默认实现。

5.3 什么是 ProviderManager

实现 了 AuthenticationManager 接口 并覆盖了 authenticate() 方法。
它使用一组 AuthenticationProvider 来验证 Authentication 对象中发送的凭据。如果 AuthenticationProvider 支持身份验证类型,则将用于验证用户。如果没有提供者支持身份验证类型,则将抛 AuthenticationException。

每个身份验证提供程序的 supports() 方法用于检查它是否可以支持所需的身份验证类型。

5.4 什么是 AuthenticationProvider

AuthenticationProvider 是一个接口,用于定义验证用户的协议。它负责接收 Authentication 对象(表示用户凭据)并返回已经认证的 Authentication 对象,如果凭据有效。如果凭据无效,则 AuthenticationProvider 应该抛出 AuthenticationException。

AuthenticationProvider 接口有两个方法:

  • authenticate():此方法接受 Authentication 对象作为输入,并在凭据有效时返回已认证的 Authentication 对象。如果凭据无效,则 AuthenticationProvider 应该抛出 AuthenticationException。

  • supports():此方法接受 Authentication 对象作为输入,并且如果 AuthenticationProvider 可以验证该对象,则返回 true。如果 AuthenticationProvider 无法验证该对象,则应返回 false。

Spring Security 中的默认 AuthenticationProvider 是 DaoAuthenticationProvider。此提供程序使用 UserDetailsService 从数据库加载用户详细信息。如果用户凭据与数据库中的详细信息匹配,则 DaoAuthenticationProvider 将返回已经认证的 Authentication 对象。

我们可以添加我们自己的 AuthenticationProvider 实现来提供不同的身份验证方法。

5.5 身份验证和 UsernamePasswordAuthenticationToken

Authentication 和 UsernamePasswordAuthenticationToken 是什么?

  • Authentication

这是 Spring Security 中的一个接口,表示传入身份验证请求的令牌或已认证的主体(表示一个实体,比如个人)AuthenticationManager.authenticate() 方法。

提供的一些方法为:

 Collection<? extends GrantedAuthority> getAuthorities();Object getCredentials();boolean isAuthenticated();void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;  
  • UsernamePasswordAuthenticationToken

此类扩展了 AbstractAuthenticationToken 类(身份验证对象的基类),可用于用户名/密码身份验证请求。

此类有两个构造函数:

public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {super((Collection)null);this.principal = principal;this.credentials = credentials;this.setAuthenticated(false);
}public UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {super(authorities);this.principal = principal;this.credentials = credentials;super.setAuthenticated(true);
}

第一个构造函数可用于传入请求以创建未经身份验证的 Authentication 对象。

Authentication authentication = new UsernamePasswordAuthenticationToken(username,password);   

第二个构造函数可用于创建完全经过身份验证的 Authentication 对象。

Authentication authToken = new UsernamePasswordAuthenticationToken(username, password, userAuthorities);   

然后,此完全经过身份验证的 Authentication 对象从 AuthenticationProvider/AuthenticationManager 返回并表示已认证的用户。然后将此已认证的对象设置在 SecurityContext 中。Spring Security 中的 SecurityContext 是当前执行线程的安全上下文的表示。它包含有关当前已认证用户的信息,例如其用户名、权限和会话信息。

相关文章:

Spring Security 认证流程,长话简说

一、代码先行 1、设计模式 SpringSecurity 采用的是 责任链 的设计模式&#xff0c;是一堆过滤器链的组合&#xff0c;它有一条很长的过滤器链。 不过我们不需要去仔细了解每一个过滤器的含义和用法,只需要搞定以下几个问题即可&#xff1a;怎么登录、怎么校验账户、认证失败…...

74HC245

74HC245&#xff1a;典型的CMOS型缓冲门电路 在这里用于增加电压...

Java的static关键字和静态代码块

一、当static关键字用来修饰属性时&#xff0c;所修饰的属性就是类属性&#xff0c;而不是对象属性&#xff0c;所以可以做到全类共享。 不能用对象名去调用&#xff0c;只能用类名调用。 二、静态方法只能调用同为静态的方法和属性&#xff0c;非静态方法什么都可以调用。 三…...

Apex 批处理将 account owner 转移,同时实现关联的 opp 和 case 转移

实现和 mass transfer account 一样的功能&#xff1a; global class AccountBatchScript implements Database.Batchable<sObject>,Schedulable{String query;Id oldOwnerId xxxxxxxxxxxx;Id newOwnerId yyyyyyyyyyyy;List<Id> AccountIds new List<Id>(…...

Python | Leetcode Python题解之第557题反转字符串中的单词III

题目&#xff1a; 题解&#xff1a; class Solution:def reverseWords(self, s: str) -> str:stack, res, s [], "", s " "for i in s:stack.append(i)if i " ":while(stack):res stack.pop()return res[1:]...

Spring设计模式

设计模式 是一种软件开发中的解决方案&#xff0c;设计原则。目的是使代码具有扩展性&#xff0c;可维护性&#xff0c;可读性&#xff0c;如&#xff1a; 单例模式&#xff08;Singleton Pattern&#xff09; Spring IoC 容器默认会将 Bean 创建为单例&#xff0c;保证一个类…...

信号保存和信号处理

目录 信号保存中重要的概念 内核中信号的保存 对sigset_t操作的函数 对block&#xff0c;pendding&#xff0c;handler三张表的操作 sigpromask ​编辑 sigpending 是否有sighandler函数呢&#xff1f; 案例 信号处理 操作系统是如何运行的&#xff1f; 硬件中断 …...

网站小程序app怎么查有没有备案?

网站小程序app怎么查有没有备案&#xff1f;只需要官方一个网址就可以&#xff0c;工信部备案查询官网地址有且只有一个&#xff0c;百度搜索 "ICP备案查询" 找到官方gov.cn网站即可查询&#xff01; 注&#xff1a;网站小程序app备案查询&#xff0c;可通过输入单位…...

如何利用宏和VBA来提高文档编辑排版速度?

一个真实的文档修改需求 为什么我会去研究VBA呢&#xff1f;主要原因是今年在一个项目里写了太多的文档。文档中很多操作其实都是机械的、重复的&#xff0c;但是偏偏又很耗时。举个例子&#xff0c;当时有这么一个修改需求&#xff0c;修改文档中所有“输入输出需求表格中”添…...

Kafka - 启用安全通信和认证机制_SSL + SASL

文章目录 官方资料概述制作kakfa证书1.1 openssl 生成CA1.2 生成server端秘钥对以及证书仓库1.3 CA 签名证书1.4 服务端秘钥库导入签名证书以及CA根证书1.5 生成服务端信任库并导入CA根数据1.6 生成客户端信任库并导入CA根证书 2 配置zookeeper SASL认证2.1 编写zk_server_jass…...

c++基础32输入和输出

输入和输出 C风格&#xff08;使用printf和scanf&#xff09;输出字符输入字符 C风格&#xff08;使用cin和cout&#xff09;输出字符输入字符 注意事项 在C和C中&#xff0c;字符的输入和输出可以通过多种方式实现&#xff0c;包括使用标准输入输出库函数如 printf和 scanf&…...

[C++] 函数详解

前言 今天zty带来的是函数的详解&#xff0c;搞了4个小时&#xff0c;大家给个赞呗&#xff0c;zty还要上学&#xff0c;发作品会少一点 先 赞 后 看 养 成 习 惯 先 赞 后 看 养 成 习 惯 先 赞 后 看 养 成 习 惯 演示用编译器及其…...

AMD CPU下pytorch 多GPU运行卡死和死锁解决

参考链接 https://medium.com/amitparekh/solving-ddp-deadlock-with-multiple-gpus-and-amd-cpus-442186632034 简要说明 AMD的IOMMU和NVIDIA的NCCL不兼容问题导致AMD的IOMMU是BIOS 级组件,它基本上充当将虚拟地址映射到 GPU 上的物理地址的接口,它的全部目的是让 CPU 和 G…...

Swift 开发教程系列 - 第12章:协议与协议扩展

协议&#xff08;Protocol&#xff09;是 Swift 的一种重要特性&#xff0c;它定义了实现特定功能的方法、属性或其他要求。通过协议&#xff0c;可以将行为定义从具体实现中分离&#xff0c;使代码更具可读性和扩展性。Swift 的协议支持协议扩展&#xff0c;这一特性允许我们为…...

麒麟V10,arm64,离线安装docker和docker-compose

文章目录 一、下载1.1 docker1.2 docker-compose1.3 docker.service 二、安装三、验证安装成功3.1 docker3.2 docker-compose 需要在离线环境的系统了里面安装docker。目前国产化主推的是麒麟os和鲲鹏的cpu&#xff0c;这块的教程还比较少&#xff0c;记录一下。 # cat /etc/ky…...

NUXT3学习日记二(样式配置、引入组件库、区分在服务端还是在客户端渲染)

上一章已经给大家分享官网下载的nuxt3了&#xff0c;下面正式进入我所要说的内容吧 一、初始化样式 想必大家从我的git下载下来的nuxt3&#xff0c;能看到nuxt.config.ts这个文件了吧。 这里我们有两种css配置方式 1、css:[~/assets/base.scss] 这种方式不能在scss文件中定义…...

FPGA/Verilog,Quartus环境下if-else语句和case语句RT视图对比/学习记录

基本概念 RTL&#xff08;Register - Transfer - Level&#xff09;视图&#xff1a;是一种硬件描述语言的抽象层次&#xff0c;用于描述数字电路中寄存器之间的数据传输和操作。在这个层次上&#xff0c;可以看到电路的基本结构&#xff0c;如寄存器、组合逻辑、多路复用器等…...

Javascript高级—闭包问题

闭包问题 循环中赋值为引用的问题 for (var i 1; i < 5; i) {setTimeout(function timer() {console.log(i)}, i * 1000) }解决方法有3种 第一种&#xff0c;使用立即执行函数方式 for (var i 1; i < 5; i) {(fuction(j){setTimeout(function timer() {console.log…...

C#入门 017 字段,属性,索引器,常量

字段&#xff0c;属性&#xff0c;索引器&#xff0c;常量都表示数据 字段 什么是字段 字段(field)是一种表示与对象或类型(类与结构体)关联的变量字段是类型的成员&#xff0c;又称“成员变量&#xff0c;写在类体里面与对象关联的字段亦称“实例字段&#xff0c;表示某个对…...

磐石云语音助手拦截介绍

呼叫中心用户实际应用场景下最高会有超过30%的和语音助手&#xff1b;无声主要是进入了语音信箱;如&#xff1a;“听到滴声后留言”&#xff0c;”漏话提醒““发送请按1&#xff0c;重录请按2”以及拨打过程中客户主动拒接产生的”您拨打的用户正忙“&#xff0c;”关机“”停…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用

文章目录 一、背景知识&#xff1a;什么是 B-Tree 和 BTree&#xff1f; B-Tree&#xff08;平衡多路查找树&#xff09; BTree&#xff08;B-Tree 的变种&#xff09; 二、结构对比&#xff1a;一张图看懂 三、为什么 MySQL InnoDB 选择 BTree&#xff1f; 1. 范围查询更快 2…...

《Docker》架构

文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器&#xff0c;docker&#xff0c;镜像&#xff0c;k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...

算法打卡第18天

从中序与后序遍历序列构造二叉树 (力扣106题) 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1a;inorder [9,3,15,20,7…...

sshd代码修改banner

sshd服务连接之后会收到字符串&#xff1a; SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢&#xff1f; 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头&#xff0c…...

2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案

一、延迟敏感行业面临的DDoS攻击新挑战 2025年&#xff0c;金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征&#xff1a; AI驱动的自适应攻击&#xff1a;攻击流量模拟真实用户行为&#xff0c;差异率低至0.5%&#xff0c;传统规则引…...