Spring MVC 源码分析之 DispatcherServlet#processDispatchResult方法

前言:

前面的篇章我们分析了 Spring MVC 工作流程中的 HandlerMapping、HandlerAdapter 的适配过程、拦截器的工作流程,以及处理业务请求的过程,本篇我们分析一下处理完业务解析视图的方法,也就是 DispatcherServlet#processDispatchResult 方法。

Spring MVC 知识传送门:

详解 Spring MVC(Spring MVC 简介)

Spring MVC 初始化源码分析

Spring MVC 工作流程源码分析

Spring MVC 源码分析之 DispatcherServlet#getHandler 方法

Spring MVC 源码分析之 DispatcherServlet#getHandlerAdapter 方法

Spring MVC 源码分析之 AbstractHandlerMethodAdapter#handle 方法

DispatcherServlet#processDispatchResult 方法源码分析

DispatcherServlet#processDispatchResult 方法名直译就是初始调度结果,其实就是解析 ModelAndView,源码也很清晰,先是判断是否有异常,有异常就解析异常视图,否则就开始解析 ModelAndView 的流程。

//org.springframework.web.servlet.DispatcherServlet#processDispatchResult
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {boolean errorView = false;//是否有异常if (exception != null) {//异常是否是模型视图异常if (exception instanceof ModelAndViewDefiningException) {this.logger.debug("ModelAndViewDefiningException encountered", exception);//获取异常视图mv = ((ModelAndViewDefiningException)exception).getModelAndView();} else {//获取异常解析器Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;//异常视图解析mv = this.processHandlerException(request, response, handler, exception);//异常视图不为空 赋值 errorView 为 trueerrorView = mv != null;}}//模型视图是否为空 模型视图是否被标识为清空if (mv != null && !mv.wasCleared()) {//解析并渲染视图 重点关注this.render(mv, request, response);//errorView if (errorView) {//清除错误请求属性WebUtils.clearErrorRequestAttributes(request);}} else if (this.logger.isTraceEnabled()) {this.logger.trace("No view rendering, null ModelAndView returned.");}//判断是否是异步处理 if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {//不是异步处理 if (mappedHandler != null) {//注册后置拦截器mappedHandler.triggerAfterCompletion(request, response, (Exception)null);}}
}

DispatcherServlet#processHandlerException 方法源码分析

DispatcherServlet#processHandlerException 方法的作用就是解析异常视图,获取所有异常解析器,遍历解析视图,解析到视图就停止循环,设置视图相关属性返回。

//org.springframework.web.servlet.DispatcherServlet#processHandlerException
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {//删除请求中的 HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE 属性request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);ModelAndView exMv = null;//异常解析器为空判断if (this.handlerExceptionResolvers != null) {//迭代遍历Iterator var6 = this.handlerExceptionResolvers.iterator();while(var6.hasNext()) {HandlerExceptionResolver resolver = (HandlerExceptionResolver)var6.next();//解析 exMv = resolver.resolveException(request, response, handler, ex);//解析到视图 就跳出循环if (exMv != null) {break;}}}//视图为 null 判断if (exMv != null) {//视图不为空if (exMv.isEmpty()) {//设置异常属性request.setAttribute(EXCEPTION_ATTRIBUTE, ex);//返回return null;} else {//为空  判断是否有视图if (!exMv.hasView()) {//没有视图  获取默认视图名称String defaultViewName = this.getDefaultViewName(request);//默认视图名称为空判断if (defaultViewName != null) {//设置视图名称exMv.setViewName(defaultViewName);}}if (this.logger.isTraceEnabled()) {this.logger.trace("Using resolved error view: " + exMv, ex);} else if (this.logger.isDebugEnabled()) {this.logger.debug("Using resolved error view: " + exMv);}//设置请求相关的属性WebUtils.exposeErrorRequestAttributes(request, ex, this.getServletName());//返回视图return exMv;}} else {//视图为空抛出异常throw ex;}
}

DispatcherServlet#render 方法源码分析

