I know that command line interfaces like Git and others are able to hide input from a user (useful for passwords). Is there a way to programmtically do this in Java? I'm taking password input from a user and I would like their input to be "hidden" on that particular line (but not on all of them). Here's my code for it (though I doubt it would be helpful...)
try (Scanner input = new Scanner(System.in)) {
//I'm guessing it'd probably be some property you set on the scanner or System.in right here...
System.out.print("Please input the password for " + name + ": ");
password = input.nextLine();
}
Try java.io.Console.readPassword. You'll have to be running at least Java 6 though.
/**
* Reads a password or passphrase from the console with echoing disabled
*
* #throws IOError
* If an I/O error occurs.
*
* #return A character array containing the password or passphrase read
* from the console, not including any line-termination characters,
* or <tt>null</tt> if an end of stream has been reached.
*/
public char[] readPassword() {
return readPassword("");
}
Beware though, this doesn't work with the Eclipse console. You'll have to run the program from a true console/shell/terminal/prompt to be able to test it.
Yes can be done. This is called Command-Line Input Masking. You can implement this easily.
You can uses a separate thread to erase the echoed characters as they are being entered, and replaces them with asterisks. This is done using the EraserThread class shown below
import java.io.*;
class EraserThread implements Runnable {
private boolean stop;
/**
*#param The prompt displayed to the user
*/
public EraserThread(String prompt) {
System.out.print(prompt);
}
/**
* Begin masking...display asterisks (*)
*/
public void run () {
stop = true;
while (stop) {
System.out.print("\010*");
try {
Thread.currentThread().sleep(1);
} catch(InterruptedException ie) {
ie.printStackTrace();
}
}
}
/**
* Instruct the thread to stop masking
*/
public void stopMasking() {
this.stop = false;
}
}
With using this thread
public class PasswordField {
/**
*#param prompt The prompt to display to the user
*#return The password as entered by the user
*/
public static String readPassword (String prompt) {
EraserThread et = new EraserThread(prompt);
Thread mask = new Thread(et);
mask.start();
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String password = "";
try {
password = in.readLine();
} catch (IOException ioe) {
ioe.printStackTrace();
}
// stop masking
et.stopMasking();
// return the password entered by the user
return password;
}
}
This Link discuss in details.
JLine 2 may be of interest. As well as character masking, it'll provide command-line completion, editing and history facilities. Consequently it's very useful for a CLI-based Java tool.
To mask your input:
String password = new jline.ConsoleReader().readLine(new Character('*'));
There is :
Console cons;
char[] passwd;
if ((cons = System.console()) != null &&
(passwd = cons.readPassword("[%s]", "Password:")) != null) {
...
java.util.Arrays.fill(passwd, ' ');
}
source
but I don't think this works with an IDE like Eclipse because the program is run as a background process rather than a top level process with a console window.
Another approach is to use the JPasswordField in swing with the accompanying actionPerformed method:
public void actionPerformed(ActionEvent e) {
...
char [] p = pwdField.getPassword();
}
source
Here is an example using console.readPassword(...); in an IDE. I use Netbeans. Note: In your IDE, Scanner will be used and it will show the password!. In the console, console.readPassword(..) will be used and it will not show the password!.
public static void main(String[] args) {
//The jar needs to be run from the terminal for console to work.
Scanner input = new Scanner(System.in);
Console console = System.console();
String username = "";
String password = "";
if (console == null)
{
System.out.print("Enter username: ");
username = input.nextLine();
System.out.print("Enter password: ");
password = input.nextLine();
}
else
{
username = console.readLine("Enter username: ");
password = new String(console.readPassword("Enter password: "));
}
//I use the scanner for all other input in the code!
//I do not know if there are any pitfalls associated with using the Scanner and console in this manner!
}
Note: I do not know if there are any pitfalls associated with using the Scanner and console in this manner!
The class Console has a method readPassword() that might solve your problem.
Related
I have written a quite basic login system in Java. Theoretically speaking, everything should work and function properly. However, when I run the program and enter the username and password, it outputs an exception.
Here's my code:
import java.util.*;
/**
* A basic login system
* #author Jamie <jamie#jamie.no>
*/
public class LoginSystem
{
public static boolean isValidated = false;
private static String userName = "";
private static String password = "";
public static void main(String[] args) {
runConsole();
}
public static void runConsole() {
Scanner console = new Scanner(System.in);
for (int i = 0; i < 3 && !isValidated; i++) {
System.out.println("You have entered the following username: ");
userName = console.nextLine();
System.out.println("You have entered the following password: ");
password = console.nextLine();
isValidated = AuthenticateUser(userName, password);
}
if (isValidated) {
System.out.println("Access Granted. User is authenticated.");
} else {
System.out.println("Unauthorized Access.");
}
}
/**
* User authentication constructor
* #param userName The username.
* #param password The password.
* #return true if user is validated.
*/
public static boolean AuthenticateUser(String userName, String password) {
return (userName.equalsIgnoreCase("User1") && password.equals("Pass1"));
}
}
Screenshot of the exception msg:
From the documentation:
public class NoSuchElementException extends RuntimeException Thrown
by the nextElement method of an Enumeration to indicate that there are
no more elements in the enumeration.
However, when I tried your code on Eclipse, it's running free of errors.
But when I tried it on Code Playground, it gave me the same error.
You have entered the following username:
You have entered the following password:
Exception in thread "main" java.util.NoSuchElementException: No line found
at java.util.Scanner.nextLine(Unknown Source)
at LoginSystem.runConsole(LoginSystem.java:25)
at LoginSystem.main(LoginSystem.java:11)
It seems it's a bug in their system. Because you need to input the userName and password for three loops in advance in one go at the very beginning of the run.
I went around this by wrapping the scan with hasNextLinewhich Returns true if there is another line in the input of this scanner.
So, it becomes:
if(console.hasNextLine()){
userName = console.nextLine();
}
if(console.hasNextLine()){
password = console.nextLine();
}
I am writing a basic program that has 3 menu options: Create User, Sign In, and Exit. The user can then choose one of these menu options.
If they choose Create User, they will be prompted to enter a User ID and password (which must follow patterns), and will then check against a .DAT file to ensure the User ID has not already been taken. After successful completion, the program will write the new User ID and password to the end of the .DAT file.
If they choose Sign In, they will be prompted to enter their User ID, followed by their Password, and the program will then read the .DAT file to validate they are on record.
Choosing exit will display a message, "You have signed out."
I am fairly new at java programming just as a forewarning.
Issues I am encountering with my code:
Choosing new user does not append to the .DAT file
Choosing Sign In - program does not seem to correctly check .DAT file because even-
though I am entering an existing account information it still gives my error "Invalid User ID."
import java.io.*;
import java.util.*;
/**
* This program will utilize a menu structure and validate input if the user doesn't choose
* a correct option. Writes a new ID and password to .dat file when user chooses to
* create new user from menu.
*
* #author CMS
* #Date 7/28/2014
*/
public class Passwordv2 {
static boolean answer = true;
static final String MENUANSW = "[1-3]{1}", USERID = "[A-Z,a-z]{6}-[0-9]{2}"; //, PASSWORD = "";
static String iMenuOption="", iID, recPassword, recUserID, password;
static Scanner scanner,scannerDat;
static PrintWriter pw;
public static void main(String[] args) {
init();
while (answer == true) {
menu();
if (iMenuOption.equals("1")) createUser();
else
if (iMenuOption.equals("2")) signIn();
else {answer = false;}
}
closing();
} // end of main
public static void init(){
//User input scanner
scanner = new Scanner(System.in);
//PrintWriter
try {
pw = new PrintWriter(new FileOutputStream (new File ("account.dat"),true) );
}
catch(FileNotFoundException ex) {
}
} // end of INIT
public static String menu(){
do {System.out.println("Please select from the following:");
System.out.println("1. Create a New User");
System.out.println("2. Sign in");
System.out.println("3. Exit");
iMenuOption = scanner.next();
answer = isValMenuOption(iMenuOption);
if (answer == false) { System.out.print("Incorrect Choice. ");}
} while (!answer);
return iMenuOption;
}
public static boolean isValMenuOption(String iMenuOption) {
return(iMenuOption.matches(MENUANSW));
}
public static void createUser() {
boolean validID = true, newID = true;
do {if (!validID) {System.out.println("User ID did not meet requirements.");}
if (!newID) {System.out.println("This User ID has been taken.");}
System.out.println("Please select a User Id (6 letters, followed by a dash (-), followed by 2 numbers).");
iID = scanner.next();
validID = isValidID(iID);
newID = isNewID(iID);}
while (validID==false || newID == false);
boolean valLength = true, valNum = true, valUpper = true, valLower = true;
do{ System.out.println("Please select a Password:");
System.out.println("(6-12 characters, one number, one upper case, one lower case, no white space or symbols).");
password = scanner.next();
valLength = isValLength(password);
valNum = valNum(password);
valUpper = valUpper(password);
valLower = valLower(password);}
while (!valLength || !valNum || !valUpper || !valLower);
pw.println(iID);
pw.println(password);
//menu();
}
public static boolean isValidID(String iID){
return(iID.matches(USERID));
}
public static boolean isNewID(String iID){
answer = true;
// Dat file scanner
try {
scannerDat = new Scanner(new File("account.dat"));
scannerDat.useDelimiter("\r\n");
}
catch (Exception e) {
e.printStackTrace();
System.out.println("File error");
System.exit(1);
}
while (scannerDat.hasNext()) {
recUserID = scannerDat.next();
recPassword = scannerDat.next();
if (recUserID.equals(iID)) {
answer = false;
break;
}
}
return answer;
}
public static boolean isValLength(String password) {
if (password.length() <6 || password.length() > 12) System.out.println("Password did not meet length requirements. ");
return(password.length() >= 6 && password.length() <= 12);
}
public static boolean valNum(String password) {
if (password.matches(".*[0-9].*") == false) System.out.println("Password must contain at least one number. ");
return(password.matches(".*[0-9].*"));
}
public static boolean valUpper(String password){
if (password.matches(".*[A-Z].*") == false) System.out.println("Password must contain one upper case letter.");
return (password.matches(".*[A-Z].*"));
}
public static boolean valLower(String password){
if (password.matches(".*[a-z].*") == false) System.out.println("Password must contain one lower case letter.");
return (password.matches(".*[a-z].*"));
}
public static void signIn() {
boolean newID;
System.out.println("Enter User ID.");
iID = scanner.next();
System.out.println("Enter Password.");
password = scanner.next();
newID = isNewID(iID);
if (newID == false) {
if (password.equals(recPassword)) {System.out.println("Authenticated. You have signed in.");}
else {System.out.println("Invalid Password.");}
}
else {System.out.println("Invalid User ID.");}
}
public static void closing(){
System.out.println("You signed out.");
pw.close();
}
} // end of program
My .DAT file simply has
aabbcc-11
Onetwo3
aaabbb-22
Onetwo34
Change this line:
scannerDat.useDelimiter("\r\n");
to
scannerDat.useDelimiter("\n");
Works for me!
The first bad thing you are doing is that you have a PrintWriter (pw) and a Scanner (scannerDat) both accessing the same file and neither of them closing the access to the file, except right at the end, the pw is closed.
isNewId is the main culprit. Inside here you are better off using a FileReader instead of a Scanner. Declare the FileReader locally within the method and ensure that the file access is closed before exiting, this procedure.
Also within isNewId - don't call System.exit(); In a program this size it's OK, but anything bigger than this it is a cardinal sin and you should never just exit a program as ungracefully as this.
You need to flush your PrintWriter in order for it to do append to the file immediately. Otherwise it would just store it in the buffer to write it into the file eventually
Also, please check what user #simo.3792095 said about your code. You should not have several file streams opened at the same time. Either open/close your streams every time you do something with them, or read the whole data file on program start, then work with in-memory data, then save on exit. It is much easier to work with in-memory data, but if your program crashes all of the in-memory changes will be lost.
Thanks for all who replied. In the end, the problem ended up being that I had a "rough draft" java class, which I then copied and pasted the code of into a new java class under the same java project, which seemed to be giving me issues. Once I created a new java project and used the valid java class it worked fine. I also added the pw.flush(); method to my code so I was able to append to the file immediately instead of upon closing. Also removed the system.exit.
I am creating a basic password based login system. It uses MD5 to secure the password. The correct password is "csk" (without quotes). If anyone enters that correctly, he gets access to a key.html file in the local computer. But if someone enters the wrong password for three consecutive times, he gets "banned" from logging in again. But the design that I have constructed bans the user only for that particular session. If he opens the terminal again, it starts from the very beginning. If the variable count is greater than 3 (three) from the last time, then the program, on execution via void main() would display "You are banned". I want to keep it basic and not use JDBC and SQL and such. Also, this is a local application and not a web-based one. I'm quite confused what approach I should take on this. Here's my code that I've cooked up:
import java.math.*;
import java.security.*;
import java.io.*;
import java.util.*;
import java.net.HttpURLConnection;
public class pwd {
public static void main(String[] args)throws NoSuchAlgorithmException, IOException, InterruptedException {
int count = 1;
boolean run = true;
while (run && count<4){
System.out.println("Enter the password");
Scanner kb = new Scanner(System.in);
String pass = kb.nextLine();
String pd = "ea0882721f7f44384ce772375696f9a6"; //Password is "csk" without quotes geeks, this is it's MD5
// so enter "csk" in the terminal
// to run the program on execution
String md5sum = md5(pass);
String os = System.getProperty("os.name");
boolean o = false;
int win = os.indexOf("Windows");
if (md5sum.equals(pd)){
System.out.println("You've logged in successfully, get the Key now");
String url = "file:///C:/Users/<username>/Desktop/key.html"; // example www
Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
run = false;
}
else {
System.out.println("You've entered the wrong password, try again.");
System.out.println();
run = true;
if (count>=3) {
System.out.println("You are banned from logging in, due to repeated unsuccessful login attempts.");
}
++count;
}
}
}
public static String md5(String input)throws NoSuchAlgorithmException, IOException {
String md5 = null;
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(input.getBytes(), 0, input.length());
md5 = new BigInteger(1, digest.digest()).toString(16);
return md5;
}
}
EDIT: There's no need for me to change MD5 hashing to anything else, it's just a basic one.
you can simply write to a file with java.io.FileWriter
FileWriter writer = null;
String text = "username";
try{
writer = new FileWriter("banned.txt", true);
writer.write("\r\n");
writer.write(text,0,text.length());
}catch(IOException ex){
ex.printStackTrace();
}finally{
if(writer != null){
writer.close();
}
}
The above code allows you to add a line in a file.
To read the file, you can do like this (java.io.BufferedReader):
BufferedReader reader = new BufferedReader(new FileReader("banned.txt"));
String line = null;
while ((line = reader.readLine()) != null) {
// check the username here
}
Hum what you want to do can be done but you have to create a kind of "login page" because as it is now (with the code you gave) there is no user information involved.
To save the important information of the ban, you can for example save a boolean in a file (or the user when you have it) and read this same file at the beginning of your code in order to know if user is ban or not. In this case you have to change your code to add the information of ban or not before trying the new input codes ;)
If you don't have a login page, so no user once you ban someone no one will be able to log any more :)
PS: Java class start with a Upper case not pwd but Pwd normally
PS2: Your count will always be increased because it's not in a else ;) so every try of new code will increase it even if the user is ban ;)
I'm making a game which plays until the user enters quit in the command line.
The user can enter different commands like get and go, with the get command the user can say what to get like, get baseball bat. What I do in my code is split the command.
everything is working fine but I have found a bug which I can't solve. If I enter "get" and press space and then ctrl+z it gets in a while loop which never ends.
It only happens with ctrl+z (1 time with ctrl c but after that 1 time not anymore)
private void run()
{
while (! quitCommand)
{
String input = null;
try
{
input = null;
System.out.println("Input "+ input);
System.out.println("Give a command.");
BufferedReader is = new BufferedReader(new InputStreamReader(System.in));
input = is.readLine();
handleCommand(input);
// As long as the command isn’t to quit:
// get the next input line and handle it. (With handleCommand.)
}
catch (Exception e)
{
System.out.println("Something went wrong we are sorry try again.");
e.printStackTrace();
}
}
}
/**
* #param userInput (This is the entire input string from the user.)
*
* (Tell others to) Perform the task which belongs to the given
* command.
*/
private void handleCommand(String userInput)
{
// Split the user input string.
if (userInput != null) // user input can not be empty
{
String[] delenTekst = userInput.split(" ");
// The first word is a command. The rest is extra information
String command = delenTekst[0];
String extra = "";
for (int i = 1; i < delenTekst.length; i ++)
{
if (i == 1)
{
extra = extra + delenTekst[i];
}
else
{
extra = extra +" " + delenTekst[i];
}
}
switch (command)
{
// Check if the command is to travel between rooms. If so, handle
case "go"
:
this.checkRoomTravel(extra);
break;
// If there isn't any room travel, then check all other command
case "get"
:
System.out.println("Looking for " +extra );
this.handleGetCommand(extra);
break;
case "quit"
:
quitCommand = true;
break;
default
:
System.out.println("Command is not known try help for information");
break;
}
}
else
{
userInput = "help";
}
}
I'm new to java so it can be something really simple.
On the top of my script I have a private boolean quitCommand = false; which is to check if the user entered quit.
Ctrl+Z closes the Console and therefore your readLine() returns null as pretended to indicate that end of file was reached. So all you need to do, is to check for null returned by readLine() and handle this as you handle the "quit".
I've changed your code (just to test my thesis) and also stream lined a few things, e.g. you dont need to recreate a BufferedReader every time you read a line.
private boolean quitCommand = false;
private void runIt() {
BufferedReader is = new BufferedReader(new InputStreamReader(System.in));
String input = null;
while(!quitCommand) {
try {
System.out.print("Give a command: ");
input = is.readLine();
// As long as the command isn’t to quit:
if(input == null || "quit".equals(input.trim())) quitCommand = true;
if(quitCommand) break;
// get the next input line and handle it. (With handleCommand.)
String[] words = input.trim().split("\\s+");
// ** This is the original handleCommand line **
System.out.println(input + ":" + Arrays.toString(words));
}
catch (Exception e) {
System.out.println("Something went wrong we are sorry try again.");
e.printStackTrace();
}
}
}
BTW: To split the input into words I'd use the regular expression as shown in my code. This works also if the user enters tabs or multiple spaces.
On DOS/Windows Ctrl+Z means end of input. This causes readLine() to return null no matter how many times you call it. This is likely to cause your code to fail as you don't appear to check for it. I suspect you are getting a NullPointerException which you are pretending didn't happen and trying again, endlessly.
I am creating a custom shell in Java. I have added history to it so that when up arrow is pressed it goes to the previous command, but the up arrow seems to not be working
Here is my code:
public class MyShell {
public static class JavaStringHistory
{
private List<String> history = new ArrayList<String>();
}
public static void main(String[] args) throws java.io.IOException {
JavaStringHistory javaStringHistory = new JavaStringHistory();
javaStringHistory.history.add("");
Integer indexOfHistory = 0;
String commandLine;
BufferedReader console = new BufferedReader
(new InputStreamReader(System.in));
//Break with Ctrl+C
while (true) {
//read the command
System.out.print("Shell>");
commandLine = console.readLine();
javaStringHistory.history.add(commandLine);
//if just a return, loop
if (commandLine.equals(""))
continue;
//history
if (commandLine.equals(KeyEvent.VK_UP))
{
System.out.println("up arrow");
}
//help command
if (commandLine.equals("help"))
{
System.out.println();
System.out.println();
System.out.println("Welcome to the shell");
System.out.println("Written by: Alex Frieden");
System.out.println("--------------------");
System.out.println();
System.out.println("Commands to use:");
System.out.println("1) cat");
System.out.println("2) exit");
System.out.println("3) clear");
System.out.println();
System.out.println();
System.out.println("---------------------");
System.out.println();
}
if (commandLine.equals("clear"))
{
for(int cls = 0; cls < 10; cls++ )
{
System.out.println();
}
}
if(commandLine.startsWith("cat"))
{
System.out.println("test");
//ProcessBuilder pb = new ProcessBuilder();
//pb = new ProcessBuilder(commandLine);
}
else
{
System.out.println("Incorrect Command");
}
if (commandLine.equals("exit"))
{
System.out.println("...Terminating the Virtual Machine");
System.out.println("...Done");
System.out.println("Please Close manually with Options > Close");
System.exit(0);
}
indexOfHistory++;
}
}
}
All I am getting is
Shell>^[[A
Incorrect Command
Shell>
Any thoughts?
There are several problems with your approach:
User blackSmith has mentioned before me that system console handling is platform-dependent when it comes to cursor key handling and similar topics.
BufferedReader.readLine is not a smart choice to use for history cycling in a shell because you want the shell to immediately react to cursor keys and not force the user to press Return or Enter. Reading whole lines is only required for user commands. Thus, you need to scan the keyboard input for each single character or key code and decide by yourself if it is e.g. a cursor key (up/down for history cycling, left/right for cursor movement within the command line) or delete/backspace for command line editing and so forth.
The text strings which are created by reading control characters via readLine can depend on the OS, maybe even on the shell and the character set (UTF-8, ISO-8859-1, US ASCII etc.) on the console.
Built-in shell editing functions like command history might get in the way with readLine, e.g. on Linux I see the "^[[A" stuff for cursor up, on Windows the cursor keys are passed through to the built-in command history feature of cmd.exe. I.e. you need to put the console in raw mode (line editing bypassed and no Enter key required) as opposed to cooked mode (line editing with Enter key required).
Anyway, so as to answer your initial question about how to find out which key codes are produced by BufferedReader.readLine, it is actually quite simple. Just dump the bytes to the console like so:
commandLine = console.readLine();
System.out.println("Entered command text: " + commandLine);
System.out.print ("Entered command bytes: ");
for (byte b : commandLine.getBytes())
System.out.print(b + ", ");
System.out.println();
Under Linux cursor up might be something like "27, 91, 65" or just "91, 65", depending on the terminal. cursor down ends with "66" instead on my system. So you could do something like:
public class MyShell {
private static final String UP_ARROW_1 = new String(new byte[] {91, 65});
private static final String UP_ARROW_2 = new String(new byte[] {27, 91, 65});
private static final String DN_ARROW_1 = new String(new byte[] {91, 66});
private static final String DN_ARROW_2 = new String(new byte[] {27, 91, 66});
// (...)
public static void main(String[] args) throws IOException {
// (...)
// history
else if (commandLine.startsWith(UP_ARROW_1) || commandLine.startsWith(UP_ARROW_2)) {
System.out.println("up arrow");
}
else if (commandLine.startsWith(DN_ARROW_1) || commandLine.startsWith(DN_ARROW_2)) {
System.out.println("down arrow");
}
// (...)
}
}
But all this is just for explanation or demonstration and so as to answer your question - I do like to get the bounty. ;-)
Maybe a way to go is not to re-invent the wheel and use the work of others, e.g. a framework like JLine. It is not perfect either from what I have heard, but goes way further than anything you can develop by yourself in a short time. Someone has written a short introductory blog post about JLine. The library seems to do just what you need. Enjoy!
Update: I gave JLine 2.11 a little try with this code sample (basically the one from the blog post plus tab filename completion:
import java.io.IOException;
import jline.TerminalFactory;
import jline.console.ConsoleReader;
import jline.console.completer.FileNameCompleter;
public class MyJLineShell {
public static void main(String[] args) {
try {
ConsoleReader console = new ConsoleReader();
console.addCompleter(new FileNameCompleter());
console.setPrompt("prompt> ");
String line = null;
while ((line = console.readLine()) != null) {
console.println(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
TerminalFactory.get().restore();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
It works nicely on Windows and Linux, but for me tab completion only works on Linux, not on Windows. Anyway, command history works well on both platforms.
VK_UP is an integer constant, while in.readLine() is a string.
They won't equal each other. Why don't you try to test for the code that appears in console usually when you click up arrow? So like:
if (in.readLine().equals("^[[A"))
and then you could clear the line, and write the command in the arraylist with the highest index.
Also, I tested this and found a bug. Change your if statements besides the first to else if; after any command it will eventually get to the else and display "Incorrect Command"