详细带你彻底搞懂 Spring Security 6.0 的实现原理

 ​

 博客主页:     南来_北往

系列专栏:Spring Boot实战


前言

Spring Security 6.0是一个功能强大且可扩展的身份验证和访问控制框架,它用于保护基于Java的应用程序。其主要目标是提供一个全面的安全解决方案,包括身份验证、授权、防止跨站请求伪造(CSRF)等功能。

  1. 身份验证(Authentication)

身份验证是确认用户身份的过程。Spring Security提供了多种身份验证机制,如表单登录、HTTP基本身份验证、OAuth2等。在Spring Security中,AuthenticationManager负责处理身份验证逻辑。当用户提供凭据(如用户名和密码)时,AuthenticationManager将创建一个Authentication对象,其中包含有关用户的信息。

  1. 授权(Authorization)

授权是确定用户可以访问哪些资源的过程。在Spring Security中,AccessDecisionManager负责处理授权逻辑。它根据用户的角色和权限来确定是否允许用户访问特定资源。AccessDecisionManager使用投票策略来决定是否允许访问。每个投票者可以根据其配置投赞成票、反对票或弃权票。如果赞成票多于反对票,则允许访问。

  1. 过滤器链(Filter Chain)

Spring Security使用一系列过滤器来处理请求。这些过滤器按照特定的顺序组成一个过滤器链。每个过滤器都负责处理特定的任务,如处理CSS和JavaScript资源、处理CORS、处理会话管理等。当请求进入应用程序时,过滤器链中的过滤器将按顺序处理请求。如果某个过滤器决定终止请求(例如,因为用户未经身份验证),则后续过滤器将不会执行。

  1. 安全上下文(Security Context)

安全上下文是一个包含有关当前用户和其权限的对象。在Spring Security中,SecurityContextHolder负责存储和管理安全上下文。当用户通过身份验证时,Authentication对象将被存储在SecurityContextHolder中。这使得应用程序可以在任何地方访问用户的凭据和权限信息。

  1. CSRF保护

跨站请求伪造(CSRF)是一种攻击,攻击者试图利用已登录用户的凭据来执行恶意操作。为了防止CSRF攻击,Spring Security提供了一个CsrfFilter,它会自动为每个表单添加一个隐藏的CSRF令牌。当表单提交时,CsrfFilter将验证令牌是否有效。如果令牌无效或不存在,请求将被拒绝。

这只是Spring Security 6.0实现原理的一个简要概述。要深入了解Spring Security的各个方面,建议您查阅官方文档和相关教程。

Java Web应用的Security实现基本思路

Java Web应用的Security实现基本思路主要包括以下几个方面:

  1. 身份验证(Authentication):确保用户的身份合法,通常使用用户名和密码进行验证。
  2. 授权(Authorization):确定用户具有哪些权限,以便控制用户可以访问的资源和执行的操作。
  3. 防止跨站请求伪造(CSRF):确保用户提交的请求是合法的,避免恶意网站利用用户在其他网站的认证状态发起攻击。
  4. 输入验证(Input Validation):对用户输入的数据进行验证,防止恶意数据注入攻击。
  5. 错误处理:正确处理异常情况,避免泄露敏感信息。
  6. 敏感数据保护:对敏感数据进行加密存储和传输,防止数据泄露。

