Spring Security 6.1.x 系列(5)—— Servlet 认证体系结构介绍

一、前言

本章主要学习Spring Security中基于Servlet 的认证体系结构,为后续认证执行流程源码分析打好基础。

二、身份认证机制

Spring Security提供个多种认证方式登录系统,包括:

  • Username and Password:使用用户名/密码 方式进行认证登录
  • OAuth 2.0 Login:使用OpenID ConnectOAuth 2.0 方式进行认证登录
  • CAS: 使用CAS企业级单点登录系统 方式进行认证登录
  • SAML 2.0 Login:使用SAML 2.0 方式进行认证登录
  • Remember Me:使用记住我 方式进行认证登录
  • JAAS: 使用JAAS 方式进行认证登录
  • Pre-Authentication Scenarios:使用外部机制 方式进行认证登录
  • X509:使用X509 方式进行认证登录

三、认证组件简介

Spring Security中的认证相关组件:

  • SecurityContextHolder:安全上下文持有者,存储当前认证用户的SecurityContext

  • SecurityContext :安全上下文,包含当期认证用户的Authentication(认证信息),从SecurityContextHolder中获取。

  • Authentication :认证信息,用户提供的用于身份认证的凭据的输入。

  • GrantedAuthority :授予用户的权限(角色、作用域)。

  • AuthenticationManager :认证管理器,是一个接口,定义Spring Security过滤器执行身份认证的API

  • ProviderManager :提供者管理器,是AuthenticationManager的默认实现。

  • AuthenticationProvider : 认证提供者,由ProviderManager选择,用于执行特定类型的身份认证。

  • AuthenticationEntryPoint : 认证入口点,处理认证过程中的认证异常,比如:重定向登录页面

  • AbstractAuthenticationProcessingFilter :抽象认证处理过滤器,一个Filter抽象类,是身份验证的基础。

四、认证组件源码分析

4.1 SecurityContextHolder

SecurityContextHolder(安全上下文持有者)是Spring Security身份认证模型的核心,存储已认证用户详细信息,包含了SecurityContext(安全上下文):

在这里插入图片描述
当用户认证成功后,会将SecurityContext设置到SecurityContextHolder中,后续流程可以用过SecurityContextHolder静态方法直接获取用户信息:

SecurityContext context = SecurityContextHolder.getContext();// 获取 SecurityContext
Authentication authentication = context.getAuthentication();// 获取认证信息
String username = authentication.getName(); // 用户名
Object principal = authentication.getPrincipal(); // 当前用户的信息,通常是UserDetails的实例
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();// 权限

默认策略下,SecurityContextHolder使用ThreadLocal来存储信息,一个已认证的请求线程在过滤器阶段,会获取会话中的认证信息,并保存在当前线程的ThreadLocal中,方便业务代码获取用户信息,线程执行完毕后,FilterChainProxy会自动执行清除。

某些应用程序并不完全适合使用默认策略 ,因为它们使用线程有特定方式。 例如:Swing 客户端可能希望Java中的所有线程都使用相同的SecurityContextHolder,您可以在启动时使用策略进行配置,以指定您希望如何存储SecurityContext

其他应用程序可能希望由安全线程派生的线程也采用相同的安全标识。 您可以通过两种方式更改默认模式:

  • 第一种是设置系统属性,通过System.getProperty("spring.security.strategy")读取设置系统属性。
  • 第二种是在调用静态方法,通过调用SecurityContextHolder.setStrategyName(String strategyName)设置。

SecurityContextHolder提供的strategyName有以下几种:

  • SecurityContextHolder.MODE_GLOBAL
  • SecurityContextHolder.MODE_INHERITABLETHREADLOCAL
  • SecurityContextHolder.MODE_THREADLOCAL

大多数应用程序不需要更改默认值。

4.2 SecurityContex

SecurityContex是一个接口,从SecurityContextHolder中获取,包含了Authentication (认证信息),提供了两个简单方法:

public interface SecurityContext extends Serializable {

	/**
	 * 获取当前已通过身份验证的主体或身份验证请求令牌。
	 */
	Authentication getAuthentication();

