java安全机制其实有点不安全

看下面的这段代码,摘自《Java Examples in a Nutsbell》(java实例技术手册):

就是一个简单的通用的多线程服务器

这个例子可以通过配置参数:

java je3.net.Server -control www 3333 je3.net.Server$HTTPMirror 5555

来启动,然后再ie中输入: http://localhost:5555 就可以看到效果。

写一个policy文件放在同根目录下:叫server.policy

grant{
permission java.net.SocketPermission “*:1024-4444”,”connect,accept”;
permission java.io.FilePermission “E://workspace//j2ee1.3//-“, “read”;
};

下面加上jvm虚拟机参数

java -Djava.security.manager -Djava.security.policy=server.policy

je3.net.Server -control www 3333 je3.net.Server$HTTPMirror 5555再次启动。

按道理,本不应该启动。因为端口5555并没有得到连接许可。但是很可惜输入 http://localhost:5555 还是可以看到结果。因为在java的sdk中暗含了java.policy文件。那就把它改为-
Djava.security.policy==server.policy应该就可以了。结果跑不了了。原因就在policy文件中的permission
java.net.SocketPermission “:1024-4444”,”connect,accept”; 其实,我一直不太清楚listen
,accept,connect的区别在什么地方。但是这里的例子说明你只用permission java.net.SocketPermission “
:10
24-4444”,”listen”;就可以了。端口该闭的就闭了。如果用accept和connect反而没有什么用。不知道java的安全性高在什么地方。因为j
ava.policy文件中从1024以上的端口全都使用了listen。所以以后要配置端口时一定要注意。

