UsernamePasswordAutheticationFilter源码解读和实践

UsernamePasswordAuthenticationFilter的目录

  • 一、概述(重点)
  • 二、标红小步骤解读
    • 2.1 步骤1(标红1)
      • 2.1.1 AbstractAuthenticationProcessingFilter
      • 2.1.2 UsernamePasswordAuthenticationFilter
    • 2.3 步骤2 和 步骤3(标红2 和 标红3)
      • 2.3.1 解读
      • 2.3.2 总结
    • 2.4 步骤4(标红4)
      • 2.4.1 AbstractUserDetailsAuthenticationProvider
      • 2.4.2 DaoAuthenticationProvider
      • 2.4.3 总结 UserDetailsService & UserCache
  • 三、说完源码,开始做实践
    • 3.1 身份验证后置处理
    • 3.2 前后端分离项目的登录配置
  • 四、总结

如果你对SecurityFilterChain之前的过程存在疑惑,那么可以去研究一下下面两个内容,其实懂不懂下下面两个内容都不会影响你阅读本篇文章。

  • DelegatingFilterProxy
  • FilterProxyChain & SecurityFilterChain

下面会更新上面两个内容,可以关注一下 Spring源码研究导航

一、概述(重点)

第一步,主要对整个UsernamePasswordAutheticationFilter有一个全面的认知,这样读源码才事半功倍。下面会通过小章节对每一个标红的步骤进行解读。

在这里插入图片描述

二、标红小步骤解读

2.1 步骤1(标红1)

AbstractAuthenticationProcessingFilter
«Interface»
ApplicationEventPublisherAware
«Interface»
Aware
«Interface»
BeanNameAware
«Interface»
DisposableBean
«Interface»
EnvironmentAware
«Interface»
EnvironmentCapable
«Interface»
Filter
GenericFilterBean
«Interface»
InitializingBean
«Interface»
MessageSourceAware
«Interface»
ServletContextAware
UsernamePasswordAuthenticationFilter

看一下UsernamePasswordAuthenticationFilter的继承图,还是相当复杂的,不过大多数都跟Spring有关,我们只需要关注AbstractAuthenticationProcessingFilter抽象类和Filter接口就差不多了。

2.1.1 AbstractAuthenticationProcessingFilter

先看AbstractAuthenticationProcessingFilterdoFilter方法,继承了Filter方法之后Servlet会执行doFilter,所以直接看doFiter方法就可以了。

public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBeanimplements ApplicationEventPublisherAware, MessageSourceAware {...@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);}// doFilter逻辑主要有三个// 1. 通过attemptAuthentication对用户身份进行验证// 2. 身份验证成功之后执行successfulAuthentication// 3. 身份验证失败执行unsuccessfulAuthenticationprivate void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {// 判断请求是否符合过滤器的要求。// 使用RequestMatcher判断,判断的条件跟URL和请求方法有关。if (!requiresAuthentication(request, response)) {chain.doFilter(request, response);return;}try {// attemptAuthentication是子类UsernamePasswordAuthenticationFilter实现的。// 这个方法跟用户身份验证有关。Authentication authenticationResult = attemptAuthentication(request, response);if (authenticationResult == null) {return;}this.sessionStrategy.onAuthentication(authenticationResult, request, response);if (this.continueChainBeforeSuccessfulAuthentication) {chain.doFilter(request, response);}// 身份验证成功,successfulAuthentication(request, response, chain, authenticationResult);}catch (InternalAuthenticationServiceException failed) {this.logger.error("An internal error occurred while trying to authenticate the user.", failed);unsuccessfulAuthentication(request, response, failed);}catch (AuthenticationException ex) {unsuccessfulAuthentication(request, response, ex);}}...
}

身份验证成功之后的回调。