	/**
	 * 更改当前已通过身份验证的主体或删除身份验证信息
	 */
	void setAuthentication(Authentication authentication);

}

SecurityContex的实现类SecurityContextImpl也很简单:

public class SecurityContextImpl implements SecurityContext {

	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

	private Authentication authentication;

	public SecurityContextImpl() {
	}

	public SecurityContextImpl(Authentication authentication) {
		this.authentication = authentication;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof SecurityContextImpl) {
			SecurityContextImpl other = (SecurityContextImpl) obj;
			if ((this.getAuthentication() == null) && (other.getAuthentication() == null)) {
				return true;
			}
			if ((this.getAuthentication() != null) && (other.getAuthentication() != null)
					&& this.getAuthentication().equals(other.getAuthentication())) {
				return true;
			}
		}
		return false;
	}

	@Override
	public Authentication getAuthentication() {
		return this.authentication;
	}

	@Override
	public int hashCode() {
		return ObjectUtils.nullSafeHashCode(this.authentication);
	}

	@Override
	public void setAuthentication(Authentication authentication) {
		this.authentication = authentication;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(getClass().getSimpleName()).append(" [");
		if (this.authentication == null) {
			sb.append("Null authentication");
		}
		else {
			sb.append("Authentication=").append(this.authentication);
		}
		sb.append("]");
		return sb.toString();
	}

}

4.3 Authentication

Authentication是一个接口,在Spring Security中主要有两个作用:

  • 预认证用户信息:此时没有经过认证,作为AuthenticationManager(认证管理器)的一个输入参数,用于提供认证凭证,在此时.isAuthenticated()值为false
  • 当前认证的用户:表示当前经过身份认证的用户,可以从SecurityContext获取当前的Authentication(认证信息)

Authentication包含以下信息:

  • principal:用户主体标识。 使用用户名/密码进行身份验证时,这通常是UserDetails的用户名。

  • credentials:通常是密码。 在许多情况下,在用户通过身份验证后会清除此信息,以确保它不会泄露。

  • authoritiesGrantedAuthority实例是授予用户的高级权限。 下文详细介绍。

Authentication源码如下所示:

public interface Authentication extends Principal, Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();

    Object getCredentials();

    Object getDetails();

    Object getPrincipal();

    boolean isAuthenticated();

    void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

Authentication接口有很多实现类,对应不同的认证方式。比如:使用用户名/密码进行身份验证时,使用的是UsernamePasswordAuthenticationToken

在这里插入图片描述

4.4 GrantedAuthority

GrantedAuthority实例是授予用户的权限,包括:角色、作用域。可以从Authentication.getAuthorities() 方法获取已认证用户的权限集合。

GrantedAuthority源码如下所示:

public interface GrantedAuthority extends Serializable {
    String getAuthority();
}

只提供一个getAuthority()方法,用于获取已认证用户的权限,默认实现类为SimpleGrantedAuthority,源码如下所示:

public final class SimpleGrantedAuthority implements GrantedAuthority {

	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

	private final String role;

	public SimpleGrantedAuthority(String role) {
		Assert.hasText(role, "A granted authority textual representation is required");
		this.role = role;
	}

	@Override
	public String getAuthority() {
		return this.role;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj instanceof SimpleGrantedAuthority sga) {
			return this.role.equals(sga.getAuthority());
		}
		return false;
	}

	@Override
	public int hashCode() {
		return this.role.hashCode();
	}

	@Override
	public String toString() {
		return this.role;
	}

}

4.5 AuthenticationManager

AuthenticationManager(认证管理器)是一个接口,Spring Security过滤器调用其认证方法进行身份认证,默认实现是ProviderManager

AuthenticationManager源码如下所示:

/**
 * Processes an {@link Authentication} request.
 * 认证管理器 实现认证主要是通过AuthenticationManager接口
 * 在实际开发中,我们可能有多种不同的认证方式,例如:用户名+密码、
 * 邮箱+密码、手机号+验证码等,而这些认证方式的入口始终只有一个,那就是AuthenticationManager。
 *
 * @author Ben Alex
 */
public interface AuthenticationManager {