DispatcherServlet#render 方法主要可以分为两步,分别是创建视图 View 和 解析渲染视图 view.render。

//org.springframework.web.servlet.DispatcherServlet#render
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {//确认语言环境 常说的国际化Locale locale = this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale();//为响应设置 Localeresponse.setLocale(locale);//获取视图名称String viewName = mv.getViewName();View view;//视图名称为 null 判断if (viewName != null) {//解析视图名称 重点关注view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);//视图为空判断if (view == null) {//视图为空 抛出异常throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + this.getServletName() + "'");}} else {//视图名称为 null//从模型视图中获取 视图view = mv.getView();//视图为 null 判断  if (view == null) {//视图为 null  抛出异常throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a View object in servlet with name '" + this.getServletName() + "'");}}if (this.logger.isTraceEnabled()) {this.logger.trace("Rendering view [" + view + "] ");}try {//模型视图的状态判断if (mv.getStatus() != null) {//不为空 把模型视图的状态设置给 responseresponse.setStatus(mv.getStatus().value());}//视图解析渲染 重点关注view.render(mv.getModelInternal(), request, response);} catch (Exception var8) {if (this.logger.isDebugEnabled()) {this.logger.debug("Error rendering view [" + view + "]", var8);}throw var8;}
}

DispatcherServlet#resolveViewName 方法源码分析

DispatcherServlet#resolveViewName 方法的主要作用是通过遍历视图解析器获取到视图,视图获取成功则停止遍历,返回视图。

//org.springframework.web.servlet.DispatcherServlet#resolveViewName
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception {//视图解析器为空判断 这里的视图解析器 就是 DispatcherServlet.properties 中的视图解析器  InternalResourceViewResolverif (this.viewResolvers != null) {//迭代遍历视图解析器Iterator var5 = this.viewResolvers.iterator();while(var5.hasNext()) {//获取视图解析器ViewResolver viewResolver = (ViewResolver)var5.next();//通过视图名称和语言环境 得到视图 重点关注View view = viewResolver.resolveViewName(viewName, locale);//为null判断if (view != null) {//返回视图return view;}}}return null;
}

AbstractCachingViewResolver#resolveViewName 方法源码分析

AbstractCachingViewResolver#resolveViewName 方法主要就是创建视图,它会先判断是否允许使用缓存,然后去创建视图或者说从缓存中去获取视图,如果允许使用缓存,最终会把创建好的视图加入到缓存中,我们重点关注 createView 即可。

@Nullable
public View resolveViewName(String viewName, Locale locale) throws Exception {//是否允许缓存if (!this.isCache()) {//不允许 直接根据 viewName 和语言环境 创建一个 View 重点关注return this.createView(viewName, locale);} else {//允许缓存//根据 viewName 和语言环境 从缓存中获取 viewObject cacheKey = this.getCacheKey(viewName, locale);View view = (View)this.viewAccessCache.get(cacheKey);//view 为 null 判断if (view == null) {//缓存没有获取到  同步锁 保证线程安全synchronized(this.viewCreationCache) {//再次去 viewCreationCache 缓存中获取view = (View)this.viewCreationCache.get(cacheKey);//view 为空判断if (view == null) {//还是为空 就创建一个 view  重点关注view = this.createView(viewName, locale);//为空判断  cacheUnresolved 默认为 trueif (view == null && this.cacheUnresolved) {//赋值为空视图 是一个没有任何实现的视图view = UNRESOLVED_VIEW;}//view 不为空 加入缓存if (view != null && this.cacheFilter.filter(view, viewName, locale)) {this.viewAccessCache.put(cacheKey, view);this.viewCreationCache.put(cacheKey, view);}}}} else if (this.logger.isTraceEnabled()) {this.logger.trace(formatKey(cacheKey) + "served from cache");}return view != UNRESOLVED_VIEW ? view : null;}
}

UrlBasedViewResolver#createView 方法源码分析

UrlBasedViewResolver#createView 方法会判断当前请求的类型,看是请求转发、重定向、普通请求的哪一种,不同请求类型会有不同的创建 View 的方法。

