前言

近期要使用JavaFX开发exp小工具,这里记录下学习过程。

安装

我的环境是Mac+java1.8+idea
1、创建javaFX项目
首先file->newProject->javaFX 然后一直默认即可,这里注意,最新版的javaFX使用的是11版本,我们先在idea上选择jdk为11,后续编译的时候换回来即可。
EE5CC471EDC942708345E009EF3358BC.png
2、安装SceneBuilder
https://www.oracle.com/java/technologies/javafxscenebuilder-1x-archive-downloads.html
选择自己想要安装的版本,默认安装即可。
然后在idea的Preference中在javaFX中添加路径即可。

编译

1、先将编译版本改为8,详细如下
https://zhuanlan.zhihu.com/p/348660719
2、该pom中的编译项如下

    <build>
        <plugins>


            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <archive>
                        <!--自动添加META-INF/MANIFEST.MF -->
                        <manifest>
                            <addClasspath>true</addClasspath>
                        </manifest>
<!--                        添加运行的主类-->
                        <manifestEntries>
                            <Main-Class>com.lvyyevd.hwscan.MainApplication</Main-Class>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
<!--                     修改编译版本-->
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.7</version>
                <executions>
                    <execution>
                        <!-- Default configuration for running with: mvn clean javafx:run -->
<!--                        <id>default-cli</id>-->
                        <configuration>
                            <mainClass>com.lvyyevd.hwscan.MainApplication</mainClass>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

然后mvn clean install就可以了

Demo

首先创建一个入口类
我的如下:
public class MainApplication extends Application {

    private Stage primaryStage;
    private BorderPane rootLayout;

    @Override
    public void start(Stage primaryStage) {
        this.primaryStage = primaryStage;
        this.primaryStage.setTitle("AddressApp");

        initRootLayout();

//        showPersonOverview();
    }

