You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Java程序授予运行时管理员权限以加密Docker内MySQL文件

问题概述

我开发了一个Java程序,使用IntelliJ IDE作为开发环境,希望从IDE启动程序并访问需要管理员权限的文件。程序包含EncryptionModule类,具备void encrypt(String password, String databaseName)和void decrypt(String password, String databaseName)两个方法,可接收用户提供的密码和文件路径,实现CSV文件或MySQL数据库的加解密。

该程序处理CSV文件正常,但加密Docker容器内的MySQL数据库文件时,因无管理员权限触发代码中else分支,输出“File does not exist”。代码判断逻辑如下:

Path path = Paths.get(databaseName);
if (Files.exists(path)){
    ...
} else {
    System.out.println("File does not exist");
}

我曾尝试以root身份运行IntelliJ IDE,但打开项目时IDE崩溃。希望找到无需以root运行IDE的解决方案,且需兼容Ubuntu和Windows系统。

程序完整代码:

package com.example.passwordsafe.data;

import com.example.passwordsafe.core.usecases.EncryptionModuleInterface;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

public class EncryptionModule implements EncryptionModuleInterface {
    private static final int ITERATION_COUNT = 1000000;
    private static final int KEY_LENGTH = 256;
    private static final String PBKDF_ALGORITHM = "PBKDF2WithHmacSHA1";
    private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
    private static final String ALGORITHM = "AES";

    @Override
    public void encrypt(String password, String databaseName) {

        Path path = Paths.get(databaseName);
        if (Files.exists(path)){
            File plaintextFile = new File(databaseName);
            File encryptedFile = new File(databaseName + ".encrypted");

            doEncryption(password, plaintextFile, encryptedFile);

            plaintextFile.delete();

            path = Paths.get(databaseName + ".encrypted");
            try {
                Files.move(path, path.resolveSibling(databaseName));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

        } else {
            System.out.println("File does not exist");
        }

    }

    @Override
    public void decrypt(String password, String databaseName) {

        Path path = Paths.get(databaseName);
        if (Files.exists(path)){
            File encryptedFile = new File(databaseName);
            File plaintextFile = new File(databaseName + ".decrypted");

            doDecryption(password, encryptedFile, plaintextFile);

            encryptedFile.delete();

            path = Paths.get(databaseName + ".decrypted");
            try {
                Files.move(path, path.resolveSibling(databaseName));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

        } else {
            System.out.println("File does not exist");
        }

    }

    private void doEncryption (String password, File inputFile, File outputFile) {
        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[16];
        random.nextBytes(salt);

        KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATION_COUNT, KEY_LENGTH);

        try {
            SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF_ALGORITHM);
            byte[] key = factory.generateSecret(spec).getEncoded();
            SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);

            byte[] ivBytes = new byte[16];
            random.nextBytes(ivBytes);
            IvParameterSpec iv = new IvParameterSpec(ivBytes);

            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);

            FileInputStream inputStream = new FileInputStream(inputFile);
            byte[] inputBytes = new byte[(int) inputFile.length()];
            inputStream.read(inputBytes);

            byte[] encValue = cipher.doFinal(inputBytes);
            byte[] finalCiphertext = new byte[encValue.length+2*16];
            System.arraycopy(ivBytes, 0, finalCiphertext, 0, 16);
            System.arraycopy(salt, 0, finalCiphertext, 16, 16);
            System.arraycopy(encValue, 0, finalCiphertext, 32, encValue.length);

            FileOutputStream outputStream = new FileOutputStream(outputFile);
            outputStream.write(finalCiphertext);

            inputStream.close();
            outputStream.close();

        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException |
                 IOException | BadPaddingException | InvalidKeySpecException | InvalidAlgorithmParameterException e) {
            throw new RuntimeException(e);
        }

    }

    private void doDecryption (String password, File inputFile, File outputFile) {
        FileInputStream inputStream = null;
        byte[] ivBytes = new byte[16];
        byte[] salt = new byte[16];

        byte[] readEncryptedBytesWithIvAndSaltPrefix;

        try {
            inputStream = new FileInputStream(inputFile);
            readEncryptedBytesWithIvAndSaltPrefix = new byte[(int) inputFile.length()];
            inputStream.read(readEncryptedBytesWithIvAndSaltPrefix);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        byte[] inputBytes = new byte[readEncryptedBytesWithIvAndSaltPrefix.length - 32];

        System.arraycopy(readEncryptedBytesWithIvAndSaltPrefix, 0, ivBytes, 0, 16);
        System.arraycopy(readEncryptedBytesWithIvAndSaltPrefix, 16, salt, 0, 16);
        System.arraycopy(readEncryptedBytesWithIvAndSaltPrefix, 32, inputBytes, 0, readEncryptedBytesWithIvAndSaltPrefix.length - 32);

        KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATION_COUNT, KEY_LENGTH);

        try {
            SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF_ALGORITHM);
            byte[] key = factory.generateSecret(spec).getEncoded();
            SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);

            IvParameterSpec iv = new IvParameterSpec(ivBytes);

            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);

            byte[] encValue = cipher.doFinal(inputBytes);

            FileOutputStream outputStream = new FileOutputStream(outputFile);
            outputStream.write(encValue);

            inputStream.close();
            outputStream.close();

        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException |
                 IOException | BadPaddingException | InvalidKeySpecException | InvalidAlgorithmParameterException e) {
            throw new RuntimeException(e);
        }

    }
}

