SHA和AES加密+GUI Swing写的一个本地运行和保存的密码管理小工具

通过SHA和AES加密方式,用Swing写的窗口界面

主要实现了登录、加密、解密、列表展示。其中密码加密后密文是以txt文件的形式保存到本地的。

我们来先看看效果。

效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

项目结构

在这里插入图片描述

功能

看完了效果和结构图,我们再来说下功能。

1、登录

在第一次使用这个程序时(通过有没有 密码管理器.txt 文件来判断是不是第一次使用),要先设置登录密码,以后用这个程序都是要用这个密码登录,并且加密解密也是用这个密码作为密钥的。

登录密码是用sha256进行加密(不可逆),密文保存到了 密码管理器.txt,这个文件设置了只读和隐藏。后续通过输入的密码用同样方式加密得到密文和这个txt的密文进行验证,通过了才能进入到主页,没通过直接结束程序。

在这里插入图片描述

2、加密

加密是会用到登录密码的,以登录密码作为密钥加密。得到的密文保存到 加密密码.txt。如果程序/网站名在txt已经存在了,则会把原密文替换成新的密文(修改密码)。比如微信,一开始的密码是123456,后面又输入了微信和新的密码点击加密,在txt中原本微信对应的密文就会变成新密码的密文。

在这里插入图片描述

3、解密

解密同样会用到登录密码。输入密文点击解密时,要再次验证登录密码,这是防止你自己登录进来后,程序还在运行,别人直接拿你的密文去解密。 如果密码错了也是直接退出程序,正确则进行解密。

在这里插入图片描述


解密用登录密码作为密钥,这是防止别人拿到了我的密文,ta去使用这个程序,设置的登录密码也是ta自己的(ta不知道我的登录密码),然后输入我的密文,点击解密,输入ta的登录密码后校验通过,导致我的密文被解密出来了。 用密码作为密钥的话,只要不知道我的登录密码,即使ta能用这个程序,知道我的密文,也不可能解密出来。 是不是这么个道理😎

在这里插入图片描述

4、列表

这个只是展示 加密密码.txt 的内容的。本来一开始是想除了展示,还能直接在这个列表进行解密(过程和上面那个一样)和修改密码。后面发现JTable这个组件太难搞了,光是一个展示页面,要让它在tab面板里面显示出来、设置表格样式这些就花了我不少时间(对swing真不会用),要让它每一行显示两个按钮,并且是可以点击的按钮,不会搞,想想就算了(不知道以后有没有精力加上去)😢,还是只做展示用吧,要修改/解密去加密、解密面板操作吧。

代码

1、先准备好两种加密方式的工具类

SHAUtil

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

/**
 * 密码盐值加密工具类
 */
public class SHAUtil {

    public static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA256";

    /**
     * 盐的长度
     */
    public static final int SALT_BYTE_SIZE = 16;

    /**
     * 生成密文的长度
     */
    public static final int HASH_BIT_SIZE = 256;

    /**
     * 迭代次数
     */
    public static final int PBKDF2_ITERATIONS = 1000;

    /**
     * 验证输入的password是否正确
     * @param attemptedPassword 待验证的password
     * @param encryptedPassword 密文
     * @param salt 盐值
     */
    public static boolean authenticate(String attemptedPassword, String encryptedPassword, String salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
        // 用同样的盐值对用户输入的password进行加密
        String encryptedAttemptedPassword = getEncryptedPassword(attemptedPassword, salt);
        // 把加密后的密文和原密文进行比較,同样则验证成功。否则失败
        return encryptedAttemptedPassword.equals(encryptedPassword);
    }

    /**
     * 生成密文
     * @param password 明文password
     * @param salt 盐值
     */
    public static String getEncryptedPassword(String password, String salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
        KeySpec spec = new PBEKeySpec(password.toCharArray(), fromHex(salt), PBKDF2_ITERATIONS, HASH_BIT_SIZE);
        SecretKeyFactory f = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
        return toHex(f.generateSecret(spec).getEncoded());
    }

    /**
     * 通过提供加密的强随机数生成器 生成盐
     */
    public static String generateSalt() throws NoSuchAlgorithmException {
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        byte[] salt = new byte[SALT_BYTE_SIZE];
        random.nextBytes(salt);
        return toHex(salt);
    }