/*/ /

  • Copyright (c) 2004 David Flanagan. All rights reserved.
  • This code is from the book Java Examples in a Nutshell, 3nd Edition.
  • It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
  • You may study, use, and modify it for any non-commercial purpose,
  • including teaching and use in open-source projects.
  • You may distribute it non-commercially as long as you retain this notice.
  • For a commercial use license, or to purchase the book,
  • please visit http://www.davidflanagan.com/javaexamples3.
    /

    package je3.net;

    import java.io.
    ;

    import java.net. ;

    import java.util.
    ;

    import java.util.logging. ;

    /**
    / /**
  • This class is a generic framework for a flexible, multi-threaded server.
  • It listens on any number of specified ports, and, when it receives a
  • connection on a port, passes input and output streams to a specified Service
  • object which provides the actual service. It can limit the number of
  • concurrent connections, and logs activity to a specified stream.
    /
    public class Server … {
    / */ /
  • A main() method for running the server as a standalone program. The
  • command-line arguments to the program should be pairs of servicenames
  • and port numbers. For each pair, the program will dynamically load the
  • named Service class, instantiate it, and tell the server to provide
  • that Service on the specified port. The special -control argument
  • should be followed by a password and port, and will start special
  • server control service running on the specified port, protected by the
  • specified password.
    /
    public static void main(String[] args) … {
    try … {

    if (args.length < 2 ) // Check number of arguments

    throw new IllegalArgumentException( “ Must specify a service “ );


    // Create a server object that has a limit of 10 concurrent

    // connections, and logs to a Logger at the Level.INFO level

    // Prior to Java 1.4 we did this: new Server(System.out, 10);

    Server s = new Server(Logger.getLogger(Server. class .getName()),

    Level.INFO, 10 );


    // Parse the argument list

    int i = 0 ;
    while (i < args.length) … {
    if (args[i].equals( “ -control “ )) … {
    // Handle the -control arg

    i ++ ;

    String password = args[i ++ ];

    int port = Integer.parseInt(args[i ++ ]);

    // add control service

    s.addService( new Control(s, password), port);
    }
    else … {

    // Otherwise start a named service on the specified port.

    // Dynamically load and instantiate a Service class

    String serviceName = args[i ++ ];

    Class serviceClass = Class.forName(serviceName);

    Service service = (Service)serviceClass.newInstance();

    int port = Integer.parseInt(args[i ++ ]);

    s.addService(service, port);
    }
    }
    }
    catch (Exception e) … { // Display a message
    if anything goes wrong

    System.err.println( “ Server: “ + e);

    System.err.println( “ Usage: java Server “ +

    “ [-control ] “ +

    “ [ … ] “ );

    System.exit( 1 );
    }
    }


    // This is the state for the server

    Map services; // Hashtable mapping ports to Listeners

    Set connections; // The set of current connections

    int maxConnections; // The concurrent connection limit

    ThreadGroup threadGroup; // The threadgroup for all our threads


    // This class was originally written to send logging output to a stream.

    // It has been retrofitted to also support the java.util.logging API of

    // Java 1.4. You can use either, neither, or both.

    PrintWriter logStream; // Where we send our logging output to

    Logger logger; // A Java 1.4 logging destination

    Level logLevel; // the level to log messages at

    / */ /
  • This is the Server() constructor. It must be passed a stream
  • to send log output to (may be null), and the limit on the number of
  • concurrent connections.
    /
    public Server(OutputStream logStream, int
    maxConnections) … {

    this (maxConnections);

    setLogStream(logStream);

    log( “ Starting server “ );
    }

    / */ /
  • This constructor added to support logging with the Java 1.4 Logger class
    /
    public Server(Logger logger, Level logLevel, int
    maxConnections) … {

    this (maxConnections);

    setLogger(logger, logLevel);

    log( “ Starting server “ );
    }

    / */ /
  • This constructor supports no logging
    /
    public Server( int maxConnections) … {

    threadGroup = new ThreadGroup(Server. class .getName());

    this .maxConnections = maxConnections;

    services = new HashMap();

    connections = new HashSet(maxConnections);
    }

    / */ /
  • A public method to set the current logging stream. Pass null
  • to turn logging off.
    /
    public synchronized void
    setLogStream(OutputStream out) … {

    if (out != null ) logStream = new PrintWriter(out);

    else logStream = null ;
    }

    / */ /
  • Set the current Logger and logging level. Pass null to turn logging off.
    /
    public synchronized void setLogger(Logger
    logger, Level level) … {

    this .logger = logger;

    this .logLevel = level;
    }

    / */ / Write the specified string to the log
    /
    protected synchronized void log(String s) …
    {

    if (logger != null ) logger.log(logLevel, s);
    if (logStream != null ) … {

    logStream.println( “ [ “ + new Date() + “ ] “ + s);

    logStream.flush();
    }
    }
    /**
    / / Write the specified object to the log
    */
    protected void log(Object o) … {
    log(o.toString()); }

    /
    / /*
  • This method makes the server start providing a new service.
  • It runs the specified Service object on the specified port.
    /

    public synchronized void addService(Service service, int port)

    throws IOException
    … {

    Integer key = new Integer(port); // the hashtable key

    // Check whether a service is already on that port

    if (services.get(key) != null )

    throw new IllegalArgumentException( “ Port “ + port +

    “ already in use. “ );

    // Create a Listener object to listen for connections on the port

    Listener listener = new Listener(threadGroup, port, service);

    // Store it in the hashtable

    services.put(key, listener);

    // Log it

    log( “ Starting service “ + service.getClass().getName() +

    “ on port “ + port);

    // Start the listener running.

    listener.start();
    }

    / */ /
  • This method makes the server stop providing a service on a port.
  • It does not terminate any pending connections to that service, merely
  • causes the server to stop accepting new connections
    /
    public synchronized void removeService( int
    port) … {

    Integer key = new Integer(port); // hashtable key

    // Look up the Listener object for the port in the hashtable

    final Listener listener = (Listener) services.get(key);

    if (listener == null ) return ;

    // Ask the listener to stop

    listener.pleaseStop();

    // Remove it from the hashtable

    services.remove(key);

    // And log it.

    log( “ Stopping service “ + listener.service.getClass().getName() +

    “ on port “ + port);
    }

    / */ /
  • This nested Thread subclass is a “listener”. It listens for
  • connections on a specified port (using a ServerSocket) and when it gets
  • a connection request, it calls the servers addConnection() method to
  • accept (or reject) the connection. There is one Listener for each
  • Service being provided by the Server.
    /
    public class Listener extends Thread … {

    ServerSocket listen_socket; // The socket to listen for connections

    int port; // The port we’re listening on

    Service service; // The service to provide on that port

    volatile boolean stop = false ; // Whether we’ve been asked to stop

    / */ /
  • The Listener constructor creates a thread for itself in the
  • threadgroup. It creates a ServerSocket to listen for connections
  • on the specified port. It arranges for the ServerSocket to be
  • interruptible, so that services can be removed from the server.
    /

    public Listener(ThreadGroup group, int port, Service service)

    throws IOException
    … {

    super (group, “ Listener: “ + port);

    listen_socket = new ServerSocket(port);

    // give it a non-zero timeout so accept() can be interrupted

    listen_socket.setSoTimeout( 5000 );

    this .port = port;

    this .service = service;
    }

    / */ /
  • This is the polite way to get a Listener to stop accepting
  • connections
    */
    public void pleaseStop() … {

    this .stop = true ; // Set the stop flag

    this .interrupt(); // Stop blocking in accept()
    try … { listen_socket.close(); } // Stop
    listening.
    catch (IOException e) … {}
    }

    /
    / /*
  • A Listener is a Thread, and this is its body.
  • Wait for connection requests, accept them, and pass the socket on
  • to the addConnection method of the server.
    /
    public void run() … {
    while ( ! stop) … { // loop until we’re
    asked to stop.
    try … {

    Socket client = listen_socket.accept();

    addConnection(client, service);
    }
    catch (InterruptedIOException e) … {}
    catch (IOException e) … {log(e);}
    }
    }
    }

    / */ /
  • This is the method that Listener objects call when they accept a
  • connection from a client. It either creates a Connection object
  • for the connection and adds it to the list of current connections,
  • or, if the limit on connections has been reached, it closes the
  • connection.
    /
    protected synchronized void addConnection(Socket
    s, Service service) … {

    // If the connection limit has been reached
    if (connections.size() >= maxConnections) …
    {
    try … {

    // Then tell the client it is being rejected.

    PrintWriter out = new PrintWriter(s.getOutputStream());

    out.print( “ Connection refused; “ +

    “ the server is busy; please try again later. “ );

    out.flush();

    // And close the connection to the rejected client.

    s.close();

    // And log it, of course

    log( “ Connection refused to “ +

    s.getInetAddress().getHostAddress() +

    “ : “ + s.getPort() + “ : max connections reached. “ );
    } catch (IOException e) … {log(e);}
    }
    else … { // Otherwise, if the limit has not
    been reached

    // Create a Connection thread to handle this connection

    Connection c = new Connection(s, service);

    // Add it to the list of current connections

    connections.add(c);

    // Log this new connection

    log( “ Connected to “ + s.getInetAddress().getHostAddress() +

    “ : “ + s.getPort() + “ on port “ + s.getLocalPort() +

    “ for service “ + service.getClass().getName());

    // And start the Connection thread to provide the service

    c.start();
    }
    }

    / */ /
  • A Connection thread calls this method just before it exits. It removes
  • the specified Connection from the set of connections.
    /
    protected synchronized void
    endConnection(Connection c) … {

    connections.remove(c);

    log( “ Connection to “ + c.client.getInetAddress().getHostAddress() +

    “ : “ + c.client.getPort() + “ closed. “ );
    }

    / */ / Change the current connection limit
    /
    public synchronized void setMaxConnections( int
    max) … {

    maxConnections = max;
    }

    /**
    / /**
  • This method displays status information about the server on the
  • specified stream. It can be used for debugging, and is used by the
  • Control service later in this example.
    /
    public synchronized void
    displayStatus(PrintWriter out) … {

    // Display a list of all Services that are being provided

    Iterator keys = services.keySet().iterator();
    while (keys.hasNext()) … {

    Integer port = (Integer) keys.next();

    Listener listener = (Listener) services.get(port);

    out.print( “ SERVICE “ + listener.service.getClass().getName()

    + “ ON PORT “ + port + “ “ );
    }


    // Display the current connection limit

    out.print( “ MAX CONNECTIONS: “ + maxConnections + “ “ );


    // Display a list of all current connections

    Iterator conns = connections.iterator();
    while (conns.hasNext()) … {

    Connection c = (Connection)conns.next();

    out.print( “ CONNECTED TO “ +

    c.client.getInetAddress().getHostAddress() +

    “ : “ + c.client.getPort() + “ ON PORT “ +

    c.client.getLocalPort() + “ FOR SERVICE “ +

    c.service.getClass().getName() + “ “ );
    }
    }

    / */ /
  • This class is a subclass of Thread that handles an individual
  • connection between a client and a Service provided by this server.
  • Because each such connection has a thread of its own, each Service can
  • have multiple connections pending at once. Despite all the other
  • threads in use, this is the key feature that makes this a
  • multi-threaded server implementation.
    /
    public class Connection extends Thread … {

    Socket client; // The socket to talk to the client through

    Service service; // The service being provided to that client

    / */ /
  • This constructor just saves some state and calls the superclass
  • constructor to create a thread to handle the connection. Connection
  • objects are created by Listener threads. These threads are part of
  • the server’s ThreadGroup, so all Connection threads are part of that
  • group, too.
    /
    public Connection(Socket client, Service service)
    … {

    super ( “ Server.Connection: “ +

    client.getInetAddress().getHostAddress() +

    “ : “ + client.getPort());

    this .client = client;

    this .service = service;
    }

    / */ /
  • This is the body of each and every Connection thread.
  • All it does is pass the client input and output streams to the
  • serve() method of the specified Service object. That method is
  • responsible for reading from and writing to those streams to
  • provide the actual service. Recall that the Service object has
  • been passed from the Server.addService() method to a Listener
  • object to the addConnection() method to this Connection object, and
  • is now finally being used to provide the service. Note that just
  • before this thread exits it always calls the endConnection() method
  • to remove itself from the set of connections
    /
    public void run() … {
    try … {

    InputStream in = client.getInputStream();

    OutputStream out = client.getOutputStream();

    service.serve(in, out);
    }
    catch (IOException e) … {log(e);}
    finally … { endConnection( this ); }
    }
    }

    / */ /
  • Here is the Service interface that we have seen so much of. It defines
  • only a single method which is invoked to provide the service. serve()
  • will be passed an input stream and an output stream to the client. It
  • should do whatever it wants with them, and should close them before
  • returning.
  • All connections through the same port to this service share a single
  • Service object. Thus, any state local to an individual connection must
  • be stored in local variables within the serve() method. State that
  • should be global to all connections on the same port should be stored
  • in instance variables of the Service class. If the same Service is
  • running on more than one port, there will typically be different
  • Service instances for each port. Data that should be global to all
  • connections on any port should be stored in static variables.
  • Note that implementations of this interface must have a no-argument
  • constructor if they are to be dynamically instantiated by the main()
  • method of the Server class.
    /
    public interface Service … {

    public void serve(InputStream in, OutputStream out) throws IOException;
    }

    / */ /
  • A very simple service. It displays the current time on the server
  • to the client, and closes the connection.
    /
    public static class Time implements Service
    … {
    public void serve(InputStream i, OutputStream o)
    throws IOException … {

    PrintWriter out = new PrintWriter(o);

    out.print( new Date() + “ “ );

    out.close();

    i.close();
    }
    }

    / */ /
  • This is another example service. It reads lines of input from the
  • client, and sends them back, reversed. It also displays a welcome
  • message and instructions, and closes the connection when the user
  • enters a ‘.’ on a line by itself.
    /
    public static class Reverse implements Service
    … {
    public void serve(InputStream i, OutputStream o)
    throws IOException … {

    BufferedReader in = new BufferedReader( new InputStreamReader(i));

    PrintWriter out =

    new PrintWriter( new BufferedWriter( new OutputStreamWriter(o)));

    out.print( “ Welcome to the line reversal server. “ );

    out.print( “ Enter lines. End with a ‘.’ on a line by itself. “ );
    for (;;) … {

    out.print( “ > “ );

    out.flush();

    String line = in.readLine();

    if ((line == null ) || line.equals( “ . “ )) break ;

    for ( int j = line.length() - 1 ; j >= 0 ; j -- )

    out.print(line.charAt(j));

    out.print( “ “ );
    }

    out.close();

    in.close();
    }
    }

    / */ /
  • This service is an HTTP mirror, just like the HttpMirror class
  • implemented earlier in this chapter. It echos back the client’s
  • HTTP request
    /
    public static class HTTPMirror implements
    Service … {
    public void serve(InputStream i, OutputStream o)
    throws IOException … {

    BufferedReader in = new BufferedReader( new InputStreamReader(i));

    PrintWriter out = new PrintWriter(o);

    out.print( “ HTTP/1.0 200 “ );

    out.print( “ Content-Type: text/plain “ );

    String line;
    while ((line = in.readLine()) != null ) …
    {

    if (line.length() == 0 ) break ;

    out.print(line + “ “ );
    }

    out.close();

    in.close();
    }
    }

    / */ /
  • This service demonstrates how to maintain state across connections by
  • saving it in instance variables and using synchronized access to those
  • variables. It maintains a count of how many clients have connected and
  • tells each client what number it is
    /
    public static class UniqueID implements
    Service … {

    public int id = 0 ;
    public synchronized int nextId() … { return
    id ++ ; }
    public void serve(InputStream i, OutputStream o)
    throws IOException … {

    PrintWriter out = new PrintWriter(o);

    out.print( “ You are client #: “ + nextId() + “ “ );

    out.close();

    i.close();
    }
    }

    / */ /
  • This is a non-trivial service. It implements a command-based protocol
  • that gives password-protected runtime control over the operation of the
  • server. See the main() method of the Server class to see how this
  • service is started.
  • The recognized commands are:
  • password: give password; authorization is required for most commands
  • add: dynamically add a named service on a specified port
  • remove: dynamically remove the service running on a specified port
  • max: change the current maximum connection limit.
  • status: display current services, connections, and connection limit
  • help: display a help message
  • quit: disconnect
  • This service displays a prompt, and sends all of its output to the user
  • in capital letters. Only one client is allowed to connect to this
  • service at a time.
    /
    public static class Control implements Service
    … {

    Server server; // The server we control

    String password; // The password we require

    boolean connected = false ; // Whether a client is already connected

    / */ /
  • Create a new Control service. It will control the specified Server
  • object, and will require the specified password for authorization
  • Note that this Service does not have a no argument constructor,
  • which means that it cannot be dynamically instantiated and added as
  • the other, generic services above can be.
    /
    public Control(Server server, String password)
    … {

    this .server = server;

    this .password = password;
    }

    / */ /
  • This is the serve method that provides the service. It reads a
  • line the client, and uses java.util.StringTokenizer to parse it
  • into commands and arguments. It does various things depending on
  • the command.
    /
    public void serve(InputStream i, OutputStream o)
    throws IOException … {

    // Setup the streams

    BufferedReader in = new BufferedReader( new InputStreamReader(i));

    PrintWriter out = new PrintWriter(o);

    String line; // For reading client input lines

    // Has the user has given the password yet?

    boolean authorized = false ;


    // If there is already a client connected to this service, display

    // a message to this client and close the connection. We use a

    // synchronized block to prevent a race condition.
    synchronized ( this ) … {
    if (connected) … {

    out.print( “ ONLY ONE CONTROL CONNECTION ALLOWED. “ );

    out.close();

    return ;
    }

    else connected = true ;
    }


    // This is the main loop: read a command, parse it, and handle it
    for (;;) … { // infinite loop

    out.print( “ > “ ); // Display a prompt

    out.flush(); // Make it appear right away

    line = in.readLine(); // Get the user’s input

    if (line == null ) break ; // Quit if we get EOF.
    try … {

    // Use a StringTokenizer to parse the user’s command

    StringTokenizer t = new StringTokenizer(line);

    if ( ! t.hasMoreTokens()) continue ; // if input was empty

    // Get first word of the input and convert to lower case

    String command = t.nextToken().toLowerCase();

    // Now compare to each of the possible commands, doing the

    // appropriate thing for each command
    if (command.equals( “ password “ )) … {
    // Password command

    String p = t.nextToken(); // Get the next word
    if (p.equals( this .password)) … { // Is
    it the password?

    out.print( “ OK “ ); // Say so

    authorized = true ; // Grant authorization
    }

    else out.print( “ INVALID PASSWORD “ );
    }
    else if (command.equals( “ add “ )) … {
    // Add Service command

    // Check whether password has been given

    if ( ! authorized) out.print( “ PASSWORD REQUIRED “ );
    else … {

    // Get the name of the service and try to

    // dynamically load and instantiate it.

    // Exceptions will be handled below

    String serviceName = t.nextToken();

    Class serviceClass = Class.forName(serviceName);

    Service service;
    try … {

    service = (Service)serviceClass.newInstance();
    }
    catch (NoSuchMethodError e) … {

    throw new IllegalArgumentException(

    “ Service must have a “ +

    “ no-argument constructor “ );
    }

    int port = Integer.parseInt(t.nextToken());

    // If no exceptions occurred, add the service

    server.addService(service, port);

    out.print( “ SERVICE ADDED “ ); // acknowledge
    }
    }
    else if (command.equals( “ remove “ )) …
    { // Remove service

    if ( ! authorized) out.print( “ PASSWORD REQUIRED “ );
    else … {

    int port = Integer.parseInt(t.nextToken());

    server.removeService(port); // remove the service

    out.print( “ SERVICE REMOVED “ ); // acknowledge
    }
    }
    else if (command.equals( “ max “ )) … {
    // Set connection limit

    if ( ! authorized) out.print( “ PASSWORD REQUIRED “ );
    else … {

    int max = Integer.parseInt(t.nextToken());

    server.setMaxConnections(max);

    out.print( “ MAX CONNECTIONS CHANGED “ );
    }
    }
    else if (command.equals( “ status “ )) …
    { // Status Display

    if ( ! authorized) out.print( “ PASSWORD REQUIRED “ );

    else server.displayStatus(out);
    }
    else if (command.equals( “ help “ )) … {
    // Help command

    // Display command syntax. Password not required

    out.print( “ COMMANDS: “ +

    “ password “ +

    “ add “ +

    “ remove “ +

    “ max “ +

    “ status “ +

    “ help “ +

    “ quit “ );
    }

    else if (command.equals( “ quit “ )) break ; // Quit command.

    else out.print( “ UNRECOGNIZED COMMAND “ ); // Error
    }
    catch (Exception e) … {

    // If an exception occurred during the command, print an

    // error message, then output details of the exception.

    out.print( “ ERROR WHILE PARSING OR EXECUTING COMMAND: “ +

    e + “ “ );
    }
    }

    // Finally, when the loop command loop ends, close the streams

    // and set our connected flag to false so that other clients can

    // now connect.

    connected = false ;

    out.close();

    in.close();
    }
    }
    }
  • 本文作者: 帐前卒
  • 本文链接: http://chillyc.info/2007/1532313/
  • 版权声明: 本博客所有文章除特别声明外,只能复制超链接地址,且必须注明出处!