	/**
	 * authenticate()方法主要做三件事:
	 *   如果验证通过,返回Authentication(通常带上authenticated=true)。
	 *   认证失败抛出AuthenticationException
	 *   如果无法确定,则返回null
	 */
	Authentication authenticate(Authentication authentication) throws AuthenticationException;

}

4.6 ProviderManager

ProviderManager(提供者管理器)默认实现了AuthenticationManager 接口。其源码如下所示:

public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {

	private static final Log logger = LogFactory.getLog(ProviderManager.class);

	private AuthenticationEventPublisher eventPublisher = new NullEventPublisher();

	private List<AuthenticationProvider> providers = Collections.emptyList();

	protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();

	private AuthenticationManager parent;

	private boolean eraseCredentialsAfterAuthentication = true;

	/**
	 * Construct a {@link ProviderManager} using the given {@link AuthenticationProvider}s
	 * @param providers the {@link AuthenticationProvider}s to use
	 */
	public ProviderManager(AuthenticationProvider... providers) {
		this(Arrays.asList(providers), null);
	}

	/**
	 * Construct a {@link ProviderManager} using the given {@link AuthenticationProvider}s
	 * @param providers the {@link AuthenticationProvider}s to use
	 */
	public ProviderManager(List<AuthenticationProvider> providers) {
		this(providers, null);
	}

	/**
	 * Construct a {@link ProviderManager} using the provided parameters
	 * @param providers the {@link AuthenticationProvider}s to use
	 * @param parent a parent {@link AuthenticationManager} to fall back to
	 */
	public ProviderManager(List<AuthenticationProvider> providers, AuthenticationManager parent) {
		Assert.notNull(providers, "providers list cannot be null");
		this.providers = providers;
		this.parent = parent;
		checkState();
	}

	@Override
	public void afterPropertiesSet() {
		checkState();
	}

	private void checkState() {
		Assert.isTrue(this.parent != null || !this.providers.isEmpty(),
				"A parent AuthenticationManager or a list of AuthenticationProviders is required");
		Assert.isTrue(!CollectionUtils.contains(this.providers.iterator(), null),
				"providers list cannot contain null values");
	}

	/**
	 * Attempts to authenticate the passed {@link Authentication} object.
	 * <p>
	 * The list of {@link AuthenticationProvider}s will be successively tried until an
	 * <code>AuthenticationProvider</code> indicates it is capable of authenticating the
	 * type of <code>Authentication</code> object passed. Authentication will then be
	 * attempted with that <code>AuthenticationProvider</code>.
	 * <p>
	 * If more than one <code>AuthenticationProvider</code> supports the passed
	 * <code>Authentication</code> object, the first one able to successfully authenticate
	 * the <code>Authentication</code> object determines the <code>result</code>,
	 * overriding any possible <code>AuthenticationException</code> thrown by earlier
	 * supporting <code>AuthenticationProvider</code>s. On successful authentication, no
	 * subsequent <code>AuthenticationProvider</code>s will be tried. If authentication
	 * was not successful by any supporting <code>AuthenticationProvider</code> the last
	 * thrown <code>AuthenticationException</code> will be rethrown.
	 * @param authentication the authentication request object.
	 * @return a fully authenticated object including credentials.
	 * @throws AuthenticationException if authentication fails.
	 */
	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		Class<? extends Authentication> toTest = authentication.getClass();
		AuthenticationException lastException = null;
		AuthenticationException parentException = null;
		Authentication result = null;
		Authentication parentResult = null;
		int currentPosition = 0;
		int size = this.providers.size();
		for (AuthenticationProvider provider : getProviders()) {
			if (!provider.supports(toTest)) {
				continue;
			}
			if (logger.isTraceEnabled()) {
				logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)",
						provider.getClass().getSimpleName(), ++currentPosition, size));
			}
			try {
				result = provider.authenticate(authentication);
				if (result != null) {
					copyDetails(authentication, result);
					break;
				}
			}
			catch (AccountStatusException | InternalAuthenticationServiceException ex) {
				prepareException(ex, authentication);
				// SEC-546: Avoid polling additional providers if auth failure is due to
				// invalid account status
				throw ex;
			}
			catch (AuthenticationException ex) {
				lastException = ex;
			}
		}
		if (result == null && this.parent != null) {
			// Allow the parent to try.
			try {
				parentResult = this.parent.authenticate(authentication);
				result = parentResult;
			}
			catch (ProviderNotFoundException ex) {
				// ignore as we will throw below if no other exception occurred prior to
				// calling parent and the parent
				// may throw ProviderNotFound even though a provider in the child already
				// handled the request
			}
			catch (AuthenticationException ex) {
				parentException = ex;
				lastException = ex;
			}
		}
		if (result != null) {
			if (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {
				// Authentication is complete. Remove credentials and other secret data
				// from authentication
				((CredentialsContainer) result).eraseCredentials();
			}
			// If the parent AuthenticationManager was attempted and successful then it
			// will publish an AuthenticationSuccessEvent
			// This check prevents a duplicate AuthenticationSuccessEvent if the parent
			// AuthenticationManager already published it
			if (parentResult == null) {
				this.eventPublisher.publishAuthenticationSuccess(result);
			}

			return result;
		}

		// Parent was null, or didn't authenticate (or throw an exception).
		if (lastException == null) {
			lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound",
					new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}"));
		}
		// If the parent AuthenticationManager was attempted and failed then it will
		// publish an AbstractAuthenticationFailureEvent
		// This check prevents a duplicate AbstractAuthenticationFailureEvent if the
		// parent AuthenticationManager already published it
		if (parentException == null) {
			prepareException(lastException, authentication);
		}
		throw lastException;
	}

	@SuppressWarnings("deprecation")
	private void prepareException(AuthenticationException ex, Authentication auth) {
		this.eventPublisher.publishAuthenticationFailure(ex, auth);
	}

	/**
	 * Copies the authentication details from a source Authentication object to a
	 * destination one, provided the latter does not already have one set.
	 * @param source source authentication
	 * @param dest the destination authentication object
	 */
	private void copyDetails(Authentication source, Authentication dest) {
		if ((dest instanceof AbstractAuthenticationToken) && (dest.getDetails() == null)) {
			AbstractAuthenticationToken token = (AbstractAuthenticationToken) dest;
			token.setDetails(source.getDetails());
		}
	}

	public List<AuthenticationProvider> getProviders() {
		return this.providers;
	}

	@Override
	public void setMessageSource(MessageSource messageSource) {
		this.messages = new MessageSourceAccessor(messageSource);
	}

	public void setAuthenticationEventPublisher(AuthenticationEventPublisher eventPublisher) {
		Assert.notNull(eventPublisher, "AuthenticationEventPublisher cannot be null");
		this.eventPublisher = eventPublisher;
	}

	/**
	 * If set to, a resulting {@code Authentication} which implements the
	 * {@code CredentialsContainer} interface will have its
	 * {@link CredentialsContainer#eraseCredentials() eraseCredentials} method called
	 * before it is returned from the {@code authenticate()} method.
	 * @param eraseSecretData set to {@literal false} to retain the credentials data in
	 * memory. Defaults to {@literal true}.
	 */
	public void setEraseCredentialsAfterAuthentication(boolean eraseSecretData) {
		this.eraseCredentialsAfterAuthentication = eraseSecretData;
	}

	public boolean isEraseCredentialsAfterAuthentication() {
		return this.eraseCredentialsAfterAuthentication;
	}

	private static final class NullEventPublisher implements AuthenticationEventPublisher {

		@Override
		public void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication) {
		}

		@Override
		public void publishAuthenticationSuccess(Authentication authentication) {
		}

	}

}