    /**
     * 十六进制字符串转二进制字符串
     * @param hex the hex string
     */
    private static byte[] fromHex(String hex) {
        byte[] binary = new byte[hex.length() / 2];
        for (int i = 0; i < binary.length; i++) {
            binary[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
        }
        return binary;
    }

    /**
     * 二进制字符串转十六进制字符串
     * @param array the byte array to convert
     */
    private static String toHex(byte[] array) {
        BigInteger bi = new BigInteger(1, array);
        String hex = bi.toString(16);
        int paddingLength = (array.length * 2) - hex.length();
        if (paddingLength > 0) return String.format("%0" + paddingLength + "d", 0) + hex;
        else return hex;
    }
}

AESUtil

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.swing.*;
import javax.swing.plaf.FontUIResource;
import java.awt.*;
import java.io.*;
import java.util.Random;

/**
 * AES加密、解密工具类
 */
public class AESUtil {

    /*
     * 加密用的Key 可以用26个字母和数字组成 此处使用AES-128-CBC加密模式,key需要为16位。
     * key是aes的一个加密源,也就是通过该key加密后,必须通过该key就进行解密;对数据不是特别敏感的,直接用静态的key即可;
     * 有些场景下数据异常敏感,那么这个key就要动态生成,结合RSA进行加密,,就能做到每次请求,key都不一样,安全程度大大加强;
     * 当然key可以为32,但是要下载两个jdk的jar包
     */
    public static String 默认密钥 = "xcwssythjqruijkg";
    public static String 密钥 = 默认密钥;

    /**
     * 偏移量,随便给一个就可以了,不给也行
     */
    public static String 默认偏移量 = "xcwssythjqruijkg";
    public static String 偏移量 = 默认偏移量;

    /**
     * 分隔符
     */
    public static final String separate = "ZmhzeGZsd2I=";

    public static String 登录密码 = "";
    public static String 密文 = "";

    /**
     * 加密
     * @param str 要加密的字符串
     * @param flag 是否使用默认密钥和偏移量进行加密
     */
    public static String encrypt(String str,boolean flag) {
        int length = 登录密码.length();
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            if (!flag){
                密钥 = 登录密码 + generateRandom(16-length); // 随机生成密钥
                偏移量 = 登录密码 + generateRandom(16-length); // 获取偏移量
            }else {
                密钥 = 登录密码 + 默认密钥.substring(0,16-length);
                偏移量 = 登录密码 + 默认偏移量.substring(0,16-length);
            }
            //System.out.println("加密密钥和偏移量:"+密钥+"\t"+偏移量);
            SecretKeySpec skeySpec = new SecretKeySpec(密钥.getBytes(), "AES");
            IvParameterSpec ivp = new IvParameterSpec(偏移量.getBytes());// 使用CBC模式,需要一个向量iv,可增加加密算法的强度
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivp);
            byte[] encrypted = cipher.doFinal(str.getBytes("utf-8"));
            return new BASE64Encoder().encode(encrypted);// 此处使用BASE64做转码。
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解密
     * @param str 要解密的字符串
     * @param raw 密钥
     * @param ivStr 偏移量
     */
    public static String decrypt(String str,String raw,String ivStr){
        try {
            SecretKeySpec skeySpec = new SecretKeySpec(raw.getBytes(), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            IvParameterSpec iv = new IvParameterSpec(ivStr.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] encrypted1 = new BASE64Decoder().decodeBuffer(str);// 先用base64解密
            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original, "utf-8");
            return originalString;
        } catch (Exception ex) {
            return null;
        }
    }