main方法调用代码:

String dbpath = "/var/lib/docker/volumes/d74a425c8728e5333a3472860c2b62a3e47a0b5655dd44cce1b4c47ac2c3b6b8/_data/password_safe/password_entries.ibd";
String password = "password";
encryptionModuleInterface.encrypt(password, dbpath);

解决方案

1. 调整Docker卷目录权限(Ubuntu)

  • 查看目标目录的所属用户组:
    ls -ld /var/lib/docker/volumes/d74a425c8728e5333a3472860c2b62a3e47a0b5655dd44cce1b4c47ac2c3b6b8/_data/password_safe
    
  • 将当前用户加入该目录所属组(假设组为docker):
    sudo usermod -aG docker $USER
    
  • 给目录添加组读写权限:
    sudo chmod -R g+rw /var/lib/docker/volumes/d74a425c8728e5333a3472860c2b62a3e47a0b5655dd44cce1b4c47ac2c3b6b8/_data/password_safe
    
    注意:操作前先停止相关Docker容器,避免文件被锁定。

2. 以管理员权限单独运行Java程序(跨平台)

无需root启动IDE,仅给Java程序提权:

  • Ubuntu:编译程序后,在终端用sudo运行JAR包:
    sudo java -jar your-program.jar
    
    若要在IntelliJ中直接提权运行,可配置免密sudo:编辑/etc/sudoers添加:
    你的用户名 ALL=(ALL) NOPASSWD: /usr/bin/java
    
    然后在IntelliJ的运行配置中,将启动命令改为sudo java。
  • Windows:右键IntelliJ快捷方式选择“以管理员身份运行”,或在Run/Debug Configurations中勾选“以管理员身份启动”选项。

3. 优化代码的错误判断逻辑

当前代码无法区分“文件不存在”和“权限不足”,修改后可准确提示错误:

@Override
public void encrypt(String password, String databaseName) {
    Path path = Paths.get(databaseName);
    try {
        if (Files.exists(path)) {
            File plaintextFile = new File(databaseName);
            File encryptedFile = new File(databaseName + ".encrypted");

            doEncryption(password, plaintextFile, encryptedFile);

            plaintextFile.delete();

            path = Paths.get(databaseName + ".encrypted");
            Files.move(path, path.resolveSibling(databaseName));
        } else {
            System.out.println("文件不存在");
        }
    } catch (AccessDeniedException e) {
        System.out.println("无权限访问该文件,请提升权限后重试");
        throw new RuntimeException("权限不足", e);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

@Override
public void decrypt(String password, String databaseName) {
    Path path = Paths.get(databaseName);
    try {
        if (Files.exists(path)) {
            File encryptedFile = new File(databaseName);
            File plaintextFile = new File(databaseName + ".decrypted");

            doDecryption(password, encryptedFile, plaintextFile);

            encryptedFile.delete();

            path = Paths.get(databaseName + ".decrypted");
            Files.move(path, path.resolveSibling(databaseName));
        } else {
            System.out.println("文件不存在");
        }
    } catch (AccessDeniedException e) {
        System.out.println("无权限访问该文件,请提升权限后重试");
        throw new RuntimeException("权限不足", e);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

4. 映射Docker卷到普通用户目录(推荐)

重新创建容器时,将MySQL数据卷映射到当前用户有权限的目录,避免权限问题:

docker run -v /home/你的用户名/docker_volumes/password_safe:/var/lib/mysql/password_safe mysql:latest

此时数据库文件会存储在/home/你的用户名/docker_volumes/password_safe,当前用户默认拥有读写权限,无需提权即可访问。


内容的提问来源于stack exchange,提问作者max23

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 19:57:33