ProviderManager内部维护了多个AuthenticationProvider (认证提供者),在ProviderManager中的 authenticate(Authentication authentication)方法中,每个AuthenticationProvider实例都有一次机会去校验身份认证是否成功,或者是表明自己不能做出决定并将身份认证交给后面的AuthenticationProvider实例处理。

如果所有的AuthenticationProvider实例中没有一个可以处理当前类型认证的,则会抛出ProviderNotFoundException 异常导致认证失败。ProviderNotFoundException 是一个特殊的AuthenticationException异常类型,用于说明ProviderManager没有配置支持指定类型的认证。

在这里插入图片描述

实际上,每个AuthenticationProvider实例都知道如何执行特定类型的身份验证。例如,一个AuthenticationProvider可能能够验证用户名/密码,而另一个可能能够验证SAML断言
我们只需要实现一个AuthenticationManager,并将我们所需要的 AuthenticationProvider实例配置到里面,这样就能保证我们的程序支持多种验证类型。

ProviderManager还允许配置一个可选的父级AuthenticationManager,当AuthenticationProvider实例无法执行身份验证时,可以咨询该父级AuthenticationManager。父级AuthenticationManager可以是任何类型的AuthenticationManager,但它通常是ProviderManager的一个实例。

在这里插入图片描述