    /**
     * 写入到txt文件
     * @param path   文件路径
     * @param start 内容开头
     * @param content 内容
     */
    public static void 写入内容(String path,String start,String content) {
        BufferedWriter out = null;
        BufferedReader reader = null;
        StringBuilder sb = new StringBuilder();
        try {
            File file = new File(path);
            if (!file.exists()){
                file.createNewFile();
            }
            String str = "";
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
            while((str = reader.readLine())!=null){
                if (isNotBlank(start) && str.startsWith(start)){ // 替换旧内容
                    sb.append(content).append("\n");
                }else {
                    sb.append(str).append("\n");
                }
            }
            str = sb.toString();
            if (!str.contains(content)){ // 追加新内容
                sb.append(content).append("\n");
            }
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
            out.write(sb.toString());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                reader.close();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static String 读取登录密码(String path) {
        BufferedReader reader = null;
        String str = "";
        try {
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(path)));
            while((str = reader.readLine())!=null){
                break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        密文 = str;
        return str;
    }

    /**
     * 根据指定长度随机生成字符串
     */
    public static String generateRandom(int length) {
        String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; ++i) {
            int number = random.nextInt(str.length());
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }

    public static boolean isNotBlank(String str){
        if (str == null || str.length() == 0){
            return false;
        }
        return true;
    }

    public static boolean isNull(String str){
        if (str == null || str.length() == 0){
            return true;
        }
        return false;
    }

    /**
     * 提示框
     */
    public static void tips(String str){
        UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("宋体", Font.ITALIC, 16)));
        JOptionPane.showMessageDialog(null, str);
    }
}

2、登录窗口

import com.util.AESUtil;
import com.util.SHAUtil;
import com.主程序;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;

public class 登录 {
    public JFrame frame;
    public JPanel panel;
    private JLabel label0;
    private JButton 确定Button;
    private JPasswordField 请输入登录密码PasswordField;

    /**
     * 登录采用SHA256盐值加密
     * @param 调用位置 用于区分是在哪个地方调用显示这个窗口的:1 主程序 2 主页
     */
    public 登录(int 调用位置) {
        String path = System.getProperty("user.dir")+"/密码管理器.txt";
        File file = new File(path);
        if (!file.exists()) {
            label0.setText("第一次使用请先设置登录密码");
        }
        确定Button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    String next = new String(请输入登录密码PasswordField.getPassword());
                    if (!AESUtil.isNotBlank(next)){
                        AESUtil.tips("请先输入登录密码!");
                    }else {
                        if (!file.exists()){ // 如果路径不存在这个文件,要求用户设置使用该应用程序的密码
                            String salt = SHAUtil.generateSalt(); // 生成盐值
                            String password = SHAUtil.getEncryptedPassword(next, salt); // 得到密文
                            password = password + AESUtil.separate + salt;
                            AESUtil.写入内容(path,null,password);
                            AESUtil.tips("登录密码设置成功!");
                            file.setReadOnly(); // 只读
                            Runtime.getRuntime().exec("attrib +H \"" + file.getAbsolutePath() + "\""); // 隐藏
                            AESUtil.登录密码 = next;
                            AESUtil.密文 = password;
                        }else {
                            String 密文 = AESUtil.读取登录密码(path);
                            String[] split = 密文.split(AESUtil.separate);
                            boolean authenticate = SHAUtil.authenticate(next,split[0], split[1]);
                            if (!authenticate){
                                AESUtil.tips("登录密码错误!无法使用本程序!");
                                System.exit(0);
                            }
                            AESUtil.登录密码 = next;
                            if (调用位置 == 1){ // 在主程序登录时,给个提示
                                AESUtil.tips("登录成功!");
                            }
                        }
                        frame.setVisible(false); // 隐藏当前登录窗口
                        if (调用位置 == 1){
                            主程序.主页Form(); // 加载主页窗口
                        }
                    }
                }catch (Exception ex){
                    AESUtil.tips("登录密码错误!无法使用本程序!");
                    System.exit(0);
                }
            }
        });
    }
}

窗口是以xml形式保存的