protected View createView(String viewName, Locale locale) throws Exception {//UrlBasedViewResolver 是否可以处理if (!this.canHandle(viewName, locale)) {return null;} else {//转发 urlString forwardUrl;//视图名称是否是否 redirect: 打头if (viewName.startsWith("redirect:")) {//截取掉 redirect: 获取真正的 urlforwardUrl = viewName.substring("redirect:".length());//创建一个重定向视图RedirectView view = new RedirectView(forwardUrl, this.isRedirectContextRelative(), this.isRedirectHttp10Compatible());//获取重定向 hostString[] hosts = this.getRedirectHosts();//为 null 判断if (hosts != null) {//给视图设置 view view.setHosts(hosts);}//应用给生命周期方法 重点关注return this.applyLifecycleMethods("redirect:", view);} else if (viewName.startsWith("forward:")) {//请求转发 获取转发 urlforwardUrl = viewName.substring("forward:".length());//创建 InternalResourceViewInternalResourceView view = new InternalResourceView(forwardUrl);//应用给生命周期方法 其实就是使用 ApplicationContext 完成 view 的初始化 重点关注return this.applyLifecycleMethods("forward:", view);} else {//不是 redirect  也不是 forward  就是普通视图 这里会调用 重点关注return super.createView(viewName, locale);}}
}

UrlBasedViewResolver#applyLifecycleMethods 方法源码分析

UrlBasedViewResolver#applyLifecycleMethods 应用给生命周期方法,其实就是使用 ApplicationContext 完成 View 的初始化 。

//org.springframework.web.servlet.view.UrlBasedViewResolver#applyLifecycleMethods
protected View applyLifecycleMethods(String viewName, AbstractUrlBasedView view) {//获取 ApplicationContextApplicationContext context = this.getApplicationContext();//为空 判断if (context != null) {//获取 beanFactory 完成 view 初始化Object initialized = context.getAutowireCapableBeanFactory().initializeBean(view, viewName);//初始化完了 是否是 view 类型if (initialized instanceof View) {//返回 viewreturn (View)initialized;}}return view;
}

AbstractCachingViewResolver#createView 方法源码分析

AbstractCachingViewResolver#createView 方法是普通请求创建 View 的方法,它调用的是父类 AbstractCachingViewResolver 的 createView 方法 ,该方法并没有什么实际操作,接着调用了 loadView 方法,loadView 方法完成了 View 的创建及初始化、属性见擦汗等。

//org.springframework.web.servlet.view.AbstractCachingViewResolver#createView
@Nullable
protected View createView(String viewName, Locale locale) throws Exception {return this.loadView(viewName, locale);
}//org.springframework.web.servlet.view.UrlBasedViewResolver#loadView 
protected View loadView(String viewName, Locale locale) throws Exception {//根据viewName 构建view  重点关注AbstractUrlBasedView view = this.buildView(viewName);//完成view 初始化View result = this.applyLifecycleMethods(viewName, view);//view.checkResource(locale) 检查view属性 默认返回 truereturn view.checkResource(locale) ? result : null;
}

AbstractCachingViewResolver#buildView 方法源码分析

AbstractCachingViewResolver#buildView 方法获取 View 的 Class 类型,反射创建了 View,并给 View 设置了各种属性,是正真创建 View 的方法。