实际上,多个ProviderManager实列可能拥有共同的父级AuthenticationManager。在有多个SecurityFilterChain实例的场景中比较常见,这些SecurityFilterChain实例有一些共同的身份验证(共享的父级AuthenticationManager),但也有不同的身份验证机制(不同的ProviderManager)。

在这里插入图片描述

默认情况下,尝试从成功的身份验证请求返回的对象中清除任何敏感凭据信息。 这样可以防止信息(如密码)在保留时限超过必要的时间。

4.7 AuthenticationProvider

AuthenticationProvider(认证提供者)是一个接口,其中提供两个方式,其源码如下所示:

public interface AuthenticationProvider {

	/**
	 * 
	 * @param 身份验证请求对象。
	 * @return 包括凭证的完全经过身份验证的对象。
	 * 如果AuthenticationProvider无法支持对传递的authentication对象进行身份验证,
	 * 则可能返回null。在这种情况下,将尝试下一个支持所提供的Authentication类的AuthenticationProvider。
	 * AuthenticationException 如果身份验证失败抛出异常
	 */
	Authentication authenticate(Authentication authentication) throws AuthenticationException;

	/**
	 * 
	 * @param authentication。
	 * @return 如果此AuthenticationProvider支持特定类型的Authentication对象,则返回true。
	 * 返回true并不保证AuthenticationProvider能够对呈现的Authentication类实例进行身份验证。它只是表明它可以支持对其进行更密切的评估。
	 * AuthenticationProvider仍然可以从authenticate(Authentication authentication)方法返回null,以表明应该尝试下一个支持所提供的Authentication类的AuthenticationProvider。
	 * 能够执行身份验证的AuthenticationProvider的选择是在ProviderManager运行时进行的。
	 */
	boolean supports(Class<?> authentication);

}

可以在ProviderManager中注入多个AuthenticationProvider。每个AuthenticationProvider执行特定类型的身份验证。

例如:

  • DaoAuthenticationProvider支持基于用户名/密码的身份验证。
  • JwtAuthenticationProvider支持对JWT令牌的身份验证。

Spring Security中默认提供多个ProviderManager实现类,如下图所示:

在这里插入图片描述
可以自定义扩展认证方式,例如:手机验证码等。

4.8 AuthenticationEntryPoint

AuthenticationEntryPoint(认证入口点)用于 ExceptionTranslationFilter发现认证异常时启动身份验证方案,例如:未经身份验证的请求,执行到登录页面的重定向。

AuthenticationEntryPoint有以下实现类:

在这里插入图片描述
例如:LoginUrlAuthenticationEntryPoint,会在表单登录失败或未经身份验证的请求,执行重定向(或转发)到登录表单页面的URL

4.9 AbstractAuthenticationProcessingFilter

AbstractAuthenticationProcessingFilter是一个抽象类,可以作为用户身份验证的基础,用户名/密码身份验证对应过滤器UsernamePasswordAuthenticationFilter就是继承的AbstractAuthenticationProcessingFilter

例如:自定义手机验证码认证过滤器就可以通过继承AbstractAuthenticationProcessingFilter进行开发,主要是提供认证成功、失败的相关处理:

在这里插入图片描述

①当用户提交凭证之后,AbstractAuthenticationProcessingFilter通过HttpServletRequest创建一个Authentication用于进行身份验证。创建的Authentication的具体类型依赖于AbstractAuthenticationProcessingFilter的子类。