<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.form.登录">
  <grid id="27dc6" binding="panel" layout-manager="GridLayoutManager" row-count="8" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
    <margin top="0" left="0" bottom="0" right="0"/>
    <constraints>
      <xy x="20" y="20" width="615" height="400"/>
    </constraints>
    <properties/>
    <border type="none"/>
    <children>
      <component id="19752" class="javax.swing.JLabel" binding="label0">
        <constraints>
          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="5" use-parent-layout="false"/>
        </constraints>
        <properties>
          <font size="28"/>
          <foreground color="-4258806"/>
          <labelFor value="d1622"/>
          <text value="请输入登录密码"/>
        </properties>
      </component>
      <vspacer id="517b1">
        <constraints>
          <grid row="3" column="1" row-span="5" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="5" use-parent-layout="false"/>
        </constraints>
      </vspacer>
      <hspacer id="7b473">
        <constraints>
          <grid row="7" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
            <preferred-size width="-1" height="50"/>
          </grid>
        </constraints>
      </hspacer>
      <hspacer id="3c260">
        <constraints>
          <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
            <preferred-size width="-1" height="20"/>
          </grid>
        </constraints>
      </hspacer>
      <hspacer id="47b1b">
        <constraints>
          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
            <preferred-size width="-1" height="30"/>
          </grid>
        </constraints>
      </hspacer>
      <component id="5547" class="javax.swing.JButton" binding="确定Button" default-binding="true">
        <constraints>
          <grid row="6" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="0" indent="5" use-parent-layout="false">
            <preferred-size width="200" height="50"/>
          </grid>
        </constraints>
        <properties>
          <font size="24"/>
          <text value="确定"/>
        </properties>
      </component>
      <hspacer id="462e7">
        <constraints>
          <grid row="5" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
            <preferred-size width="-1" height="20"/>
          </grid>
        </constraints>
      </hspacer>
      <component id="d1622" class="javax.swing.JPasswordField" binding="请输入登录密码PasswordField" default-binding="true">
        <constraints>
          <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="5" use-parent-layout="false">
            <preferred-size width="500" height="40"/>
          </grid>
        </constraints>
        <properties>
          <font size="18"/>
        </properties>
      </component>
    </children>
  </grid>
</form>

在这里插入图片描述

3、主页窗口(加密和解密面板)

import com.util.AESUtil;
import com.主程序;

import javax.swing.*;
import java.awt.event.*;

public class 主页 {

    public JFrame frame;
    public JPanel panel;
    public JTabbedPane tabPane;
    private JPanel 加密tab;
    private JPanel 解密tab;
    public JPanel 列表tab;
    private JLabel label1;
    private JTextField 输入应用的网站或软件TextField;
    private JLabel label2;
    private JLabel label4;
    private JTextArea 加密后TextArea;
    private JButton 加密Button;
    private JPasswordField 输入密码PasswordField;
    private JLabel label3;
    private JPasswordField 确认密码PasswordField;
    private JLabel label5;
    private JTextArea 输入需要解密的密文TextArea;
    private JButton 解密Button;
    private JTextField 解密后TextField;
    private JLabel label6;

