<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    BinCat V4 - 實現PHP文件解析

    BinCat V4 - 實現PHP文件解析

    Quercus-QuercusServlet

    Quercus是一個Resin實現的解析并運行php文件的jar庫,其本質是使用QuercusServlet處理所有訪問.php的文件請求,Quercus會將php文件翻譯成java class文件并在JVM中執行。

    添加Quercus依賴:

    <dependency>
      <groupId>com.caucho</groupId>
      <artifactId>quercus</artifactId>
      <version>4.0.63</version>
    </dependency>

    然后創建一個QuercusServlet映射,因為BinCat只支持注解,所以無法在QuercusServlet類上添加@WebServlet注解,但是我們可以寫一個類去繼承QuercusServlet從而間接的完成Servlet聲明。

    QuercusPHPServlet示例:

    package com.anbai.sec.server.test.servlet;
    
    import com.caucho.quercus.servlet.QuercusServlet;
    
    import javax.servlet.annotation.WebServlet;
    
    @WebServlet(name = "QuercusPHPServlet", urlPatterns = ".*\\.php$")
    public class QuercusPHPServlet extends QuercusServlet {
    
    
    }

    BinCatConfig示例代碼(方便統一的Servlet注冊):

    /**
        * 手動注冊Servlet并創建BinCatServletContext對象
      *
        * @param appClassLoader 應用的類加載器
        * @return ServletContext Servlet上下文對象
        */
    public static BinCatServletContext createServletContext(BinCatWebAppClassLoader appClassLoader) throws Exception {
        BinCatServletContext servletContext = new BinCatServletContext(appClassLoader);
    
        // 手動注冊Servlet類
        Class<Servlet>[] servletClass = new Class[]{
          TestServlet.class,
          CMDServlet.class,
          QuercusPHPServlet.class
            };
    
        for (Class<Servlet> clazz : servletClass) {
          Servlet    servlet    = clazz.newInstance();
          WebServlet webServlet = clazz.getAnnotation(WebServlet.class);
    
          if (webServlet != null) {
            // 獲取WebInitParam配置
            WebInitParam[] webInitParam = webServlet.initParams();
    
            // 動態創建Servlet對象
            ServletRegistration.Dynamic dynamic = servletContext.addServlet(webServlet.name(), servlet);
    
            // 動態設置Servlet映射地址
            dynamic.addMapping(webServlet.urlPatterns());
    
            // 設置Servlet啟動參數
            for (WebInitParam initParam : webInitParam) {
              dynamic.setInitParameter(initParam.name(), initParam.value());
            }
          }
        }
    
        // 創建ServletContext
        return servletContext;
    }

    因為QuercusServlet創建時需要必須有ServletContext對象,所以我們必須實現ServletContext接口。除此之外,Servlet創建時還需要調用Servlet的初始化方法(public void init(ServletConfig config) throws ServletException)。調用init的時候還需要實現ServletConfig接口。

    初始化Servlet代碼片段:

    /**
        * 初始化Servlet
        *
        * @param servletContext Servlet上下文
        * @throws ServletException Servlet處理異常
        */
    public static void initServlet(BinCatServletContext servletContext) throws ServletException {
      Set<BinCatServletRegistrationDynamic> dynamics = servletContext.getRegistrationDynamics();
    
      for (BinCatServletRegistrationDynamic dynamic : dynamics) {
        Servlet             servlet          = dynamic.getServlet();
        String              servletName      = dynamic.getServletName();
        Map<String, String> initParameterMap = dynamic.getInitParameters();
    
        servlet.init(new BinCatServletConfig(servletContext, servletName, initParameterMap));
      }
    }

    BinCatServletContext實現

    Servlet容器啟動的時候必須創建一個ServletContext(Servlet上下文),用于管理容器中的所有Servlet對象。在創建BinCatServletContext的時候需要創建并初始化所有的Servlet并存儲到servletMap中。

    BinCatServletContext代碼片段:

    package com.anbai.sec.server.servlet;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.descriptor.JspConfigDescriptor;
    import java.io.File;
    import java.io.InputStream;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.*;
    import java.util.concurrent.ConcurrentHashMap;
    
    public class BinCatServletContext implements ServletContext {
    
        // 創建一個裝動態注冊的Servlet的Map
        private final Map<String, Servlet> servletMap = new HashMap<>();
    
        // 創建一個裝ServletContext初始化參數的Map
        private final Map<String, String> initParameterMap = new HashMap<>();
    
        // 創建一個裝ServletContext屬性對象的Map
        private final Map<String, Object> attributeMap = new HashMap<>();
    
        // 創建一個裝Servlet動態注冊的Set
        private final Set<BinCatServletRegistrationDynamic> registrationDynamics = new LinkedHashSet<>();
    
        // BinCatWebAppClassLoader,Web應用的類加載器
        private final BinCatWebAppClassLoader appClassLoader;
    
        public BinCatServletContext(BinCatWebAppClassLoader appClassLoader) throws Exception {
            this.appClassLoader = appClassLoader;
        }
    
        // 此處省略ServletContext接口中的大部分方法,僅保留幾個示例方法...
    
        @Override
        public Servlet getServlet(String name) throws ServletException {
            return servletMap.get(name);
        }
    
        @Override
        public Enumeration<Servlet> getServlets() {
            Set<Servlet> servlets = new HashSet<Servlet>();
            servlets.addAll(servletMap.values());
    
            return Collections.enumeration(servlets);
        }
    
        @Override
        public Enumeration<String> getServletNames() {
            Set<String> servlets = new HashSet<String>();
            servlets.addAll(servletMap.keySet());
    
            return Collections.enumeration(servlets);
        }
    
    }

    BinCatServletConfig實現

    在創建BinCatServletContext時我們指定了一個ServletConfig實現:BinCatServletConfigServletConfig用于指定Servlet啟動時的配置信息。

    BinCatServletConfig實現:

    package com.anbai.sec.server.servlet;
    
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletContext;
    import javax.servlet.annotation.WebInitParam;
    import javax.servlet.annotation.WebServlet;
    import java.util.Collections;
    import java.util.Enumeration;
    import java.util.HashSet;
    import java.util.Set;
    
    public class BinCatServletConfig implements ServletConfig {
    
       private final BinCatServletContext servletContext;
    
       private final WebServlet webServlet;
    
       private final WebInitParam[] webInitParam;
    
       public BinCatServletConfig(BinCatServletContext servletContext, WebServlet webServlet) {
          this.servletContext = servletContext;
          this.webServlet = webServlet;
          this.webInitParam = webServlet.initParams();
       }
    
       @Override
       public String getServletName() {
          return webServlet.name();
       }
    
       @Override
       public ServletContext getServletContext() {
          return this.servletContext;
       }
    
       @Override
       public String getInitParameter(String name) {
          for (WebInitParam initParam : webInitParam) {
             String paramName = initParam.name();
    
             if (paramName.equals(name)) {
                return initParam.value();
             }
          }
    
          return null;
       }
    
       @Override
       public Enumeration<String> getInitParameterNames() {
          Set<String> initParamSet = new HashSet<String>();
    
          for (WebInitParam initParam : webInitParam) {
             initParamSet.add(initParam.name());
          }
    
          return Collections.enumeration(initParamSet);
       }
    
    }

    BinCatDispatcherServlet實現

    為了方便后續的BinCat版本處理Http請求和響應處理結果,我們簡單的封裝了BinCatDispatcherServletBinCatResponseHandler對象。BinCatDispatcherServlet會根據瀏覽器請求的不同URL地址去調用對應的Servlet服務,除此之外還提供了一個簡單的靜態資源文件處理邏輯和PHP解析功能。

    BinCatDispatcherServlet實現代碼:

    package com.anbai.sec.server.handler;
    
    import com.anbai.sec.server.servlet.BinCatRequest;
    import com.anbai.sec.server.servlet.BinCatResponse;
    import com.anbai.sec.server.servlet.BinCatServletContext;
    import com.anbai.sec.server.servlet.BinCatServletRegistrationDynamic;
    import org.javaweb.utils.FileUtils;
    import org.javaweb.utils.StringUtils;
    
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.IOException;
    import java.nio.file.Files;
    import java.util.Collection;
    import java.util.Set;
    import java.util.regex.Pattern;
    
    public class BinCatDispatcherServlet {
    
        public void doDispatch(BinCatRequest req, BinCatResponse resp, ByteArrayOutputStream out) throws IOException {
            // 請求URI地址
            String uri = req.getRequestURI();
    
            // 獲取ServletContext
            BinCatServletContext servletContext = (BinCatServletContext) req.getServletContext();
    
            // 獲取Http請求的文件
            File requestFile = new File(req.getRealPath(uri));
    
            // 處理Http請求的靜態文件,如果文件存在(.php后綴除外)就直接返回文件內容,不需要調用Servlet
            if (requestFile.exists() && requestFile.isFile() && !uri.endsWith(".php")) {
                // 修改狀態碼
                resp.setStatus(200, "OK");
    
                // 解析文件的MimeType
                String mimeType = Files.probeContentType(requestFile.toPath());
    
                if (mimeType == null) {
                    String fileSuffix = FileUtils.getFileSuffix(requestFile.getName());
                    resp.setContentType("text/" + fileSuffix);
                } else {
                    resp.setContentType(mimeType);
                }
    
                out.write(Files.readAllBytes(requestFile.toPath()));
            } else {
                // 遍歷所有已注冊得Servlet,處理Http請求
                Set<BinCatServletRegistrationDynamic> dynamics = servletContext.getRegistrationDynamics();
                for (BinCatServletRegistrationDynamic dynamic : dynamics) {
                    Collection<String> urlPatterns = dynamic.getMappings();
    
                    for (String urlPattern : urlPatterns) {
                        try {
                            // 檢測請求的URL地址和Servlet的地址是否匹配
                            if (Pattern.compile(urlPattern).matcher(uri).find()) {
                                // 修改狀態碼
                                resp.setStatus(200, "OK");
    
                                // 調用Servlet請求處理方法
                                dynamic.getServlet().service(req, resp);
                                return;
                            }
                        } catch (Exception e) {
                            // 修改狀態碼,輸出服務器異常信息到瀏覽器
                            resp.setStatus(500, "Internal Server Error");
                            e.printStackTrace();
    
                            out.write(("<pre>" + StringUtils.exceptionToString(e) + "</pre>").getBytes());
                        }
                    }
                }
            }
        }
    
    }

    BinCatResponseHandler實現

    BinCatResponseHandler只是一個簡單封裝的用于向瀏覽器輸出Http處理請求結果的對象。

    BinCatResponseHandler實現代碼:

    package com.anbai.sec.server.handler;
    
    import com.anbai.sec.server.servlet.BinCatResponse;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.Map;
    
    public class BinCatResponseHandler {
    
       public void processResult(BinCatResponse response, Map<String, String> responseHeader, String serverName,
                                 OutputStream out, ByteArrayOutputStream baos) throws IOException {
    
          // 處理Http響應內容
          out.write(("HTTP/1.1 " + response.getStatus() + " " + response.getMessage() + "\n").getBytes());
          // 輸出Web服務器信息
          out.write(("Server: " + serverName + "\n").getBytes());
          // 輸出返回的消息類型
          out.write(("Content-Type: " + response.getContentType() + "\n").getBytes());
          // 輸出返回字節數
          out.write(("Content-Length: " + baos.size() + "\n").getBytes());
    
          // 輸出用戶自定義的Header
          for (String key : responseHeader.keySet()) {
             out.write((key + ": " + responseHeader.get(key) + "\n").getBytes());
          }
    
          // 寫入換行
          out.write("\n".getBytes());
          // 將讀取到的數據寫入到客戶端Socket
          out.write(baos.toByteArray());
       }
    
    }

    BinCat V4實現

    V4V3的基礎上實現了ServletConfigServletContext接口,從而實現了Servlet實例化初始化BinCatDispatcherServlet實現的Servlet服務調用。

    BinCatServerV4實現代碼:

    package com.anbai.sec.server;
    
    import com.anbai.sec.server.config.BinCatConfig;
    import com.anbai.sec.server.handler.BinCatDispatcherServlet;
    import com.anbai.sec.server.handler.BinCatResponseHandler;
    import com.anbai.sec.server.servlet.BinCatRequest;
    import com.anbai.sec.server.servlet.BinCatResponse;
    import com.anbai.sec.server.servlet.BinCatServletContext;
    
    import java.io.ByteArrayOutputStream;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.logging.Logger;
    
    /**
     * ServerSocket Http 服務器示例
     */
    public class BinCatServerV4 {
    
        // 設置服務監聽端口
        private static final int PORT = 8080;
    
        // 設置服務名稱
        private static final String SERVER_NAME = "BinCat-0.0.4";
    
        private static final Logger LOG = Logger.getLogger("info");
    
        public static void main(String[] args) {
            try {
                // 創建ServerSocket,監聽本地端口
                ServerSocket ss = new ServerSocket(PORT);
    
                // 創建BinCatServletContext對象
                BinCatServletContext servletContext = BinCatConfig.createServletContext();
    
                // 初始化Servlet
                BinCatConfig.initServlet(servletContext);
    
                LOG.info(SERVER_NAME + " 啟動成功,監聽端口: " + PORT);
    
                while (true) {
                    // 等待客戶端連接
                    Socket socket = ss.accept();
    
                    try {
                        // 獲取Socket輸入流對象
                        InputStream in = socket.getInputStream();
    
                        // 獲取Socket輸出流對象
                        OutputStream out = socket.getOutputStream();
    
                        // 創建BinCat請求處理對象
                        BinCatRequest request = new BinCatRequest(socket, servletContext);
    
                        // 創建BinCat請求處理結果輸出流
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
    
                        // 創建BinCat請求處理結果Header對象
                        Map<String, String> responseHeader = new ConcurrentHashMap<String, String>();
    
                        // 創建BinCat響應處理對象
                        BinCatResponse response = new BinCatResponse(socket, responseHeader, baos);
    
                        // 創建BinCatDispatcherServlet對象,用于分發Http請求
                        BinCatDispatcherServlet dispatcherServlet = new BinCatDispatcherServlet();
    
                        // 創建BinCatResponseHandler對象,用于處理Http請求結果
                        BinCatResponseHandler responseHandler = new BinCatResponseHandler();
    
                        // 使用BinCatDispatcherServlet處理Servlet請求
                        dispatcherServlet.doDispatch(request, response, baos);
    
                        // 響應服務器處理結果
                        responseHandler.processResult(response, responseHeader, SERVER_NAME, out, baos);
    
                        in.close();
                        out.close();
                    } catch (Exception e) {
                        LOG.info("處理客戶端請求異常:" + e);
                    } finally {
                        socket.close();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    }

    BinCat PHP解析測試

    我們需要在javaweb-sec項目根目錄創建一個測試文件,如info.php:

    <?php phpinfo();?>

    image-20200911163413630

    啟動BinCat V4后訪問http://localhost:8080/info.php:

    image-20200911150900145

    復制一個最新版本的Discuzjavaweb-sec目錄,嘗試安裝Discuz,訪問:http://localhost:8080/discuz/install/index.php

    image-20200911150712040

    Discuz環境檢測正常:

    image-20200911150754241

    測試BinCatPHP解析功能正常,只是開始安裝Discuz時無法下一步,無異常和錯誤卡了,無法完成安裝。

    本文章首發在 網安wangan.com 網站上。

    上一篇 下一篇
    討論數量: 0
    只看當前版本


    暫無話題~
    亚洲 欧美 自拍 唯美 另类