例如UsernamePasswordAuthenticationFilter从在HttpServletRequest中提交的用户名和密码创建一个UsernamePasswordAuthenticationToken

②接着Authentication对象将被传递到AuthenticationManager中进行身份认证。

③如果认证失败:

  • SecurityContextHolder被清空。
  • RememberMeServices.loginFail被调用,如果没有配置remember-me,则不执行。
  • AuthenticationFailureHandler被调用。

④如果认证成功:

  • SessionAuthenticationStrategy收到新登录的通知 。
  • Authentication被设置到SecurityContextHolder,如果需要保存,以便在将来的请求中自动设置它,则必须显式调用SecurityContextRepository#saveContext(详见下文)。
  • RememberMeServices.loginSuccess被调用,如果没有配置remember-me,则不执行。
  • ApplicationEventPublisher发布InteractiveAuthenticationSuccessEvent事件。
  • AuthenticationSuccessHandler被调用。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【采坑分享】导出文件流responseType:“blob“如何提示报错信息

目录 前言&#xff1a; 采坑之路 总结&#xff1a; 前言&#xff1a; 近日&#xff0c;项目中踩了一个坑分享一下经验&#xff0c;也避免下次遇到方便解决。项目基于vue2axioselement-ui&#xff0c;业务中导出按钮需要直接下载接口中的文件流。正常是没有问题&#xff0c;但…

STM32 默认时钟更改 +debug调试

STM32时钟 文章目录 STM32时钟前言一、修改系统时钟二、DEBUG 前言 为什么我们要改STM32的时钟呢&#xff0c;打个比方在做SPI驱动的时候&#xff0c;需要16M的时钟&#xff0c;但是stm32默认是72的分频分不出来&#xff0c;这个时候我们就要改系统时钟了&#xff0c;那么怎么…

[科普] 无刷直流电机驱动控制原理图解

Title: [科普] 无刷直流电机驱动控制原理图解 文章目录 I. 引言II. 直流电机的原理1. 有刷直流电机和无刷直流电机的区别2. 有刷直流电机的运行原理3. 既是电动机又是发电机 III. 无刷直流电机的原理1. 无刷直流电机与永磁同步电机的区别2. 无刷直流电机的换向控制原理3. 无刷直…

Spring Cache(缓存框架)

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

指针的进阶

重中之重&#xff1a; 目录 1.字符指针&#xff1a; 2.指针数组 3.数组指针 4.数组参数、指针参数 5.函数指针 1.字符指针&#xff1a; 一般实现&#xff1a; int main() {char ch w;char *pc &ch;*pc w;return 0; } 二班实现&#xff1a; int main() {const c…

人工智能基础_机器学习050_对比sigmoid函数和softmax函数的区别_两种分类器算法的区别---人工智能工作笔记0090

可以看到最上面是softmax的函数对吧,但是如果当k = 2 那么这个时候softmax的函数就可以退化为sigmoid函数,也就是 逻辑斯蒂回归了对吧 我们来看一下推导过程,可以看到上面是softmax的函数 可以看到k=2 表示,只有两个类别对吧,两个类别的分类不就是sigmoid函数嘛对吧,所以说 …

3 时间序列预测入门:TCN

0 引言 TCN&#xff08;全称Temporal Convolutional Network&#xff09;&#xff0c;时序卷积网络&#xff0c;是在2018年提出的一个卷积模型&#xff0c;但是可以用来处理时间序列。 论文&#xff1a;https://arxiv.org/pdf/1803.01271.pdf 一维卷积&#xff1a;在时间步长方…

2024年天津天狮学院专升本计算机科学与技术《数据结构》考试大纲

2024年天津天狮学院计算机科学与技术专业高职升本入学考试《数据结构》考试大纲 一、考试性质 《数据结构》专业课程考试是天津天狮学院计算机科学与技术专业高职升本入学考 试的必考科目之一&#xff0c;其性质是考核学生是否达到了升入本科继续学习的要求而进行的选拔性考试…

超详细csapp-linklab之第一阶段“输出学号”实验报告