public class SavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {protected final Log logger = LogFactory.getLog(this.getClass());private RequestCache requestCache = new HttpSessionRequestCache();// 负责重定向回原来的页面,下面有详细的解释。protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,Authentication authResult) throws IOException, ServletException {// 保存认证成功之后的用户信息SecurityContext context = SecurityContextHolder.createEmptyContext();context.setAuthentication(authResult);SecurityContextHolder.setContext(context);this.securityContextRepository.saveContext(context, request, response);if (this.logger.isDebugEnabled()) {this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));}// 这个母鸡this.rememberMeServices.loginSuccess(request, response, authResult);if (this.eventPublisher != null) {this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));}// 重定向到原始的访问地址,就比如:// 你访问localhost:8080/user,由于你没有认证,所以后端会让浏览器重定向到身份认证的页面http://localhost:8080/login// 你在http://localhost:8080/login网址通过了认证之后,后端会让浏览器重定向回localhost:8080/userthis.successHandler.onAuthenticationSuccess(request, response, authResult);}public void setRequestCache(RequestCache requestCache) {this.requestCache = requestCache;}}

身份验证失败之后的回调。

public class SimpleUrlAuthenticationFailureHandler implements AuthenticationFailureHandler {...// 失败处理也分三个部分// 1. 如果没有指定失败的重定向链接,那么直接调用response.sendError// 2. 如果指定了defaultFailureUrl,forwardToDestination=true,那么就把请求转发给defaultFailureUrl,这个是内部转发,共享request。// 3. 最后就是重定向了,重定向到defaultFailureUrl。@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,AuthenticationException exception) throws IOException, ServletException {if (this.defaultFailureUrl == null) {if (this.logger.isTraceEnabled()) {this.logger.trace("Sending 401 Unauthorized error since no failure URL is set");} else {this.logger.debug("Sending 401 Unauthorized error");}response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());return;}saveException(request, exception);if (this.forwardToDestination) {this.logger.debug("Forwarding to " + this.defaultFailureUrl);request.getRequestDispatcher(this.defaultFailureUrl).forward(request, response);}else {this.redirectStrategy.sendRedirect(request, response, this.defaultFailureUrl);}}...
}

2.1.2 UsernamePasswordAuthenticationFilter

这个类主要看attemptAuthentication方法。

public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {...@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {if (this.postOnly && !request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());}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);// 这里就是要进入到我们红色圆圈2了。return this.getAuthenticationManager().authenticate(authRequest);}...
}

2.3 步骤2 和 步骤3(标红2 和 标红3)

2.3.1 解读

通过上面的分析,可以从UsernamePasswordAuthenticationFilter的return this.getAuthenticationManager().authenticate(authRequest)代码得知,UsernamePasswordAuthenticationFilter会把身份认证的任务抛给AuthenticationManager。接下来我们对AuthenticationManager进行分析,从下图得知ProviderManager实现了AuthenticationManager接口,这里主要是理解ProviderManager实现类

«Interface»
AuthenticationManager
«Interface»
Aware
«Interface»
InitializingBean
«Interface»
MessageSourceAware
ProviderManager

下面主要看一下ProviderManager的核心属性(providers、parent)和核心方法authenticate

public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {// 两个核心属性private List<AuthenticationProvider> providers = Collections.emptyList();private AuthenticationManager parent;// 方法主要有两个核心的方法// 1. 首先从认证提供器(AuthenticationProvider)里边做身份验证// 2. 如果认证提供器认证失败,那就继续给上一级的认证管理器(AuthenticationManager)做认证@Overridepublic 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();// 先遍历所有的AuthenticationProvider,通过他们去做身份验证。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 statusthrow ex;}catch (AuthenticationException ex) {lastException = ex;}}// 如果所有的AuthenticationProvider的身份验证都失败了,那么去找ProviderManager父级。if (result == null && this.parent != null) {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)) {((CredentialsContainer) result).eraseCredentials();}if (parentResult == null) {this.eventPublisher.publishAuthenticationSuccess(result);}return result;}if (lastException == null) {lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound",new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}"));}if (parentException == null) {prepareException(lastException, authentication);}throw lastException;}}

2.3.2 总结

ProviderManagerauthenticate方法中可以总结出下图的结构,虽然有点抽象。主要表达的意思是,ProviderManager内部维护了一堆AuthenticationProvider和父级AuthenticationManagerauthenticate的逻辑是先把认证交给一堆AuthenticationProvider做认证,AuthenticationProvider认证失败了才交给父级。

在这里插入图片描述

2.4 步骤4(标红4)

上面# 三、步骤2 和 步骤3(标红2 和 标红3) 对AuthenticationProvider做了一个介绍,最终的身份认证会交给它,现在我们对它做详细的介绍。AuthenticationProvider有超级多的实现类,我们只需要关注AbstractUserDetailsAuthenticationProvider抽象类,以及抽象类的实现类DaoAuthenticationProvider

AbstractJaasAuthenticationProvider
AbstractUserDetailsAuthenticationProvider
AnonymousAuthenticationProvider
«Interface»
AuthenticationProvider
DaoAuthenticationProvider
DefaultJaasAuthenticationProvider
JaasAuthenticationProvider
PreAuthenticatedAuthenticationProvider
RememberMeAuthenticationProvider
RemoteAuthenticationProvider
RunAsImplAuthenticationProvider
TestingAuthenticationProvider

2.4.1 AbstractUserDetailsAuthenticationProvider

因为AbstractUserDetailsAuthenticationProvider实现了AuthenticationProvider接口,所以我们直接挑authenticate来看。
下面方法主要看两个方法

  1. 查缓存,如果缓存有用户,就直接返回了。可以发现默认实现是NullUserCache,啥也不做。
  2. 做身份验证的retrieveUser方法,这是一个抽象方法,所有我们要看子类DaoAuthentiationProvider。
public abstract class AbstractUserDetailsAuthenticationProviderimplements AuthenticationProvider, InitializingBean, MessageSourceAware {private UserCache userCache = new NullUserCache();@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, () -> this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported"));String username = determineUsername(authentication);boolean cacheWasUsed = true;// 查缓存,这里也可以进行扩展,我们可以把缓存实现成RedisUserDetails user = this.userCache.getUserFromCache(username);if (user == null) {cacheWasUsed = false;try {// 调用抽象方法user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);}catch (UsernameNotFoundException ex) {this.logger.debug("Failed to find user '" + username + "'");if (!this.hideUserNotFoundExceptions) {throw ex;}throw new BadCredentialsException(this.messages .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));}Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");}try {this.preAuthenticationChecks.check(user);additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);}catch (AuthenticationException ex) {if (!cacheWasUsed) {throw ex;}// There was a problem, so try again after checking// we're using latest data (i.e. not from the cache)cacheWasUsed = false;user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);this.preAuthenticationChecks.check(user);additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);}this.postAuthenticationChecks.check(user);if (!cacheWasUsed) {this.userCache.putUserInCache(user);}Object principalToReturn = user;if (this.forcePrincipalAsString) {principalToReturn = user.getUsername();}return createSuccessAuthentication(principalToReturn, authentication, user);}protected abstract UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException;
}