    public 主页() {
        确认密码PasswordField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                super.focusLost(e);
                String 密码 = new String(输入密码PasswordField.getPassword());
                String 确认密码 = new String(确认密码PasswordField.getPassword());
                if(!密码.equals(确认密码)){
                    AESUtil.tips("两次输入的密码不一致!");
                }
            }
        });

        加密Button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String 应用 = 输入应用的网站或软件TextField.getText();
                String 密码 = new String(输入密码PasswordField.getPassword());
                String 确认密码 = new String(确认密码PasswordField.getPassword());
                if (AESUtil.isNull(应用) || AESUtil.isNull(密码) || AESUtil.isNull(确认密码)){
                    AESUtil.tips("上面三个输入框为必填项!");
                    return;
                }
                if(!密码.equals(确认密码)){
                    AESUtil.tips("两次输入的密码不一致!");
                    return;
                }
                String 保存位置 = System.getProperty("user.dir")+"/加密密码.txt";
                String 密码加密 = AESUtil.encrypt(密码,false); // 先使用随机密钥加密密码
                密码加密 = AESUtil.密钥 + AESUtil.separate + AESUtil.偏移量 + AESUtil.separate + 密码加密;
                // 再将 密钥、偏移量、加密的密码拼接在一起,通过默认密钥再进行一次加密,得到最终密文
                String 最终密文 = AESUtil.encrypt(密码加密,true).replaceAll("\r\n","");
                加密后TextArea.setText(最终密文);
                String 输出内容 = 应用 + ":" + 最终密文;
                AESUtil.写入内容(保存位置,应用,输出内容);
            }
        });

        解密Button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                主程序.登录Form(2); // 点击界面按钮,以模态框的形式显示登录窗口,校验登录密码。
                String text = 输入需要解密的密文TextArea.getText();
                if (AESUtil.isNotBlank(text)){
                    String 密钥 = AESUtil.登录密码 + AESUtil.默认密钥.substring(0,16 - AESUtil.登录密码.length());
                    String 偏移量 = AESUtil.登录密码 + AESUtil.默认偏移量.substring(0,16 - AESUtil.登录密码.length());
                    //System.out.println("解密密钥和偏移量:"+密钥+"\t"+偏移量);
                    String 密文 = AESUtil.decrypt(text,密钥,偏移量);
                    if (AESUtil.isNull(密文)){
                        AESUtil.tips("解密失败,请检查密文是否正确!");
                        return;
                    }
                    String[] split = 密文.split(AESUtil.separate);
                    String 密码 = AESUtil.decrypt(split[2],split[0],split[1]);
                    if (AESUtil.isNull(密文)){
                        AESUtil.tips("解密失败,请检查密文是否正确!");
                        return;
                    }
                    解密后TextField.setText(密码);
                }else {
                    AESUtil.tips("请输入需要解密的密文!");
                }
            }
        });
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.form.主页">
  <grid id="27dc6" binding="panel" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
    <margin top="0" left="0" bottom="0" right="0"/>
    <constraints>
      <xy x="20" y="20" width="2529" height="817"/>
    </constraints>
    <properties/>
    <border type="none"/>
    <children>
      <tabbedpane id="dbc08" binding="tabPane">
        <constraints>
          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="3" use-parent-layout="false">
            <preferred-size width="1000" height="600"/>
          </grid>
        </constraints>
        <properties>
          <font name="Consolas" size="24"/>
        </properties>
        <border type="none"/>
        <children>
          <grid id="e28e7" binding="加密tab" layout-manager="GridLayoutManager" row-count="12" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
            <margin top="0" left="0" bottom="0" right="0"/>
            <constraints>
              <tabbedpane title="加密"/>
            </constraints>
            <properties>
              <font/>
            </properties>
            <border type="none"/>
            <children>
              <hspacer id="621b5">
                <constraints>
                  <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
                    <preferred-size width="-1" height="20"/>
                  </grid>
                </constraints>
              </hspacer>
              <component id="6d000" class="javax.swing.JLabel" binding="label1">
                <constraints>
                  <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
                </constraints>
                <properties>
                  <font name="Consolas" size="20"/>
                  <foreground color="-1942775"/>
                  <labelFor value="ec6a5"/>
                  <text value="输入应用的网站或软件"/>
                </properties>
              </component>
              <component id="ec6a5" class="javax.swing.JTextField" binding="输入应用的网站或软件TextField" default-binding="true">
                <constraints>
                  <grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
                    <preferred-size width="500" height="30"/>
                  </grid>
                </constraints>
                <properties>
                  <columns value="0"/>
                  <font size="18"/>
                </properties>
              </component>
              <component id="efd75" class="javax.swing.JLabel" binding="label2">
                <constraints>
                  <grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
                </constraints>
                <properties>
                  <font name="Consolas" size="20"/>
                  <foreground color="-1942775"/>
                  <labelFor value="8bad4"/>
                  <text value="输入密码"/>
                </properties>
              </component>
              <component id="8bad4" class="javax.swing.JPasswordField" binding="输入密码PasswordField" default-binding="true">
                <constraints>
                  <grid row="4" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
                    <preferred-size width="500" height="30"/>
                  </grid>
                </constraints>
                <properties>
                  <font size="18"/>
                </properties>
              </component>
              <component id="9eb3d" class="javax.swing.JLabel" binding="label3">
                <constraints>
                  <grid row="5" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
                </constraints>
                <properties>
                  <font name="Consolas" size="20"/>
                  <foreground color="-1942775"/>
                  <labelFor value="accd4"/>
                  <text value="确认密码"/>
                </properties>
              </component>
              <component id="accd4" class="javax.swing.JPasswordField" binding="确认密码PasswordField" default-binding="true">
                <constraints>
                  <grid row="6" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
                    <preferred-size width="500" height="30"/>
                  </grid>
                </constraints>
                <properties>
                  <font size="18"/>
                </properties>
              </component>
              <component id="70f6c" class="javax.swing.JButton" binding="加密Button" default-binding="true">
                <constraints>
                  <grid row="7" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
                </constraints>
                <properties>
                  <background color="-3148033"/>
                  <font name="Consolas" size="24" style="1"/>
                  <foreground color="-14065484"/>
                  <text value="加密"/>
                </properties>
              </component>
              <component id="48e2f" class="javax.swing.JLabel" binding="label4">
                <constraints>
                  <grid row="8" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
                </constraints>
                <properties>
                  <font name="Consolas" size="20"/>
                  <foreground color="-1942775"/>
                  <labelFor value="20165"/>
                  <text value="加密后"/>
                </properties>
              </component>
              <component id="20165" class="javax.swing.JTextArea" binding="加密后TextArea" default-binding="true">
                <constraints>
                  <grid row="9" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
                    <preferred-size width="500" height="150"/>
                  </grid>
                </constraints>
                <properties>
                  <enabled value="true"/>
                  <font size="18"/>
                  <lineWrap value="true"/>
                  <wrapStyleWord value="true"/>
                </properties>
              </component>
              <hspacer id="fe821">
                <constraints>
                  <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="3" use-parent-layout="false"/>
                </constraints>
              </hspacer>
              <hspacer id="d1224">
                <constraints>
                  <grid row="1" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="3" use-parent-layout="false"/>
                </constraints>
              </hspacer>
              <hspacer id="2dcc">
                <constraints>
                  <grid row="10" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
                    <preferred-size width="-1" height="20"/>
                  </grid>
                </constraints>
              </hspacer>
            </children>
          </grid>
          <grid id="9db33" binding="解密tab" layout-manager="GridLayoutManager" row-count="11" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
            <margin top="0" left="0" bottom="0" right="0"/>
            <constraints>
              <tabbedpane title="解密"/>
            </constraints>
            <properties/>
            <border type="none"/>
            <children>
              <hspacer id="ed522">
                <constraints>
                  <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
                    <preferred-size width="-1" height="20"/>
                  </grid>
                </constraints>
              </hspacer>
              <component id="b7c47" class="javax.swing.JLabel" binding="label5">
                <constraints>
                  <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
                </constraints>
                <properties>
                  <font name="Consolas" size="20"/>
                  <foreground color="-1942775"/>
                  <labelFor value="efa68"/>
                  <text value="输入需要解密的密文"/>
                </properties>
              </component>
              <component id="efa68" class="javax.swing.JTextArea" binding="输入需要解密的密文TextArea" default-binding="true">
                <constraints>
                  <grid row="3" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
                    <preferred-size width="500" height="150"/>
                  </grid>
                </constraints>
                <properties>
                  <font size="18"/>
                  <lineWrap value="true"/>
                  <wrapStyleWord value="true"/>
                </properties>
              </component>
              <component id="1cf30" class="javax.swing.JButton" binding="解密Button" default-binding="true">
                <constraints>
                  <grid row="5" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
                </constraints>
                <properties>
                  <background color="-3148033"/>
                  <font name="Consolas" size="24" style="1"/>
                  <foreground color="-14065484"/>
                  <horizontalTextPosition value="0"/>
                  <text value="解密"/>
                </properties>
              </component>
              <component id="145c5" class="javax.swing.JLabel" binding="label6">
                <constraints>
                  <grid row="7" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
                </constraints>
                <properties>
                  <background color="-14605013"/>
                  <font name="Consolas" size="20"/>
                  <foreground color="-1942775"/>
                  <labelFor value="7d1e"/>
                  <text value="解密后"/>
                </properties>
              </component>
              <component id="7d1e" class="javax.swing.JTextField" binding="解密后TextField" default-binding="true">
                <constraints>
                  <grid row="9" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
                    <preferred-size width="500" height="30"/>
                  </grid>
                </constraints>
                <properties>
                  <enabled value="true"/>
                  <font size="18"/>
                </properties>
              </component>
              <hspacer id="f8a36">
                <constraints>
                  <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="3" use-parent-layout="false"/>
                </constraints>
              </hspacer>
              <hspacer id="efa45">
                <constraints>
                  <grid row="1" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="3" use-parent-layout="false"/>
                </constraints>
              </hspacer>
              <hspacer id="2c93e">
                <constraints>
                  <grid row="4" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
                    <preferred-size width="-1" height="20"/>
                  </grid>
                </constraints>
              </hspacer>
              <hspacer id="41ab9">
                <constraints>
                  <grid row="6" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
                    <preferred-size width="-1" height="20"/>
                  </grid>
                </constraints>
              </hspacer>
              <hspacer id="a62e9">
                <constraints>
                  <grid row="8" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
                    <preferred-size width="-1" height="10"/>
                  </grid>
                </constraints>
              </hspacer>
              <hspacer id="531f5">
                <constraints>
                  <grid row="10" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
                    <preferred-size width="-1" height="20"/>
                  </grid>
                </constraints>
              </hspacer>
              <hspacer id="43303">
                <constraints>
                  <grid row="2" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
                    <preferred-size width="-1" height="10"/>
                  </grid>
                </constraints>
              </hspacer>
            </children>
          </grid>
        </children>
      </tabbedpane>
      <vspacer id="f6bfb">
        <constraints>
          <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="3" use-parent-layout="false"/>
        </constraints>
      </vspacer>
      <hspacer id="c482c">
        <constraints>
          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
            <preferred-size width="-1" height="30"/>
          </grid>
        </constraints>
      </hspacer>
      <hspacer id="78883">
        <constraints>
          <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
            <preferred-size width="-1" height="30"/>
          </grid>
        </constraints>
      </hspacer>
    </children>
  </grid>