//org.springframework.web.servlet.view.UrlBasedViewResolver#buildView	
protected AbstractUrlBasedView buildView(String viewName) throws Exception {//获取view 的classClass<?> viewClass = this.getViewClass();//断言判断 class 是否为 nullAssert.state(viewClass != null, "No view class");//根据 view 的 class 创建一个 viewAbstractUrlBasedView view = (AbstractUrlBasedView)BeanUtils.instantiateClass(viewClass);//根据前缀+名称+后缀 拼接成视图信息view.setUrl(this.getPrefix() + viewName + this.getSuffix());//view 设置 Attributeview.setAttributesMap(this.getAttributesMap());//获取 contentTypeString contentType = this.getContentType();if (contentType != null) {//view 设置 contentTypeview.setContentType(contentType);}//获取 RequestContextAttributeString requestContextAttribute = this.getRequestContextAttribute();if (requestContextAttribute != null) {//设置 RequestContextAttribute view.setRequestContextAttribute(requestContextAttribute);}//是否暴露 PathVariables  Request请求中的 url 属性Boolean exposePathVariables = this.getExposePathVariables();if (exposePathVariables != null) {//是否将这些属性暴露在视图中view.setExposePathVariables(exposePathVariables);}//是否将 bean 暴露在视图中Boolean exposeContextBeansAsAttributes = this.getExposeContextBeansAsAttributes();if (exposeContextBeansAsAttributes != null) {//设置到视图中view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);}//需要暴露的 bean nameString[] exposedContextBeanNames = this.getExposedContextBeanNames();if (exposedContextBeanNames != null) {//设置到视图中view.setExposedContextBeanNames(exposedContextBeanNames);}//返回 视图return view;
}

AbstractView#render 方法源码分析

上文说了 DispatcherServlet#render 方法主要可以分为两步,分别是创建视图 View 和 解析渲染视图 view.render,创建 View 的步骤我们已经分析完了,我们来分析一下 view.render 方法,也就是 AbstractView#render 方法,该方法会将 Model、request、response 封装成成一个 Map 对象给后面的视图渲染使用,同时会对下载请求做一些处理,然后就开始进行视图渲染。

//org.springframework.web.servlet.view.AbstractView#render
public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {if (this.logger.isDebugEnabled()) {this.logger.debug("View " + this.formatViewName() + ", model " + (model != null ? model : Collections.emptyMap()) + (this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));}//合并成一个 Map 对象Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response);//响应前处理 其实就是判断这是否是一个下载请求  默认不是下载请求this.prepareResponse(request, response);//渲染合并数据模型 重点关注this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response);
}

InternalResourceView#renderMergedOutputModel 方法源码分析

InternalResourceView#renderMergedOutputModel 方法就是进行视图渲染了,对于不同的请求类型有不同的渲染方式。

//org.springframework.web.servlet.view.InternalResourceView#renderMergedOutputModel
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {//将模型的数据全部写去到 request 中 就是一个 request.setAttribute 的过程this.exposeModelAsRequestAttributes(model, request);//暴露一个助手 默认空实现 可以理解为一个扩展点this.exposeHelpers(request);//确定请求的路径String dispatcherPath = this.prepareForRendering(request, response);//获取可以用于 include、forward  的 RequestDispatcherRequestDispatcher rd = this.getRequestDispatcher(request, dispatcherPath);//为 null 判断if (rd == null) {//为 null 抛出异常throw new ServletException("Could not get RequestDispatcher for [" + this.getUrl() + "]: Check that the corresponding file exists within your web application archive!");} else {//判断当前是否为include请求//include方法使原先的 Servlet 和转发到的 Servlet 都可以输出响应信息 即原先的 Servlet 还可以继续输出响应信息if (this.useInclude(request, response)) {//是 include 请求//设置 ContentTyperesponse.setContentType(this.getContentType());if (this.logger.isDebugEnabled()) {this.logger.debug("Including [" + this.getUrl() + "]");}//include 包含的意思// 调用 include()方法进行文件引入rd.include(request, response);} else {if (this.logger.isDebugEnabled()) {this.logger.debug("Forwarding to [" + this.getUrl() + "]");}//请求转发 直接使用 forward 请求将当前请求转发到目标文件路径中 渲染该视图rd.forward(request, response);}}
}

至此,视图渲染部分的核心流程已经分析完毕,其实整个流程就两个要点,一个是通过视图解析器去创建 View,一个就是 View 的渲染过程,视图渲染分析完毕,也代表着整个 Spring MVC 的工作流程分析完毕,源码告诉了我们 Spirng MVC 是怎样去处理一个请求的,希望可以帮助到有需要的小伙伴。

欢迎提出建议及对错误的地方指出纠正。

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

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