2.4.2 DaoAuthenticationProvider

public abstract class AbstractUserDetailsAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {@Overrideprotected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {prepareTimingAttackProtection();try {// 这里就是我们自己扩展的地方了,调用UserDetailsService.loadUserByUsernameUserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);if (loadedUser == null) {throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");}return loadedUser;}catch (UsernameNotFoundException ex) {mitigateAgainstTimingAttack(authentication);throw ex;}catch (InternalAuthenticationServiceException ex) {throw ex;}catch (Exception ex) {throw new InternalAuthenticationServiceException(ex.getMessage(), ex);}}
}

2.4.3 总结 UserDetailsService & UserCache

上面的AbstractUserDetailsAuthenticationProvider的authenticate方法逻辑是先查缓存(UserCache),再交给UserDetailsService。那如何自定义自己的UserDetailsService和UserCache呢?在下个大章节说。

三、说完源码,开始做实践

3.1 身份验证后置处理

主要是failureHandler.setUseForward(true),如果设置为false就重定向,如果设置为true就是内部转发。

@EnableWebSecurity
public class SpringSecurityConfig {@Autowiredpublic void configure(AuthenticationManagerBuilder builder) throws Exception {builder.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser(User.withUsername("admin").password(new BCryptPasswordEncoder().encode("123")).roles("admin"));}@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.formLogin().failureHandler(buildAuthenticationFailureHandler()).successHandler(buildSimpleUrlAuthenticationSuccessHandler());return http.build();}// 失败后置处理SimpleUrlAuthenticationFailureHandler buildAuthenticationFailureHandler() {SimpleUrlAuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();failureHandler.setUseForward(true);failureHandler.setDefaultFailureUrl("/other/login/fail");return failureHandler;}
}@RestController
@RequestMapping("/other")
public class UserController {@PostMapping("/login/fail")public AjaxResult fail() {return AjaxResult.failure("账号或密码错误!");}@PostMapping("/login/success")public AjaxResult success() {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();return AjaxResult.success("登录成功!", authentication.getPrincipal());}
}

登录失败的效果

在这里插入图片描述登录成功的效果
在这里插入图片描述

3.2 前后端分离项目的登录配置

这个配置加上了登录的后置处理,重点在移除了自带的登录页面,设置了登录url,有的同学很懵逼,我解释一下,设置了登录url之后UsernamePasswordAuthenticationFilter会拦截/user/login做登录处理,然后就是走我们# 一、概述(重点)那个图的整个过程了

@EnableWebSecurity
public class SpringSecurityConfig {@Autowiredpublic void configure(AuthenticationManagerBuilder builder) throws Exception {builder.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser(User.withUsername("admin").password(new BCryptPasswordEncoder().encode("123")).roles("admin"));}@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {// 关闭自带的登录页面http.getConfigurer(DefaultLoginPageConfigurer.class).disable();http.formLogin().failureHandler(buildAuthenticationFailureHandler()).successHandler(buildSimpleUrlAuthenticationSuccessHandler())// 设置登录url.loginProcessingUrl("/user/login").and().csrf().disable();return http.build();}// 登录失败处理器SimpleUrlAuthenticationFailureHandler buildAuthenticationFailureHandler() {SimpleUrlAuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();failureHandler.setUseForward(true);failureHandler.setDefaultFailureUrl("/other/login/fail");return failureHandler;}// 登录成功处理器,内部转发AuthenticationSuccessHandler buildSimpleUrlAuthenticationSuccessHandler() {return ((request, response, authentication) -> request.getRequestDispatcher("/other/login/success").forward(request, response));}
}@RestController
@RequestMapping("/other")
public class UserController {@PostMapping("/login/fail")public AjaxResult fail() {return AjaxResult.failure("账号或密码错误!");}@PostMapping("/login/success")public AjaxResult success() {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();return AjaxResult.success("登录成功!", authentication.getPrincipal());}
}

登录成功结果

在这里插入图片描述

登录失败结果

在这里插入图片描述

四、总结

目前场景比较少,各位有遇到的场景可以留言评论,我给出代码。

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

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

相关文章

【SpringBoot篇】解决Redis分布式锁的 误删问题 和 原子性问题

文章目录 &#x1f354;Redis的分布式锁&#x1f6f8;误删问题&#x1f388;解决方法&#x1f50e;代码实现 &#x1f6f8;原子性问题&#x1f339;Lua脚本 ⭐利用Java代码调用Lua脚本改造分布式锁&#x1f50e;代码实现 &#x1f354;Redis的分布式锁 Redis的分布式锁是通过利…

synchronized 浅读解析 一

引言 在学习synchronized前&#xff0c;我们实际上是需要先了解Java对象在jvm中的储存结构&#xff0c;在了解了它的实际储存方式后&#xff0c;再对后边的锁学习会有一个更好更深入的理解。 一、对象结构 我们为什么要知道什么是对象头 在学习synchronized的时候&#xff0c…

破除Github API接口的访问次数限制

破除Github API接口的访问次数限制 1、Github介绍2、Github API接口2.1 介绍2.2 使用方法 3、Github API访问限制3.1 访问限制原因3.2 访问限制类别 4、Github API访问限制破除4.1 限制破除原理4.2 限制破除示例 1、Github介绍 Github&#xff0c;是一个面向开源及私有软件项目…

提升你的PHP开发效率:探索JetBrains PhpStorm 2022的全新特性

在当今快速发展的软件开发领域&#xff0c;选择一个强大且高效的开发工具对于提升开发效率、保证代码质量至关重要。对于PHP开发者来说&#xff0c;JetBrains PhpStorm一直是市场上最受欢迎的IDE之一。随着JetBrains PhpStorm 2022的发布&#xff0c;这款工具带来了一系列创新功…

MybatisPlus快速入门及常见设置

目录 一、快速入门 1.1 准备数据 1.2 创建SpringBoot工程 1.3 使用MP 1.4 获取Mapper进行测试 二、常用设置 2.1 设置表映射规则 2.1.1 单独设置 2.1.2 全局设置 2.2 设置主键生成策略 2.2.1 为什么会有雪花算法&#xff1f; 2.2.2 垂直分表 2.2.3 水平分表 2.…

JavaScript流程控制详解之顺序结构和选择结构

流程控制 流程控制&#xff0c;指的是控制程序按照怎样的顺序执行 在JavaScript中&#xff0c;共有3种流程控制方式 顺序结构选择结构循环结构 顺序结构 在JavaScript中&#xff0c;顺序结构是最基本的结构&#xff0c;所谓的顺序结构&#xff0c;指的是代码按照从上到下、…

上海亚商投顾:沪指涨超3% 深成指和创指双双飙涨超6%

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 今日A股三大指数一改近期低迷状态&#xff0c;早盘小幅低开后一路高歌猛进集体大涨&#xff0c;沪指涨超3%&am…

锁(二)队列同步器AQS

一、队列同步器AQS 1、定义 用来构建锁或者其他同步组件的基础框架&#xff0c;它使用了一个int成员变量表示同步状态&#xff0c;通过内置的FIFO队列来完成资源获取线程的排队工作。是实现锁的关键。 2、实现 同步器的设计是基于模板方法模式的&#xff0c;也就是说&#…

如何安装x11vnc并结合cpolar实现win远程桌面Deepin

文章目录 1. 安装x11vnc2. 本地远程连接测试3. Deepin安装Cpolar4. 配置公网远程地址5. 公网远程连接Deepin桌面6. 固定连接公网地址7. 固定公网地址连接测试 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的 人工智能学习网站&#xff0c; 通俗易懂&#xff…

11.0 Zookeeper watcher 事件机制原理剖析

zookeeper 的 watcher 机制&#xff0c;可以分为四个过程&#xff1a; 客户端注册 watcher。服务端处理 watcher。服务端触发 watcher 事件。客户端回调 watcher。 其中客户端注册 watcher 有三种方式&#xff0c;调用客户端 API 可以分别通过 getData、exists、getChildren …

GLSL ES 1.0

GLSL ES 概述 写在前面 程序是大小写敏感的每一个语句都应该以英文分号结束一个shader必须包含一个main函数&#xff0c;该函数不接受任何参数&#xff0c;并且返回voidvoid main() { }数据值类型 GLSL支持三种数据类型&#xff1a; 整型浮点型&#xff1a;必须包含小数点&…

python 动态显示数据。

界面显示动态的数据。 from time import sleep import serialimport tkinter as tklis[1,10,40] # 打开串行端口 ser serial.Serial(COM3, 9600) # 9600为波特率&#xff0c;根据实际情况进行调整# 创建窗口和画布 window tk.Tk() canvas tk.Canvas(window, width400, heig…

高阶滤波器

一阶后向差分&#xff1a;s&#xff08;1-z^(-1)&#xff09;/T dx/dt[x(k)-x(k-1)]/T[x(k)-x(k)z^(-1)]/Tx(k)*&#xff08;1-z^(-1)&#xff09;/T 一阶前向差分&#xff1a;s(z-1)/T dx/dt[x(k1)-x(k)]/T[z*x(k)-x(k)]/Tx(k)*(z-1)/T 双线性差分&#xff1a;s(2/T)*(1-z…

通过 docker-compose 部署 Flink

概要 通过 docker-compose 以 Session Mode 部署 flink 前置依赖 Docker、docker-composeflink 客户端docker-compose.yml version: "2.2" services:jobmanager:image: flink:1.17.2ports:- "8081:8081"command: jobmanagervolumes:- ${PWD}/checkpoin…

【大模型上下文长度扩展】FlashAttention:高效注意力计算的新纪元

FlashAttention&#xff1a;高效注意力计算的新纪元 核心思想核心操作融合&#xff0c;减少高内存读写成本分块计算&#xff08;Tiling&#xff09;&#xff0c;避免存储一次性整个矩阵块稀疏注意力&#xff0c;处理长序列时的效率问题利用快速 SRAM&#xff0c;处理内存与计算…

【大模型上下文长度扩展】LongQLoRA:单GPU(V100)环境下的语言模型优化方案

LongQLoRA 核心问题子问题1: 预定义的上下文长度限制子问题2: 训练资源的需求高子问题3: 保持模型性能分析不足 LongQLoRA方法拆解子问题1: 上下文长度限制子问题2: 高GPU内存需求子问题3: 精确量化导致的性能损失分析不足效果 论文&#xff1a;https://arxiv.org/pdf/2311.048…

docker镜像结构

# 基础镜像 FROM openjdk:11.0-jre-buster # 设定时区 ENV TZAsia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # 拷贝jar包 COPY docker-demo.jar /app.jar # 入口 ENTRYPOINT ["java", "-jar"…

游泳耳机推荐性价比排行榜,四大高性价比游泳耳机推荐

随着运动健康意识的提高&#xff0c;越来越多的朋友选择在游泳馆进行锻炼。然而&#xff0c;在水中享受音乐并非易事&#xff0c;这就需要一款真正防水的耳机。尽管市面上有许多声称具备防水功能的耳机产品&#xff0c;但实际使用中往往难以达到理想的防水效果。为了帮助大家找…

之前看过的前序遍历的线索二叉树感觉写的有点问题 这里更新一下我的思路

前序线索化 #include<iostream> using namespace std;typedef int datatype; typedef struct BitNode {datatype Data;struct BitNode* leftchild;struct BitNode* rightchild;int lefttag;int righttag; }Node; #pragma region 前序线索化递归遍历 Node* previous NUL…

maven依赖报错处理(或者maven怎么刷新都下载不了依赖)

maven依赖报错&#xff0c;或者不报错&#xff0c;但是怎么刷新maven都没反应&#xff0c;可以试一下以下操作 当下载jar的时候&#xff0c;如果断网&#xff0c;或者连接超时的时候&#xff0c;会自动在文件夹中创建一个名为*lastupdate的文件&#xff0c;当有了这个文件之后…