</form>

4、主页窗口(列表面板)

列表面板是单独拿出来,加载主页窗口时,手动添加一个tab,把列表面板放到这个tab中显示的。

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.table.*;
import java.awt.*;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class 列表 extends JPanel {

    private DefaultTableModel tableModel;
    public JTable table;
    private TableColumn column;
    private int DEFAULE_WIDTH = 1000;
    private int DEFAULE_HEIGH = 560;
    private String[] 表头 = { "程序/网站", "密文"};
    private String[][] 数据 = new String[0][0]; // 初始化一开始先设置长度为0,不然即使没数据,表格也会显示一行空行

    public 列表() {
        setBorder(new EmptyBorder(5, 5, 5, 5));
        setLayout(new BorderLayout());
        setPreferredSize(new Dimension(DEFAULE_WIDTH, DEFAULE_HEIGH));
        createTable();
    }

    public void createTable(){
        int[] width = {100, 700}; // 列宽
        读取内容(); // 填充表头和数据行
        tableModel = new DefaultTableModel(数据, 表头){ // 用双数组创建DefaultTableModel对象
            @Override
            public boolean isCellEditable(int row, int column) {
                if(column == 1){
                    return true;
                }else{
                    return false; //表格不允许被编辑
                }
            }
        };
        table = new JTable(tableModel); // 创建表格组件

        // 设置表头样式
        JTableHeader head = table.getTableHeader(); // 创建表格标题对象
        head.setPreferredSize(new Dimension(head.getWidth(), 50));// 设置表头大小
        head.setFont(new Font("宋体", Font.BOLD, 20));// 设置表格字体
        head.setReorderingAllowed(false); // 设置表头不可拖动

        // 设置表格样式
        table.setRowHeight(40);// 设置表格行高
        table.setFont(new Font("宋体",0,14));
        table.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);// 以下设置表格列宽
        table.setFillsViewportHeight(true);
        table.setAutoCreateRowSorter(true);
        for (int i = 0; i < 2; i++) {
            column = table.getColumnModel().getColumn(i);
            column.setPreferredWidth(width[i]);
        }

        // 设置表格间隔色(表格单元格以JLabel的形式显示)
        DefaultTableCellRenderer ter = new DefaultTableCellRenderer() {
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
                if (row % 2 == 0) setBackground(Color.pink);
                else if (row % 2 == 1) setBackground(Color.white);
                return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
            }
        };
        ter.setHorizontalAlignment(JLabel.CENTER);// 设置表格内容居中显示

        table.getColumn(表头[0]).setCellRenderer(ter);
        table.getColumn(表头[1]).setCellRenderer(new TableViewRenderer());
        // 创建滚动条组件,默认滚动条始终出现,初始化列表组件
        JScrollPane scrollPane = new JScrollPane(table, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        add(scrollPane, BorderLayout.CENTER);
    }

    private void 读取内容() {
        String path = System.getProperty("user.dir")+"/加密密码.txt";
        File file = new File(path);
        if (!file.exists()){
            return;
        }
        BufferedReader reader = null;
        String str = "";
        try {
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(path)));
            List<String> list = new ArrayList<>();
            while((str = reader.readLine())!=null){
                list.add(str);
            }
            数据 = new String[list.size()][];
            for (int i = 0; i < list.size(); i++) {
                String[] split = list.get(i).toString().split(":");
                String[] arr = {split[0], split[1]};
                数据[i] = arr;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void 刷新(){
        读取内容(); // 先重新读取一遍txt的内容,得到最新的数据
        int rowCount = tableModel.getRowCount(); // 获得未刷新前表格的行数(没数据时默认会有一行)
        int length = 数据.length; // 得到最新有多少条数据

        for (int i = 0; i < 数据.length; i++) {
            String[] obj = 数据[i];
            // txt的行数大于表格的行数,要将txt多出来的数据添加到表格中,只将新数据添加到表格中
            if (i >= rowCount){
                tableModel.addRow(obj);
            }
            String col0 = (String) tableModel.getValueAt(i, 0);
            String col1 = (String) tableModel.getValueAt(i, 1);
            // 这里是假如在加密中改了某一项的密码,密文也就变了,所以把某项对应的新密文更新到表格中
            if (obj[0].equals(col0) && !obj[1].equals(col1)){
                tableModel.setValueAt(obj[1],i,1);
            }
        }
        tableModel.setRowCount(length); // 更新表格的行数
    }
}