以下是一个简单的Java Web应用Security实现代码示例:

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN").antMatchers("/user/**").hasAnyRole("USER", "ADMIN").anyRequest().authenticated().and().formLogin().loginPage("/login").permitAll().and().logout().permitAll();}
}

在这个示例中,我们使用了Spring Security框架来实现Web应用的安全功能。首先,我们通过@EnableWebSecurity注解启用了Web安全配置。然后,我们继承了WebSecurityConfigurerAdapter类并重写了configure方法来配置安全规则。

configure方法中,我们使用authorizeRequests方法定义了不同URL路径的访问权限。例如,只有具有"ADMIN"角色的用户才能访问/admin/**路径,而具有"USER"或"ADMIN"角色的用户都可以访问/user/**路径。其他所有请求都需要用户进行身份验证。

我们还配置了表单登录和注销功能,分别对应于/login页面和允许所有用户访问的注销操作。

Spring Security框架的基本架构和原理 

Spring Security是一个功能强大且可扩展的身份验证和访问控制框架,它提供了一种简单的方式来保护基于Java的应用程序。以下是Spring Security的基本架构和原理:

  1. 身份验证(Authentication):Spring Security通过AuthenticationManager接口来处理身份验证。这个接口负责从用户提交的凭据中获取认证信息,并将其封装成一个Authentication对象。常见的认证方式包括用户名密码认证、OAuth2认证等。

  2. 授权(Authorization):一旦用户被认证,Spring Security会使用AccessDecisionManager来决定用户是否有权访问特定的资源。AccessDecisionManager会根据用户的权限和请求的资源来判断是否允许访问。

  3. 过滤器链(Filter Chain):Spring Security使用一系列过滤器来处理HTTP请求。这些过滤器按照顺序执行,每个过滤器都负责一个特定的安全功能,如身份验证、授权、防止跨站请求伪造(CSRF)等。

  4. 安全上下文(Security Context):Spring Security使用SecurityContextHolder来存储当前用户的安全上下文信息。这个上下文包含了用户的认证信息、权限等信息,可以在应用程序的任何位置访问。

  5. 配置(Configuration):Spring Security的配置通常通过继承WebSecurityConfigurerAdapter类并重写其方法来实现。例如,可以配置登录页面、注销行为、URL访问规则等。

下面是一个简单的Spring Security配置示例:

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {// 配置内存中的用户存储auth.inMemoryAuthentication().withUser("user").password("{noop}password").roles("USER").and().withUser("admin").password("{noop}password").roles("ADMIN");}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN").antMatchers("/user/**").hasAnyRole("USER", "ADMIN").anyRequest().authenticated().and().formLogin().loginPage("/login").permitAll().and().logout().permitAll();}
}

在这个示例中,我们配置了两个内存中的用户(user和admin),分别具有不同的角色。我们还定义了URL访问规则,要求访问/admin/**路径的用户必须具有"ADMIN"角色,而访问/user/**路径的用户必须具有"USER"或"ADMIN"角色。最后,我们配置了表单登录和注销功能。

Authentication身份认证

身份认证有很多种方式,大致可以分为以下4类:

  1. 标准的账号密码认证:这是很多网站都支持的方式,也是大家最熟悉的认证模式;

  2. 调用第三方服务或内部其它API进行认证:当服务自身无法直接获取用户的密码时,需要借助第三方服务或者内部API进行认证;

  3. 基于Token的认证:这是API服务一般使用的认知方式,通过令牌来进行身份验证;

  4. OAuth2或其它OpenID认证:这种方式广泛用于允许用户使用其它平台的身份信息进行登录,例如微信登录,Google登录等。

Spring Security支持大部分的认证方式,但不同的认证方式需要配置不同的Bean及其依赖Bean,否则很容易遇到各种异常和空指针。

本文重点讨论标准的账号密码认证方式。

如果你使用的是Spring Boot,那么Spring Boot Starter Security默认就配置了Form表单和Basic认证方式,其配置代码如下所示:

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
class SpringBootWebSecurityConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnDefaultWebSecuritystatic class SecurityFilterChainConfiguration {@Bean@Order(SecurityProperties.BASIC_AUTH_ORDER)SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated()); // 所有URL都需要认证用户http.formLogin(withDefaults()); // 支持form表单认证,默认配置提供了自动生成的登录和注销页面http.httpBasic(withDefaults()); // 支持HTTP Basic Authenticationreturn http.build();}}// ...其它配置...
}

为了讨论方便,我们用下面的配置覆盖Spring Boot默认的配置,只支持Form表单认证方式,讨论它具体是如何实现的。 

@Configuration()  
public class MySecurityConfig {@BeanSecurityFilterChain mySecurityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated()); // (1)http.formLogin(withDefaults()); // (2)return http.build();}
}
  1. authorizeHttpRequests方法用于配置每个请求的权限控制,这里要求所有请求都要通过认证后才能访问。实际上,这个方法配置的更多是鉴权相关的内容,跟身份认证的关联较小,它本质上是增加了一个AuthorizationFilter用于鉴权,具体细节在鉴权部分会详细说明。

  2. http.formLogin方法提供了Form表单认证的方式,withDefaults方法是Form表单认证的默认配置。这段配置的作用就是增加了用于账号密码认证的UsernamePasswordAuthenticationFilter,以及自动生成登录页面和注销页面的DefaultLogoutPageGeneratingFilterDefaultLogoutPageGeneratingFilter共3个Security Filter。值得注意的是,登录页面和注销页面这两个Filter是配合DefaultLoginPageConfigurer配置一起注册的。如果你通过formLogin.loginPage提供了自定义的登录页面,那么这两个Filter就不会被注册。

在本节中,我们主要讨论身份认证的实现,因此,接下来将详细探究Form表单认证方式中UsernamePasswordAuthenticationFilter的实现。

AbstractAuthenticationProcessingFilter

对于Filter,我们重点分析它的doFilter方法的源码。实际上,它继承了抽象类AbstractAuthenticationProcessingFilter,而这个抽象类的doFilter是一个模板方法,定义了整个认证流程。其核心流程非常简单,伪代码如下:

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {// 首先判断该请求是否是认证请求或者登录请求if (!requiresAuthentication(request, response)) { // (1)chain.doFilter(request, response);return;}try {Authentication authenticationResult = attemptAuthentication(request, response); // (2) 实际认证逻辑// 认证成功successfulAuthentication(request, response, chain, authenticationResult); // (3)}catch (AuthenticationException ex) {// 认证失败unsuccessfulAuthentication(request, response, ex); // (4)}
}
  1. 首先requiresAuthentication方法用于判断当前请求是否为认证请求或者登录请求,例如通常是POST /login。只有在登录认证的情况下,才需要通过这个Filter;

  2. attempAuthentication方法是实际的认证逻辑,这是一个抽象方法,具体的逻辑由子类重写实现。它的规范行为是,如果认证成功,应该返回认证结果Authentication,否则以抛出异常AuthenticationException的方式表示认证失败;

  3. successfulAuthentication认证成功后,该方法会将Authentication对象放到Security Context中,这是非常关键的一步,后续需要认证结果的时候都是从Security Context获取的,比如鉴权Filter。此外,该方法还会处理其它一些相关功能,比如RememberMe,事件发布,最后再调用AuthenticationSuccessHandler

  4. unsuccessfulAuthentication :在认证失败后,它会清空Security Context,调用RememberMe相关服务和AuthenticationFailureHandler来处理认证失败后的回调逻辑,比如跳转到错误页面。

 

Authentication模型

在这里,我们涉及到了一个非常重要的数据模型——Authentication,它是一个接口类型,它既是对认证结果的一个抽象表示,同时也是对认证请求的一个抽象,通常也被称为认证Token。它的方法都比较抽象,定义如下:

public interface Authentication extends Principal, Serializable {// 当前认证用户拥有的权限列表Collection<? extends GrantedAuthority> getAuthorities();// 用户的一个身份标识,通常就是用户名Object getPrincipal();// 可用于证明用户身份的一个凭证,通常就是用户密码Object getCredentials();// 当前用户是否认证通过boolean isAuthenticated();// 更新用户的认证状态void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;// 获取附加的详情信息,比如原始的Http请求体等。Object getDetails();
}

具体的Authentication实现一般都命名为XXXToken,大部分都继承自抽象类AbstractAuthenticationToken,比如表示标准的用户名密码认证结果的UsernamePasswordAuthenticationToken,表示匿名登录用户认证结果的AnonymousAuthenticationToken等等,你也可以完全实现自己的Authentication

attempAuthentication方法

接下来,我们看下UsernamePasswordAuthenticationFilter的认证具体实现方法attempAuthentication,它的源码如下:

public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)throws AuthenticationException {// 默认只支持POST请求if (this.postOnly && !request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());}// 从form表单获取用户名和密码String username = obtainUsername(request);username = (username != null) ? username.trim() : "";String password = obtainPassword(request);password = (password != null) ? password : "";// 构建一个用于认证的请求UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,password);// 附加详细信息,比如请求体,有些认证方式需要除了用户名密码外更多的信息setDetails(request, authRequest);// 委托给AuthenticationManager做具体的认证return this.getAuthenticationManager().authenticate(authRequest);
}

 这个方法非常简单,它主要进行一些前置校验工作,从请求体中获取用户名和密码,并构建认证请求对象。然后,剩余的认证工作都是委托给AuthenticationManager接口来完成的,该接口的定义如下:

public interface AuthenticationManager {Authentication authenticate(Authentication authentication) throws AuthenticationException;
}

AuthenticationManager和AuthenticationProvider

AuthenticationManager接口只有一个方法,它的入参和出参都是Authentication对象。通常情况下,入参提供了必要的认证信息,例如用户名和密码。而在认证成功后,该方法会返回认证结果,并附加认证状态,用户拥有的权限列表等信息。如果认证失败,它会抛出AuthenticationException异常类的子类,其中包括DisabledExceptionLockedExceptionBadCredentialsException等账号相关的异常。

AuthenticationManager接口定义了Spring Security的认证行为。你可以提供自定义的实现,Spring Security也提供了一个通用的实现类ProviderManagerProviderManager将具体的认证工作委托给一系列的AuthenticationProvider

每个AuthenticationProvider对应不同的认证方式。比如最常见的用户名密码的认证实现是DaoAuthenticationProvider,而JwtAuthenticationProvider提供了JWT Token的认证。你可以通过添加不同的AuthenticationProvider的方式,在同一个服务内支持多种类型的认证方式,比如需要调用其它API检验密码的情况,就需要自定义AuthenticationProvider

此外,ProviderManager还可以配置父级AuthenticationManager,当这个ProviderManager的所有AuthenticationProvider都不支持所需的认证方式时,它会继续委托给父级的AuthenticationManager,而该父级通常也是一个ProviderManager类型。

UserDetailsService和PasswordEncoder

DaoAuthenticationProvider是最常用的认证实现之一,它通过UserDetailsServicePasswordEncoder来验证用户名和密码。

UserDetailsService的作用是查找用户信息UserDetails,这些信息包括用户密码,状态,权限列表等。用户信息可以存储在内存,数据库或者其它任何地方。Spring Security默认的配置是内存存储,对应的UserDetailsService实现是InMemoryUserDetailsManager,而数据库存储则对应JdbcUserDetailsManager

UserDetailsService获取到用户密码后,需要通过PasswordEncoder来验证密码的正确性。因为密码一般都不应该以明文形式存储,实际存储的是按一定规则编码后的文本,Spring Security支持多种编码方式,例如bcryptargon2scryptpbkdf2等。你可以配置PasswordEncoder Bean来选择不同的编码方式。都是请注意,内置的编码方式默认对编码后的文本有一个格式要求,就是必须有类似{bcrypt}的前缀来表示编码方式。

总结

本文重点分析了Spring Security的源码和架构,帮助读者理解其实现原理。由于篇幅有限,本文只覆盖了身份认证和鉴权模块的核心逻辑,很多特性没有涉及,包括Session管理,Remember Me服务,异常分支和错误处理等等,不过有了上述的基础知识,读者完全可以自己分析源码并深入理解这些特性。 

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

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

相关文章

IPv6过渡技术之网络工程师软考中级

IPv6过渡技术 IPv4/IPv6双栈 1.设备支持IPV4/IPv6&#xff0c;IPv4及IPv6在网络中独立部署&#xff0c;在一段时间内并存。对现有IPv4业务影响较小。 2.演进方案相对简单、易理解。网络规划设计工作量相对更少 3.现有软硬件(网络设备、终端、操作系统等)已经有很大一部分支持…

IDEA-安装插件 驼峰下划线转换

第一步&#xff1a;安装 file-settings-plugins-在marketplace搜索“CamelCase”-点击安装 第二步&#xff1a;设置 file-settings-editor-camel_case 第三步&#xff1a;使用 选中想转换的遍历 使用快捷键 Alt Shift U

用excel能做出这些报表吗?

用excel能做出这些报表吗&#xff1f; 有什么办法不安装OFFICE也能显示出来&#xff1f;

ARM体系结构和接口技术(十)按键中断实验

文章目录 一、按键中断实验&#xff08;一&#xff09;分析按键电路图&#xff08;二&#xff09;芯片手册 二、按键中断实验分析&#xff08;一&#xff09;查看所有外设的总线以及寄存器基地址&#xff08;二&#xff09;RCC章节&#xff08;三&#xff09;GPIO章节&#xff…

2024最新Uniapp的H5网页版添加谷歌授权验证

现在教程不少&#xff0c;但是自从谷歌升级验证之后&#xff0c;以前的老教程就失效了&#xff0c;现在写一个新教程以备不时之需。 由于众所周知的特殊原因&#xff0c;开发的时候一定注意网络环境&#xff0c;如果没有梯子是无法进行开发的哦~ clientID的申请方式我就不再进…

HTML开发笔记:3.图形化开发软件与模版网站

一、Google Web Designer 下载网址&#xff1a;webdesigner.withgoogle.com&#xff08;得挂梯子&#xff09; 可以编辑文字 可以插入图片&#xff0c;快捷键是ctrlshiftI 右侧“大纲”属性中可以调节大小 可以点击右上角在浏览器中预览效果&#xff1a; 二、模版网站 https://…

Python Flask入门到精通:详细教程和实战案例

前言 Flask是一个轻量级的Web框架&#xff0c;用于快速开发Web应用程序。它的设计理念是简洁、灵活和易于扩展&#xff0c;非常适合于从简单的单页应用到复杂的大型项目。通过Flask&#xff0c;可以创建各种Web应用程序&#xff0c;比如博客、电子商务网站、RESTful API等。 …

WordPress主题追格企业官网主题免费开源版V1.1.6

追格企业官网主题免费开源版由追格开发的一款开源wordpress主题&#xff0c;专为企业建站和追格企业官网小程序&#xff08;开源版&#xff09;PC配套而设计&#xff0c;功能集新闻动态、留言反馈、产品与服务、公司简介、联系我们等模块。

如何通过集成软件授权管理系统推动企业业务增长?

软件货币化已经成为许多企业商业成功的关键&#xff0c;随着全球数字化进程不断深入&#xff0c;其重要性也在不断增加。将许可解决方案优化集成到现有系统中&#xff0c;已成为从接收到订单到交付和激活许可的任何高效流程的基本要素。 软件货币化无处不在 无论是传统的软件企…

Vulnhub系列:Kioptix Level 1

1.环境配置 ifconfig kali机&#xff1a;192.168.26.128&#xff08;不同kali机不同ip&#xff09; 靶机&#xff1a;桥接 特别说明&#xff1a;有的小伙伴在打开虚拟机后&#xff0c;无法获取到靶机IP地址 这里特别说明&#xff1a;修改“Kioptix Level 1.vmx”配置文件中…

探索PyMuPDF:Python中的强大PDF处理库

探索PyMuPDF&#xff1a;Python中的强大PDF处理库 背景&#xff1a;为何选择PyMuPDF 在数字化时代&#xff0c;PDF文件因其跨平台的兼容性和对格式的严格保持而成为文档交换的通用格式。然而&#xff0c;处理PDF文件往往需要专门的工具或库。这就是PyMuPDF库的用武之地。PyMuP…

模方单体化功能联动3DMax能支持2018版本以上的3DMax吗?

答&#xff1a;目前还不支持&#xff0c;仅支持2018版本的 模方是一款针对实景三维模型的冗余碎片、水面残缺、道路不平、标牌破损、纹理拉伸模糊等共性问题研发的实景三维模型修复编辑软件。模方4.1新增自动单体化建模功能&#xff0c;支持一键自动提取房屋结构&#xff0c;平…

【Python selenium过极验滑块】用自动化selenium 操作GEETEST极验滑块,简单粗暴

文章日期&#xff1a;2024.07.24 使用工具&#xff1a;Python 文章类型&#xff1a;自动化过极验滑块 文章全程已做去敏处理&#xff01;&#xff01;&#xff01; 【需要做的可联系我】 AES解密处理&#xff08;直接解密即可&#xff09;&#xff08;crypto-js.js 标准算法&…

Python自动化测试之Selenium各浏览器驱动下载网址

在自动化测试领域&#xff0c;Selenium无疑是一个不可或缺的工具。它允许开发者编写脚本来模拟用户在浏览器中的操作&#xff0c;从而进行自动化测试。然而&#xff0c;为了使用Selenium控制不同的浏览器&#xff0c;我们需要安装相应的浏览器驱动&#xff08;WebDriver&#x…

企业利用AI智能名片S2B2C商城小程序参与社区团购的风险与机遇分析

摘要 在新零售浪潮的推动下&#xff0c;社区团购以其独特的商业模式迅速崛起&#xff0c;成为连接消费者与供应商的重要桥梁。企业纷纷探索如何有效利用这一新兴渠道&#xff0c;以扩大市场份额、提升品牌影响力。AI智能名片S2B2C商城小程序的引入&#xff0c;为企业参与社区团…

maven archetype

1.简介 maven脚手架是为了创建一个项目模板&#xff0c;以后新建项目都能够复用该模板 maven中模板引擎使用的是velocity,在文件中可以使用它的语法获取变量等操作 2.实现 单模块脚手架实现 pom.xml <?xml version"1.0" encoding"UTF-8"?> &…

windows下实现mongodb备份还原

添加环境变量 把mongodb安装目录下的bin路径添加到环境变量的path路径: 备份库 打开CMD&#xff0c;执行以下命令&#xff1a; mongodump -u test -p test -d test -o D://backup_mongodb//20220706 –gzip 参数说明&#xff1a; -u 用户名 -p 密码 -d 需要备份的库名称…

MongoDB教程(二十一):MongoDB大文件存储GridFS

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、GridFS…

学习笔记(数据结构:链表 栈)3

fun为回调函数由主函数决定 栈: 栈是限定仅在表尾进行插入和删除操作的线性表。 先进后出、后进先出 栈顶:允许操作的一端 栈底:不允许操作的一端 入栈&#xff0c;出栈。 顺序栈 链式栈 1.创建 CreateSeqStack 2.销毁 DestroySeqStack 3.判断是否为空栈 IsEm…

AD7606采集控制

过采样&#xff1a;其效果就是在ADC之后增加数字滤波器的功能