相关文章

提高篇(二):高级绘图技巧:在Processing中创造精美图形

提高篇(二):高级绘图技巧:在Processing中创造精美图形 引言 Processing不仅是一种编程语言,更是一个用于创意编程的强大工具。掌握高级绘图技巧,可以让你在艺术创作中如虎添翼。在本篇文章中,我们将探索贝塞尔曲线、样条曲线、自定义形状、图形变换等高级绘图技术,帮助…

tkinter+火山引擎+python实现语音识别聊天机器人

想要做一款能通过语音识别来聊天的智能机器人,首先需要能通过麦克风录制语音进行识别转换成文字,将文字发送给机器人得到聊天结果,并能将返回的文字转换成语音进行合成,之后再通过本地播放语音实现语音交互。 架构: 实现步骤 一、本地录音 本地录音可以通过pyAudio库实…

2024-06-05-记一次cnvd渗透

前言&#xff1a;挖src挖郁闷了&#xff0c;闲来无事选择挖一个cnvd来练练手&#xff0c;本次的漏洞都没啥难度&#xff0c;企查查资产过了5000万 说一下cnvd证书的下放标准 对于中危及中危以上通用型漏洞&#xff08;CVSS2.0基准评分超过4.0分&#xff09;&#xff0c;以及涉…

Scaling vision Transformer 论文理解

Scaling vision Transformer 论文理解 1. 摘要2. 一些主要结论小结2.1 few shot transfer learning2.2 Pareto-front3. 讨论3.1 Limitations3.2 社会作用4. 文章结论参考资料1. 摘要 Attention-based neural networks such as the Vision Transformer (ViT) have recently att…

valgrind-内存泄漏定位工具

1、前言 valgirnd 是一套开放源代码的动态调试工具集合。能够检测内存管理错误&#xff0c;线程BUG等。valgirnd是由内核以及基于内核的其他调试工具组成。内核类似于一个框架&#xff0c;它模拟了一个cpu的环境&#xff0c;并提供服务给其他工具使用。而其他工具则类似于插件…

红酒:红酒保存中的光照与避免阳光直射

在红酒保存中&#xff0c;光照是一个常常被忽视的因素。光照对红酒的影响是不可小觑的&#xff0c;因为阳光中的紫外线会加速红酒的氧化&#xff0c;导致其口感和品质的下降。因此&#xff0c;在保存云仓酒庄雷盛红酒时&#xff0c;应特别注意避免阳光直射。 阳光直射对红酒的影…

企业代码签名证书1300元

随着手机和电脑等设备的普及&#xff0c;越来越多的开发者进入软件行业&#xff0c;为了软件的安全性、完整性和可信度&#xff0c;开发者往往会使用由正规CA认证机构颁发的代码签名证书对软件代码进行数字签名&#xff0c;来标识软件的来源和软件开发者的真实身份。今天就随SS…

AGI时代下,计算机专业出身的程序员该何去何从?

Easy&#xff1a;单独开个贴说一下吧。 如果你计算机、软件工程、通信、电子、网络等相关专业本科毕业&#xff0c;也考虑前后端/测试岗&#xff0c;戳 技术大厂&#xff0c;6险1金&#xff0c;待遇不错。 提前说明&#xff0c;一切以古代思维对AGI时代的推演&#xff0c;都是可…

博物馆文物库房管理软件

博物馆作为文化遗产的守护者和传承者&#xff0c;承载着人类智慧与文明的结晶。在博物馆的背后&#xff0c;一个庞大而严密的管理系统支撑着文物的保护与展示。而其中&#xff0c;文物库房管理软件的使用&#xff0c;无疑是一项重要的管理工具。 文物库房管理软件的功能具有多样…

【CentOS 7】挑战探索:在CentOS 7上实现Python 3.9的完美部署指南

【CentOS 7】挑战探索&#xff1a;在CentOS 7上实现Python 3.9的完美部署指南 大家好 我是寸铁&#x1f44a; 总结了一篇【CentOS 7】挑战探索&#xff1a;在CentOS 7上实现Python 3.9的完美部署指南详细步骤✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言 此篇教程只适用于p…