这个是用于设置密文那一列的单元格以JTextArea(多行文本)的形式显示,可以自动换行。

import javax.swing.*;
import javax.swing.table.TableCellRenderer;
import java.awt.*;

public class TableViewRenderer extends JTextArea implements TableCellRenderer {

    public TableViewRenderer() {
        super();
        //将表格设为自动换行
        setLineWrap(true); //利用JTextArea的自动换行方法
        setFont(new Font("Consolas",0,14));
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        setText(value == null ? "" : value.toString()); //利用JTextArea的setText设置文本方法
        // 设置隔行色
        if (row % 2 == 0) setBackground(Color.pink);
        else if (row % 2 == 1) setBackground(Color.white);
        if (isSelected){ // 如果当前单元格是选中状态,变更背景色
            setBackground(table.getSelectionBackground());
        }
        return this;
    }
}

5、主程序(main)

import com.form.主页;
import com.form.列表;
import com.form.登录;

import javax.swing.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;

public class 主程序 {

    public static void main(String[] args) {
        登录Form(1);
    }

    /**
     * 加载登录窗口
     * @param 调用位置 用于区分是在哪个地方调用显示这个窗口的:1 主程序 2 主页
     */
    public static void 登录Form(int 调用位置){
        登录 登录 = new 登录(调用位置);
        登录.frame = new JFrame("登录");
        if (调用位置 == 2){
            // 这里使用模态窗口,置顶,且只能操作这个窗口
            JDialog dialog = new JDialog(登录.frame,"登录",true);
            dialog.setSize(600,500);
            dialog.setLocationRelativeTo(null);
            dialog.setContentPane(登录.panel);
            dialog.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            dialog.pack();
            dialog.setVisible(true);
            dialog.setResizable(false);
        }else {
            登录.frame.setSize(600,500);
            登录.frame.setLocationRelativeTo(null);
            登录.frame.setContentPane(登录.panel);
            登录.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            登录.frame.pack();
            登录.frame.setVisible(true);
            登录.frame.setResizable(false);
        }
    }