    /**
     * Initializes the root layout.
     */
    public void initRootLayout() {
        try {
            // Load root layout from fxml file.
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(MainApplication.class.getResource("view/main/RootLayout.fxml"));
            rootLayout = (BorderPane) loader.load();
            // Show the scene containing the root layout.
            Scene scene = new Scene(rootLayout);
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

模仿自带的即可,view/main/RootLayout.fxml为你自己编写的fxml文件。

选择漏洞并动态加载UI

布局
可以参考下面
https://blog.csdn.net/Dream_Weave/article/details/119642184

1、我需要创建一个下拉菜单,然后给他初始化一个值,
在相应的controller下用@FXML 标记一个私有变量:

 @FXML
    private ComboBox vlunComboBox2;

然后在sceneBuilder中联系起来,如下:
C105D435BCA04626A6878A5D71C1AC47.png
然后在controller中 编写初始化代码

@FXML
private  void initialize() {
    ObservableList<String> observableList = FXCollections.observableArrayList("111", "222", "333");
    vlunComboBox2.setItems(observableList);
}

4298375E1AD441D8A47E99247E1AEDF5.png
效果如下:
24C898BA0497473F8E615FC6BABD3035.png
2、如何监听选择框里面的变化,可以使用监听器
编辑如下

    private  void initialize() {
        ObservableList<String> observableList = FXCollections.observableArrayList("All", "fanwei-8-sql");
        vlunComboBox1.setItems(observableList);
        vlunComboBox1.getSelectionModel().select(0);
        ObservableList<String> observableList2 = FXCollections.observableArrayList("fanwei-8-sql");
        vlunComboBox2.setItems(observableList2);


//        设置下拉框的监听器
        vlunComboBox2.getSelectionModel().selectedItemProperty().addListener(
                new ChangeListener<String>() {
                    @Override
                    public void changed(ObservableValue<? extends String> arg0, String old_str, String new_str) {
                        // getSelectedIndex方法可获得选中项的序号,getSelectedItem方法可获得选中项的对象
                                System.out.println(vlunComboBox2.getSelectionModel().getSelectedItem().toString());
                    }
                }
        );
}

3、如何动态的加载其他的fxml文件,可以如下:

vulnScene.getChildren().clear();
                               FXMLLoader loader = new FXMLLoader();
                               loader.setLocation(MainApplication.class.getResource("view/main/vuln/fanwei/fanwei_v9_fileupload.fxml"));
                               try {
                                   vulnScene.getChildren().setAll((TabPane)loader.load());
                               } catch (IOException e) {
                                   e.printStackTrace();
                               }

将所需的vulnScene 中的子节点 加载进所需的fxml文件。
效果如下:
19F0FE98634D4A6DA9798E7A8BB79CD4.png

我们就可以根据选择的漏洞动态修改右边的展示界面了。

代理的实现

1、 点击添加代理弹出一个窗口
这个比较简单,在menu中的MenuItem中添加action
1CC3952D85C64C74A973AAB66690F8A3.png
action如下:

 @FXML
    private void proxySeting() throws IOException {
        Stage stage = new Stage();
        Scene scene = new Scene((Parent) FXMLLoader.load(MainApplication.class.getResource("view/main/proxySetting.fxml")));
        stage.setScene(scene);
        stage.show();
    }

效果如下:
2C95D5C315804232824DECEDB7677055.png
2、设置单选框与几个输入框,
这两个启用禁用按钮的属性是RadioButton,从sceneBuilder中拖出来就可以了。要设置成互斥属性可以如下配置:
分别将启用和禁用的RadioButton的fixId设置成enableRadioButton与disableRadioButton,然后如下:

    @FXML
    private  void initialize() {
        ToggleGroup tg = new ToggleGroup();
        enableRadioButton.setToggleGroup(tg);
        disableRadioButton.setToggleGroup(tg);
      //设置disableRadioButton默认为选择。
        disableRadioButton.setSelected(true);
    }

便实现了上图的效果。
输入框的属性是TextField,直接拖拽,再在左边加上label显示即可。

3、设置取消与保存按钮
取消按钮就是关闭当前窗口,可以如下设置action:

@FXML
    private void cancelAction(){
        Stage stage = (Stage)cancelButton.getScene().getWindow();
        stage.close();
    }

保存就是将当前信息保存下来,这里我设置了一个静态类,好让全局访问。

public class httpProxy {
    public static Boolean enabel = false;
    public static String ip = "127.0.0.1";
    public static Integer port = 8080;
    public static String username = "";
    public static String password = "";

    public static void proxySetting(Boolean enabel,String ip,Integer port,String username,String password){
        httpProxy.enabel = enabel;
        httpProxy.ip = ip;
        httpProxy.port = port;
        httpProxy.username = username;
        httpProxy.password = password;
    }
}

接下来就是实现代理了,
我这里使用的是httpok,pom里面添加

<dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.9.3</version>
        </dependency>

写了一个简单的demo如下

    public static String get(String url) throws IOException {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url(url).build();
        Response response = client.newCall(request).execute();
        if (response.isSuccessful()) {
            return response.body().string();
        } else {
            throw new IOException("Unexpected code " + response);
      }

可以实现httpget请求,但这肯定是不满足要求的,我们需要添加代理,以及可以添加header参数等等要求,httpok是可以添加Interceptor的,这个可以让我们在发请求的时候在里面添加参数,在网上搜了一个挺好用的类,

package com.lvyyevd.hwscan.util.httpInterceptor;

import okhttp3.*;
import okio.Buffer;

import java.io.File;
import java.io.IOException;
import java.util.*;

public class BasicParamsInterceptor implements Interceptor {

    Map<String, String> queryParamsMap = new HashMap<>(); // 添加到 URL 末尾,Get Post 方法都使用
    Map<String, String> paramsMap = new HashMap<>(); // 添加到公共参数到消息体,适用 Post 请求
    Map<String, String> headerParamsMap = new HashMap<>(); // 


    // 私有构造器
    public BasicParamsInterceptor() {}

    @Override
    public Response intercept(Chain chain) throws IOException {

        Request request = chain.request();
        Request.Builder requestBuilder = request.newBuilder();
//
//        // process header params inject
//        Headers.Builder headerBuilder = request.headers().newBuilder();
        // 以 Entry 添加消息头
        if (headerParamsMap.size() > 0) {
            Iterator iterator = headerParamsMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry entry = (Map.Entry) iterator.next();
                requestBuilder.addHeader((String) entry.getKey(), (String) entry.getValue());
            }
        }

        // process queryParams inject whatever it's GET or POST
        if (queryParamsMap.size() > 0) {
            request = injectParamsIntoUrl(request.url().newBuilder(), requestBuilder, queryParamsMap);
        }

        // process post body inject
        if (paramsMap.size() > 0) {
            if (canInjectIntoBody(request)) {
                FormBody.Builder formBodyBuilder = new FormBody.Builder();
                for(Map.Entry<String, String> entry : paramsMap.entrySet()) {
                    formBodyBuilder.add((String) entry.getKey(), (String) entry.getValue());
                }

                RequestBody formBody = formBodyBuilder.build();
                String postBodyString = bodyToString(request.body());
                postBodyString += ((postBodyString.length() > 0) ? "&" : "") +  bodyToString(formBody);
                requestBuilder.post(RequestBody.create(MediaType.parse("application/x-www-form-urlencoded;charset=UTF-8"), postBodyString));
            }
        }

//        if(fileParamsMap.size()>0 || file !=null ){
//
//        }

        request = requestBuilder.build();
        return chain.proceed(request);
    }

    /**
     *  确认是否是 post 请求
     * @param request 发出的请求
     * @return true 需要注入公共参数
     */
    private boolean canInjectIntoBody(Request request) {
        if (request == null) {
            return false;
        }
        if (!request.method().equals("POST")) {
            return false;
        }
        RequestBody body = request.body();
        if (body == null) {
            return false;
        }
        MediaType mediaType = body.contentType();
        if (mediaType == null) {
            return false;
        }
        if (!mediaType.subtype().equals("x-www-form-urlencoded")) {
            return false;
        }
        return true;
    }

    // func to inject params into url
    private Request injectParamsIntoUrl(HttpUrl.Builder httpUrlBuilder, Request.Builder requestBuilder, Map<String, String> paramsMap) {
        if (paramsMap.size() > 0) {
            Iterator iterator = paramsMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry entry = (Map.Entry) iterator.next();
                httpUrlBuilder.addQueryParameter((String) entry.getKey(), (String) entry.getValue());
            }
            requestBuilder.url(httpUrlBuilder.build());
            return requestBuilder.build();
        }
        return null;
    }

    private static String bodyToString(final RequestBody request){
        try {
            final RequestBody copy = request;
            final Buffer buffer = new Buffer();
            if(copy != null)
                copy.writeTo(buffer);
            else
                return "";
            return buffer.readUtf8();
        }
        catch (final IOException e) {
            return "did not work";
        }
    }

    public static class Builder {

        BasicParamsInterceptor interceptor;

        public Builder() {
            interceptor = new BasicParamsInterceptor();
        }

        // 添加公共参数到 post 消息体
        public Builder addParam(String key, String value) {
            interceptor.paramsMap.put(key, value);
            return this;
        }

        // 添加公共参数到 post 消息体
        public Builder addParamsMap(Map<String, String> paramsMap) {
            interceptor.paramsMap.putAll(paramsMap);
            return this;
        }

        // 添加公共参数到消息头
        public Builder addHeaderParam(String key, String value) {
            interceptor.headerParamsMap.put(key, value);
            return this;
        }

        // 添加公共参数到消息头
        public Builder addHeaderParamsMap(Map<String, String> headerParamsMap) {
            interceptor.headerParamsMap.putAll(headerParamsMap);
            return this;
        }


        // 添加公共参数到 URL
        public Builder addQueryParam(String key, String value) {
            interceptor.queryParamsMap.put(key, value);
            return this;
        }

        // 添加公共参数到 URL
        public Builder addQueryParamsMap(Map<String, String> queryParamsMap) {
            interceptor.queryParamsMap.putAll(queryParamsMap);
            return this;
        }

        public BasicParamsInterceptor build() {
            return interceptor;
        }

    }
}

使用的时候初始化一下Builder,然后调用一下Builder的相应方法添加参数,再调用Builder的builder方法就返回一个interceptor了,具体想要实现的可以所需添加。

这里封装了一个get请求方式作为参考,

public class httpRequest {

    private X509TrustManager x509TrustManager() {
        final X509TrustManager x509TrustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            }


            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[]{};
            }
        };
        return x509TrustManager;
    }

    private SSLSocketFactory sslSocketFactory() {
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[] {x509TrustManager()}, new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        return null;
    }

    public Response getHttpRequest(String url,  Map<String, String> headPargram) throws IOException {
        OkHttpClient client;
      //设置interceptor
        BasicParamsInterceptor interceptor = null;
        BasicParamsInterceptor.Builder builder = new BasicParamsInterceptor.Builder();
        //添加参数
        Map header = new HashMap<String,String>();

        header.put("User-Agent","Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:100.0) Gecko/20100101 Firefox/100.0");
        builder.addHeaderParamsMap(header);
        if(!CollectionUtils.isEmpty((Collection<?>) headPargram)){
            builder.addHeaderParamsMap(headPargram);
        }

        //代理和非代理走不同的路

        if(httpProxy.enabel) {

            Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(httpProxy.ip, httpProxy.port));

            Authenticator proxyAuthenticator = new Authenticator() {
                @Override
                public Request authenticate(Route route, Response response) throws IOException {
                    String credential = Credentials.basic(httpProxy.username, httpProxy.password);
                    return response.request().newBuilder()
                            .header("Proxy-Authorization", credential)
                            .build();
                }
            };
//            Map headermap =  new HashMap<String,String>();
//            headermap.put("Proxy-Connection","Keep-Alive");

            interceptor = builder.build();
            client = new OkHttpClient.Builder()
                    .addInterceptor(interceptor)
                    .retryOnConnectionFailure(false)//是否开启缓存
//                .connectionPool(pool())//连接池
                    .connectTimeout(5L, TimeUnit.SECONDS)
                    .readTimeout(5L, TimeUnit.SECONDS)
                    .sslSocketFactory(sslSocketFactory(), x509TrustManager())
                    .proxy(proxy)
                    .proxyAuthenticator(proxyAuthenticator)
                    .authenticator(proxyAuthenticator)
                    .build();
        }else {
            interceptor = builder.build();
            client = new OkHttpClient.Builder()
                    .addInterceptor(interceptor)
                    .retryOnConnectionFailure(false)//是否开启缓存
//                .connectionPool(pool())//连接池
                    .connectTimeout(5L, TimeUnit.SECONDS)
                    .readTimeout(5L, TimeUnit.SECONDS)
                    .sslSocketFactory(sslSocketFactory(), x509TrustManager())
                    .build();
        }
        Request request = new Request.Builder().url(url).build();
        return  client.newCall(request).execute();

}
}

调用getHttpRequest函数即可,这样可以实现http与https的代理,效果如下:

D35E54912EC8477F8A79E867088F75EC.png

后记

后面的功能根据需求添加即可,我这里做一个记录,后面有需求再添加。