【java前端课堂】05_类的多态

类的多态是面向对象编程的三大特性之一&#xff0c;与封装和继承并列。多态性允许我们将父对象设置为与其子对象相等的技术&#xff0c;让我们可以把一个子对象当作父对象对待。在Java中&#xff0c;多态主要体现在以下三个方面&#xff1a; 方法的重载&#xff08;Overloadin…

【Mybatis】源码分析-高级应用

1、Mybatis配置文件深入理解 1.2、动态SQL语句 Mybatis 的映射⽂件中&#xff0c;前⾯我们的 SQL 都是⽐较简单的&#xff0c;有些时候业务逻辑复杂时&#xff0c;我们的 SQL是动态变化的&#xff0c;此时在前⾯的学习中我们的 SQL 就不能满⾜要求了。 1.2.1、条件判断 我们根…

技巧:合并ZIP分卷压缩包

如果ZIP压缩文件文件体积过大&#xff0c;大家可能会选择“分卷压缩”来压缩ZIP文件&#xff0c;那么&#xff0c;如何合并zip分卷压缩包呢&#xff1f;今天我们分享两个ZIP分卷压缩包合并的方法给大家。 方法一&#xff1a; 我们可以将分卷压缩包&#xff0c;通过解压的方式…

E10:系统弹窗提示

效果– window.WeFormSDK.showMessage("这是一个E10的提示", 3, 2); const onClickCreate () > console.log("create"); const onClickSave () > console.log("save"); const onClickCancel () > dialogComponent?.destroy(); co…

Java四舍五入保留小数

这里介绍两种方法&#xff1a; package Book.jj.hh;import java.text.DecimalFormat; //使用DecimalFormat类 public class Demo1 {public static void main(String[] args) {double num 123.52631;DecimalFormat a new DecimalFormat("#.00"); //小数点后有几个0…

SpringCloud Gateway基础入门与使用实践总结

官网文档&#xff1a;点击查看官网文档 Cloud全家桶中有个很重要的组件就是网关&#xff0c;在1.x版本中都是采用的Zuul网关。但在2.x版本中&#xff0c;zuul的升级一直跳票&#xff0c;SpringCloud最后自己研发了一个网关替代Zuul&#xff0c;那就是SpringCloud Gateway一句话…

抖音账号永久封号后强制注销释放实名!一分钟教程方法公开

目前方法是可行的&#xff0c;不知道能保持多久&#xff01; 下载旧版本抖音&#xff1a;下载抖音6.8版本或5.8版本的老版本应用。 使用封禁手机号登录&#xff1a;使用已被永久封禁的手机号登录旧版本的抖音应用。 账号注销操作&#xff1a; 在设置中找到账号与安全的选项。…

从零开始发布你的第一个npm插件包并在多项目中使用

引言 在开源的世界里&#xff0c;每个人都有机会成为贡献者&#xff0c;甚至是创新的引领者。您是否有过这样的想法&#xff1a;开发一个解决特定问题的小工具&#xff0c;让她成为其他开发者手中的利器&#xff1f;今天&#xff0c;我们就来一场实战训练&#xff0c;学习如何将…

【ubuntu】增加samba服务和文件夹

发现ai -server的ubuntu机器无法git clone 下来github的文件所以 使用samba 连接到linux的文件夹proj然后在我的windows上git clone 即可。安装samba Creating config file /etc/samba/smb.conf with new version Setting up libcephfs2 (17.2.7-0ubuntu0.22.04.1) ... Setting…

漏洞挖掘 | 验证码绕过

还是老规矩&#xff0c;开局一个登录框&#xff0c;中途漏洞全靠舔&#xff0c;先来研究一下这个登录窗口 很好&#xff0c;发现有验证码登录&#xff0c;先测试测试能不能并发 看来没有&#xff0c;只成功发送了两条&#xff0c;再看看验证码是不是4位 很好&#xff0c;是4位。…