该实验的主题是“链接”。 准备工具 虚拟机&#xff0c;Ubuntu32位&#xff0c;hexedit&#xff0c;main.o&#xff0c;phase1.o&#xff0c;该实验的C代码框架如下 // main.c void (*phase)(); /*初始化为0*/int main( int argc, const char* argv[] ) {if ( phase )(*ph…

十大排序之选择排序(详解)

文章目录 &#x1f412;个人主页&#x1f3c5;算法思维框架&#x1f4d6;前言&#xff1a; &#x1f380;选择排序 时间复杂度O(n^2)&#x1f387;1. 算法步骤思想&#x1f387;2.动画实现&#x1f387; 3.代码实现 &#x1f412;个人主页 &#x1f3c5;算法思维框架 &#x1f…

三轴加速度计LIS2DW12开发(1)----轮询获取加速度数据

STM32WB55开发.6--FUS更新 概述视频教学通信模式管脚定义IIC通信模式速率生成STM32CUBEMX串口配置IIC配置CS和SA0设置串口重定向参考程序初始换管脚获取ID复位操作BDU设置设置传感器的量程配置过滤器链配置电源模式设置输出数据速率轮询获取加速度演示 概述 本文将介绍如何驱…

乘波前行的问题

1.问题&#xff1a; 考虑两个信号叠加在一起&#xff0c;比如&#xff0c;一个是工频信号50Hz&#xff0c;一个是叠加的高频信号比如有3KHz&#xff0c;简单起见&#xff0c;两个信号都是幅值固定的标准的正弦波&#xff0c;现在我们期望得到那个高频信号&#xff0c;相对工频…

Linux 网络通信

(一)套接字Socket概念 Socket 中文意思是“插座”&#xff0c;在 Linux 环境下&#xff0c;用于表示进程 x 间网络通信的特殊文件 类型。本质为内核借助缓冲区形成的伪文件。 既然是文件&#xff0c;那么理所当然的&#xff0c;我们可以使用文件描述符引用套接字。Linux 系统…

【MATLAB源码-第88期】基于matlab的灰狼优化算法(GWO)的栅格路径规划,输出做短路径图和适应度曲线

操作环境&#xff1a; MATLAB 2022a 1、算法描述 灰狼优化算法&#xff08;Grey Wolf Optimizer, GWO&#xff09;是一种模仿灰狼捕食行为的优化算法。灰狼是群居动物&#xff0c;有着严格的社会等级结构。在灰狼群体中&#xff0c;通常有三个等级&#xff1a;首领&#xff…

聊一聊索引覆盖的好处

问&#xff1a;索引覆盖啥意思&#xff1f; 答&#xff1a;若查询的字段在二级索引的叶子节点中&#xff0c;则可直接返回结果&#xff0c;无需回表。这种通过组合索引避免回表的优化技术也称为索引覆盖&#xff08;Covering Index&#xff09;。在叶子节点中的包括索引字段和主…

Python之内置函数和模块

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

4/5G互操作 EPSFB讲解

今天我们来讲一下4/5G之间之间互操作&#xff0c;以及5G的EPSFB是基于什么实现的~ 目录 4/5G互操作 重选 切换 基于覆盖的切换 基于业务的切换 两个面试问题 想要加快4G切换5G的速度&#xff0c;调哪个参数怎么调高效&#xff1f; 想要减慢5G切换4G的速度调哪个参数怎…

嵌入式硬件电路·电平

目录 1. 电平的概念 1.1 高电平 1.2 低电平 2. 电平的使用场景 2.1 高电平使能 2.2 低电平使能 2.3 失能 1. 电平的概念 电平是指电信号电压的大小或高低状态。在数字电子学中&#xff0c;电平有两种状态&#xff0c;高电平和低电平&#xff0c;用来表示二进制中…

Python之基础语法和六大数据类型

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

C++ 数据结构之-最小栈(MinStack)

最小栈 最小栈&#xff08;Min Stack&#xff09;是一个支持常数时间复杂度获取栈中最小元素的特殊栈数据结构。通常&#xff0c;标准的栈数据结构只支持在常数时间内执行入栈&#xff08;push&#xff09;和出栈&#xff08;pop&#xff09;操作&#xff0c;但无法在常数时间内…