    /**
     * 加载主页窗口
     */
    public static void 主页Form(){
        主页 主页 = new 主页();
        主页.frame = new JFrame("主页");
        主页.frame.setSize(1000,600);
        主页.frame.setLocationRelativeTo(null);
        主页.frame.setContentPane(主页.panel);
        主页.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        主页.frame.pack();
        主页.frame.setVisible(true);
        主页.frame.setResizable(false);
		
		// 手动添加列表tab到tabPane中
        列表 table = new 列表();
        主页.列表tab = new JPanel();
        主页.列表tab.add(new JScrollPane(table));
        主页.tabPane.add("列表",主页.列表tab);
        主页.tabPane.add(主页.列表tab,"列表",2);
        // 每次选中列表tab时,刷新它表格里面的数据
        主页.列表tab.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentShown(ComponentEvent e) {
                super.componentShown(e);
                table.刷新();
            }
        });
    }
}

最后

上面是全部代码了,觉得复制麻烦的同学,可以去 这里Gitee 直接下载,我把打包的jar和exe程序也放到这里了,有兴趣的可以下载来看看😁(这是2023年第一篇文章,求点赞求评论鸭,你的点赞和评论就是我前进的动力~🥰)

哦,对了,关于用到的exe4j打包工具(jar转exe),直接去 这里 下载吧!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

符华-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值