Java SFTP 服务器库?

发布于 2024-09-06 10:46:37 字数 1539 浏览 5 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

撞了怀 2024-09-13 10:46:37

如何使用 Apache Mina SSHD 设置 SFTP 服务器:

public void setupSftpServer(){
    SshServer sshd = SshServer.setUpDefaultServer();
    sshd.setPort(22);
    sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("hostkey.ser"));

    List<NamedFactory<UserAuth>> userAuthFactories = new ArrayList<NamedFactory<UserAuth>>();
    userAuthFactories.add(new UserAuthNone.Factory());
    sshd.setUserAuthFactories(userAuthFactories);

    sshd.setCommandFactory(new ScpCommandFactory());

    List<NamedFactory<Command>> namedFactoryList = new ArrayList<NamedFactory<Command>>();
    namedFactoryList.add(new SftpSubsystem.Factory());
    sshd.setSubsystemFactories(namedFactoryList);

    try {
        sshd.start();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

仅此而已。

How to setup an SFTP server using Apache Mina SSHD:

public void setupSftpServer(){
    SshServer sshd = SshServer.setUpDefaultServer();
    sshd.setPort(22);
    sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("hostkey.ser"));

    List<NamedFactory<UserAuth>> userAuthFactories = new ArrayList<NamedFactory<UserAuth>>();
    userAuthFactories.add(new UserAuthNone.Factory());
    sshd.setUserAuthFactories(userAuthFactories);

    sshd.setCommandFactory(new ScpCommandFactory());

    List<NamedFactory<Command>> namedFactoryList = new ArrayList<NamedFactory<Command>>();
    namedFactoryList.add(new SftpSubsystem.Factory());
    sshd.setSubsystemFactories(namedFactoryList);

    try {
        sshd.start();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

And that's all.

小…红帽 2024-09-13 10:46:37

请注意,SFTP 不是基于 SSL 的 FTP,也不是基于 SSH 的 FTP。 SFTP 服务器支持需要在 Java 中实现 SSHD。最好的选择是 Apache SSHD,

http://mina.apache.org/sshd-project/

我从未使用过 SFTP,但我听说它很基本但很实用。

Please notice that SFTP is a not FTP over SSL, nor FTP over SSH. The SFTP server support requires an implementation of SSHD in Java. Your best bet is Apache SSHD,

http://mina.apache.org/sshd-project/

I never used the SFTP but I heard it's basic but functional.

司马昭之心 2024-09-13 10:46:37

我尝试使用上述方法在 Windows 上执行 MINA 0.10.1 并修复了一些问题,另外我需要更好的身份验证和 PK 支持(仍然不建议用于生产使用):

import java.io.File;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.PrintWriter;

import java.util.Arrays;
import java.util.Map;
import java.util.HashMap;
import java.util.Scanner;

import java.math.BigInteger;

import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.interfaces.DSAPublicKey;

import java.security.KeyFactory;

import java.security.spec.KeySpec;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.RSAPublicKeySpec;

import org.apache.sshd.common.NamedFactory;

import org.apache.sshd.SshServer;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.command.ScpCommandFactory;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.PasswordAuthenticator;
import org.apache.sshd.server.PublickeyAuthenticator;
import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.server.sftp.SftpSubsystem;
import org.apache.sshd.server.shell.ProcessShellFactory;
import org.apache.sshd.server.UserAuth;
import org.apache.sshd.server.auth.UserAuthPassword;
import org.apache.sshd.server.auth.UserAuthPublicKey;

import org.apache.sshd.common.KeyExchange;
//import org.apache.sshd.server.kex.DHGEX;
//import org.apache.sshd.server.kex.DHGEX256;
import org.apache.sshd.server.kex.ECDHP256;
import org.apache.sshd.server.kex.ECDHP384;
import org.apache.sshd.server.kex.ECDHP521;
import org.apache.sshd.server.kex.DHG1;

import org.apache.mina.util.Base64;
/*
javac -classpath .;lib/sshd-core-0.10.1.jar;lib/mina-core-2.0.7.jar;lib/waffle-jna.jar;lib/guava-13.0.1.jar;lib/jna-platform-4.0.0.jar;lib/jna-4.0.0.jar SFTPServer.java
java -classpath .;lib/sshd-core-0.10.1.jar;lib/slf4j-simple-1.7.6.jar;lib/slf4j-api-1.6.6.jar;lib/mina-core-2.0.7.jar;lib/waffle-jna.jar;lib/guava-13.0.1.jar;lib/jna-platform-4.0.0.jar;lib/jna-4.0.0.jar SFTPServer
*/
public class SFTPServer {
  public void setupSftpServer() throws Exception {

    class AuthorizedKeyEntry {
      private String keyType;
      private String pubKey;

      private byte[] bytes;
      private int pos;
      private PublicKey key = null;

      private int decodeInt() {
        return ((bytes[pos++] & 0xFF) << 24) | ((bytes[pos++] & 0xFF) << 16)
                | ((bytes[pos++] & 0xFF) << 8) | (bytes[pos++] & 0xFF);
      }

      private BigInteger decodeBigInt() {
        int len = decodeInt();
        byte[] bigIntBytes = new byte[len];
        System.arraycopy(bytes, pos, bigIntBytes, 0, len);
        pos += len;
        return new BigInteger(bigIntBytes);
      }

      private void decodeType() {
        int len = decodeInt();
        keyType = new String(bytes, pos, len);
        pos += len;
      }

      public PublicKey getPubKey()  {
        return key;
      }

      public void setPubKey(PublicKey key) throws Exception {
        this.key = key;
        ByteArrayOutputStream byteOs = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(byteOs);
        if (key instanceof RSAPublicKey) {
          keyType = "ssh-rsa";
          dos.writeInt(keyType.getBytes().length);
          dos.write(keyType.getBytes());

          RSAPublicKey rsakey = (RSAPublicKey)key;
          BigInteger e = rsakey.getPublicExponent();
          dos.writeInt(e.toByteArray().length);
          dos.write(e.toByteArray());
          BigInteger m = rsakey.getModulus();
          dos.writeInt(m.toByteArray().length);
          dos.write(m.toByteArray());
        } else if (key instanceof DSAPublicKey) {
          keyType = "ssh-dss";
          dos.writeInt(keyType.getBytes().length);
          dos.write(keyType.getBytes());

          DSAPublicKey dsskey = (DSAPublicKey)key;
          BigInteger p = dsskey.getParams().getP();
          dos.writeInt(p.toByteArray().length);
          dos.write(p.toByteArray());
          BigInteger q = dsskey.getParams().getQ();
          dos.writeInt(q.toByteArray().length);
          dos.write(q.toByteArray());
          BigInteger g = dsskey.getParams().getG();
          dos.writeInt(g.toByteArray().length);
          dos.write(g.toByteArray());
          BigInteger y = dsskey.getY();
          dos.writeInt(y.toByteArray().length);
          dos.write(y.toByteArray());
        } else {
          throw new IllegalArgumentException("unknown key encoding " + key.getAlgorithm());
        }
        bytes = byteOs.toByteArray();
        this.pubKey = new String(Base64.encodeBase64(bytes));
      }

      public void setPubKey(String pubKey) throws Exception {
        this.pubKey = pubKey;
        bytes = Base64.decodeBase64(pubKey.getBytes());
        if (bytes == null)
          return;
        decodeType();
        if (keyType.equals("ssh-rsa")) {
          BigInteger e = decodeBigInt();
          BigInteger m = decodeBigInt();
          KeySpec spec = new RSAPublicKeySpec(m, e);
          key = KeyFactory.getInstance("RSA").generatePublic(spec);
        } else if (keyType.equals("ssh-dss")) {
          BigInteger p = decodeBigInt();
          BigInteger q = decodeBigInt();
          BigInteger g = decodeBigInt();
          BigInteger y = decodeBigInt();
          KeySpec spec = new DSAPublicKeySpec(y, p, q, g);
          key = KeyFactory.getInstance("DSA").generatePublic(spec);
        } else {
          throw new IllegalArgumentException("unknown type " + keyType);
        }
      }
    }

    final SshServer sshd = SshServer.setUpDefaultServer();
    final Map<ServerSession, PublicKey> sessionKeys = new HashMap();

    class AuthorizedKeys extends HashMap<String,AuthorizedKeyEntry> {
      private File file;


      public void load(File file) throws Exception {
        this.file = file;
        Scanner scanner = new Scanner(file).useDelimiter("\n");
        while (scanner.hasNext())
          decodePublicKey(scanner.next());
        scanner.close();
      }

      public void save() throws Exception {
        PrintWriter w = new PrintWriter(file);
        for (String username : keySet()) {
          AuthorizedKeyEntry entry = get(username);
          w.print(entry.keyType + " " + entry.pubKey + " " + username + "\n");
        }
        w.close();
      }

      public void put(String username, PublicKey key) {
        AuthorizedKeyEntry entry = new AuthorizedKeyEntry();
        try {
          entry.setPubKey(key);
        } catch (Exception e) {
          e.printStackTrace();
        }
        super.put(username,entry);
      }

      private void decodePublicKey(String keyLine) throws Exception {
        AuthorizedKeyEntry entry = new AuthorizedKeyEntry();
        String[] toks = keyLine.split(" ");
        String username = toks[toks.length-1];
        for (String part : toks) {
          if (part.startsWith("AAAA")) {
            entry.setPubKey(part);
            //bytes = Base64.decodeBase64(part.getBytes());
            break;
          }
        }
        super.put(username,entry);
      }
    };

    final AuthorizedKeys authenticUserKeys = new AuthorizedKeys(); // load authorized_keys
    File file = new File("authorized_keys");
    file.createNewFile(); // create if not exists
    authenticUserKeys.load(file);


    sshd.setPort(22);
    sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("key.ser"));

    sshd.setShellFactory(new ProcessShellFactory(new String[] { "cmd.exe "}));

    sshd.setPasswordAuthenticator(new PasswordAuthenticator() {
      public boolean authenticate(String username, String password, ServerSession session) {
        boolean authentic = false;
        try {
          new waffle.windows.auth.impl.WindowsAuthProviderImpl().logonUser(username,password);
          authentic = true;
          //authentic = username != null && username.equals(password+password); // obsecurity :)
          if (authentic) {
            PublicKey sessionKey = sessionKeys.get(session);
            if (sessionKey != null)
              authenticUserKeys.put(username, sessionKey); //save entry to authorized_keys
          }
        } catch (Exception e) {
          System.err.println(e);
        }
        return authentic;
      }
    });

    sshd.setPublickeyAuthenticator(new PublickeyAuthenticator() {
      public boolean authenticate(String username, PublicKey key, ServerSession session) {
        sessionKeys.put(session,key);
        return key.equals(authenticUserKeys.get(username).getPubKey());
      }
    });

    sshd.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(
      new UserAuthPublicKey.Factory()
      ,new UserAuthPassword.Factory()));

    sshd.setCommandFactory(new ScpCommandFactory());

    sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(
      new SftpSubsystem.Factory()));

    //workaround for apache sshd 10.0+ (putty)
    sshd.setKeyExchangeFactories(Arrays.<NamedFactory<KeyExchange>>asList(
      //new DHGEX256.Factory()
      //,new DHGEX.Factory()
      new ECDHP256.Factory()
      ,new ECDHP384.Factory()
      ,new ECDHP521.Factory()
      ,new DHG1.Factory()));

    Runtime.getRuntime().addShutdownHook(new Thread() {
      public void run() {
        try {
          authenticUserKeys.save();
          System.out.println("Stopping");
          sshd.stop();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    });

    System.out.println("Starting");    
    try {
      sshd.start();
      Thread.sleep(Long.MAX_VALUE);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  static public void main(String[] args) throws Exception {
    new SFTPServer().setupSftpServer();
  }
}

I tried doing MINA 0.10.1 on Windows with the above and fixed some issues, plus I need better authentication and PK support (still not recommended for production use):

import java.io.File;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.PrintWriter;

import java.util.Arrays;
import java.util.Map;
import java.util.HashMap;
import java.util.Scanner;

import java.math.BigInteger;

import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.interfaces.DSAPublicKey;

import java.security.KeyFactory;

import java.security.spec.KeySpec;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.RSAPublicKeySpec;

import org.apache.sshd.common.NamedFactory;

import org.apache.sshd.SshServer;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.command.ScpCommandFactory;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.PasswordAuthenticator;
import org.apache.sshd.server.PublickeyAuthenticator;
import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.server.sftp.SftpSubsystem;
import org.apache.sshd.server.shell.ProcessShellFactory;
import org.apache.sshd.server.UserAuth;
import org.apache.sshd.server.auth.UserAuthPassword;
import org.apache.sshd.server.auth.UserAuthPublicKey;

import org.apache.sshd.common.KeyExchange;
//import org.apache.sshd.server.kex.DHGEX;
//import org.apache.sshd.server.kex.DHGEX256;
import org.apache.sshd.server.kex.ECDHP256;
import org.apache.sshd.server.kex.ECDHP384;
import org.apache.sshd.server.kex.ECDHP521;
import org.apache.sshd.server.kex.DHG1;

import org.apache.mina.util.Base64;
/*
javac -classpath .;lib/sshd-core-0.10.1.jar;lib/mina-core-2.0.7.jar;lib/waffle-jna.jar;lib/guava-13.0.1.jar;lib/jna-platform-4.0.0.jar;lib/jna-4.0.0.jar SFTPServer.java
java -classpath .;lib/sshd-core-0.10.1.jar;lib/slf4j-simple-1.7.6.jar;lib/slf4j-api-1.6.6.jar;lib/mina-core-2.0.7.jar;lib/waffle-jna.jar;lib/guava-13.0.1.jar;lib/jna-platform-4.0.0.jar;lib/jna-4.0.0.jar SFTPServer
*/
public class SFTPServer {
  public void setupSftpServer() throws Exception {

    class AuthorizedKeyEntry {
      private String keyType;
      private String pubKey;

      private byte[] bytes;
      private int pos;
      private PublicKey key = null;

      private int decodeInt() {
        return ((bytes[pos++] & 0xFF) << 24) | ((bytes[pos++] & 0xFF) << 16)
                | ((bytes[pos++] & 0xFF) << 8) | (bytes[pos++] & 0xFF);
      }

      private BigInteger decodeBigInt() {
        int len = decodeInt();
        byte[] bigIntBytes = new byte[len];
        System.arraycopy(bytes, pos, bigIntBytes, 0, len);
        pos += len;
        return new BigInteger(bigIntBytes);
      }

      private void decodeType() {
        int len = decodeInt();
        keyType = new String(bytes, pos, len);
        pos += len;
      }

      public PublicKey getPubKey()  {
        return key;
      }

      public void setPubKey(PublicKey key) throws Exception {
        this.key = key;
        ByteArrayOutputStream byteOs = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(byteOs);
        if (key instanceof RSAPublicKey) {
          keyType = "ssh-rsa";
          dos.writeInt(keyType.getBytes().length);
          dos.write(keyType.getBytes());

          RSAPublicKey rsakey = (RSAPublicKey)key;
          BigInteger e = rsakey.getPublicExponent();
          dos.writeInt(e.toByteArray().length);
          dos.write(e.toByteArray());
          BigInteger m = rsakey.getModulus();
          dos.writeInt(m.toByteArray().length);
          dos.write(m.toByteArray());
        } else if (key instanceof DSAPublicKey) {
          keyType = "ssh-dss";
          dos.writeInt(keyType.getBytes().length);
          dos.write(keyType.getBytes());

          DSAPublicKey dsskey = (DSAPublicKey)key;
          BigInteger p = dsskey.getParams().getP();
          dos.writeInt(p.toByteArray().length);
          dos.write(p.toByteArray());
          BigInteger q = dsskey.getParams().getQ();
          dos.writeInt(q.toByteArray().length);
          dos.write(q.toByteArray());
          BigInteger g = dsskey.getParams().getG();
          dos.writeInt(g.toByteArray().length);
          dos.write(g.toByteArray());
          BigInteger y = dsskey.getY();
          dos.writeInt(y.toByteArray().length);
          dos.write(y.toByteArray());
        } else {
          throw new IllegalArgumentException("unknown key encoding " + key.getAlgorithm());
        }
        bytes = byteOs.toByteArray();
        this.pubKey = new String(Base64.encodeBase64(bytes));
      }

      public void setPubKey(String pubKey) throws Exception {
        this.pubKey = pubKey;
        bytes = Base64.decodeBase64(pubKey.getBytes());
        if (bytes == null)
          return;
        decodeType();
        if (keyType.equals("ssh-rsa")) {
          BigInteger e = decodeBigInt();
          BigInteger m = decodeBigInt();
          KeySpec spec = new RSAPublicKeySpec(m, e);
          key = KeyFactory.getInstance("RSA").generatePublic(spec);
        } else if (keyType.equals("ssh-dss")) {
          BigInteger p = decodeBigInt();
          BigInteger q = decodeBigInt();
          BigInteger g = decodeBigInt();
          BigInteger y = decodeBigInt();
          KeySpec spec = new DSAPublicKeySpec(y, p, q, g);
          key = KeyFactory.getInstance("DSA").generatePublic(spec);
        } else {
          throw new IllegalArgumentException("unknown type " + keyType);
        }
      }
    }

    final SshServer sshd = SshServer.setUpDefaultServer();
    final Map<ServerSession, PublicKey> sessionKeys = new HashMap();

    class AuthorizedKeys extends HashMap<String,AuthorizedKeyEntry> {
      private File file;


      public void load(File file) throws Exception {
        this.file = file;
        Scanner scanner = new Scanner(file).useDelimiter("\n");
        while (scanner.hasNext())
          decodePublicKey(scanner.next());
        scanner.close();
      }

      public void save() throws Exception {
        PrintWriter w = new PrintWriter(file);
        for (String username : keySet()) {
          AuthorizedKeyEntry entry = get(username);
          w.print(entry.keyType + " " + entry.pubKey + " " + username + "\n");
        }
        w.close();
      }

      public void put(String username, PublicKey key) {
        AuthorizedKeyEntry entry = new AuthorizedKeyEntry();
        try {
          entry.setPubKey(key);
        } catch (Exception e) {
          e.printStackTrace();
        }
        super.put(username,entry);
      }

      private void decodePublicKey(String keyLine) throws Exception {
        AuthorizedKeyEntry entry = new AuthorizedKeyEntry();
        String[] toks = keyLine.split(" ");
        String username = toks[toks.length-1];
        for (String part : toks) {
          if (part.startsWith("AAAA")) {
            entry.setPubKey(part);
            //bytes = Base64.decodeBase64(part.getBytes());
            break;
          }
        }
        super.put(username,entry);
      }
    };

    final AuthorizedKeys authenticUserKeys = new AuthorizedKeys(); // load authorized_keys
    File file = new File("authorized_keys");
    file.createNewFile(); // create if not exists
    authenticUserKeys.load(file);


    sshd.setPort(22);
    sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("key.ser"));

    sshd.setShellFactory(new ProcessShellFactory(new String[] { "cmd.exe "}));

    sshd.setPasswordAuthenticator(new PasswordAuthenticator() {
      public boolean authenticate(String username, String password, ServerSession session) {
        boolean authentic = false;
        try {
          new waffle.windows.auth.impl.WindowsAuthProviderImpl().logonUser(username,password);
          authentic = true;
          //authentic = username != null && username.equals(password+password); // obsecurity :)
          if (authentic) {
            PublicKey sessionKey = sessionKeys.get(session);
            if (sessionKey != null)
              authenticUserKeys.put(username, sessionKey); //save entry to authorized_keys
          }
        } catch (Exception e) {
          System.err.println(e);
        }
        return authentic;
      }
    });

    sshd.setPublickeyAuthenticator(new PublickeyAuthenticator() {
      public boolean authenticate(String username, PublicKey key, ServerSession session) {
        sessionKeys.put(session,key);
        return key.equals(authenticUserKeys.get(username).getPubKey());
      }
    });

    sshd.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(
      new UserAuthPublicKey.Factory()
      ,new UserAuthPassword.Factory()));

    sshd.setCommandFactory(new ScpCommandFactory());

    sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(
      new SftpSubsystem.Factory()));

    //workaround for apache sshd 10.0+ (putty)
    sshd.setKeyExchangeFactories(Arrays.<NamedFactory<KeyExchange>>asList(
      //new DHGEX256.Factory()
      //,new DHGEX.Factory()
      new ECDHP256.Factory()
      ,new ECDHP384.Factory()
      ,new ECDHP521.Factory()
      ,new DHG1.Factory()));

    Runtime.getRuntime().addShutdownHook(new Thread() {
      public void run() {
        try {
          authenticUserKeys.save();
          System.out.println("Stopping");
          sshd.stop();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    });

    System.out.println("Starting");    
    try {
      sshd.start();
      Thread.sleep(Long.MAX_VALUE);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  static public void main(String[] args) throws Exception {
    new SFTPServer().setupSftpServer();
  }
}
很糊涂小朋友 2024-09-13 10:46:37

查看 SSHTools (j2ssh)。它包括客户端和服务器。

然而,轮询目录并不是一个坏主意 - 它可能比使用 j2ssh 设置您自己的 SFTP 服务器可靠得多。我已经记不清我遇到过多少个进行这种轮询的应用程序,而且它通常工作得很好。

Take a look at SSHTools (j2ssh). It includes a client and server.

However polling a directory isn't that bad an idea - it's probably much more reliable than setting up your own SFTP server using j2ssh. I've lost count of the number of applications I've encountered that do this kind of polling, and it usually works quite well.

吝吻 2024-09-13 10:46:37

只是为了完整起见 - 我们维护的 SecureBlackbox 库提供了创建您自己的 SSH/SFTP 的类Java 中的服务器(包括 Android)。

Just for completeness - the SecureBlackbox library, which we maintain, offers classes to create your own SSH/SFTP server in Java (including Android).

萌辣 2024-09-13 10:46:37

我正在使用 jftp http://j-ftp.sourceforge.net/
从 j-ftp-*.tgz/j-ftp/dist 中提取 jftp.jar
唯一的问题 - 他们将 apache 类放入 jar 中(所以我必须手动删除 common-httpclient、log4j 包以避免依赖冲突)

i am using jftp http://j-ftp.sourceforge.net/
extract jftp.jar from j-ftp-*.tgz/j-ftp/dist
the only problem - they put apache classes inside there jar (so i have to remove common-httpclient, log4j packages manually to avoid conflicting dependencies)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文