Chapter 4. Password Authentication
Hardcoding passwords in source code, as Examples 2-1 and 3-1 do, is a very bad idea to say the least. If a password is required, you should ask the user for it at runtime. Furthermore, when the user types the password, it should not be displayed on the screen. Ideally, it should not even be transmitted in clear text across the network, although in fact many current clients and servers do exactly that.
When you start a mail session, the JavaMail API allows you to provide a javax.mail
.Authenticator
object that it can use to get the username and password. Authenticator
is an abstract class:
public
abstract
class
Authenticator
extends
Object
When the provider needs to know a username or password, it calls back to the getPasswordAuthentication()
method in a user-defined subclass of Authenticator
. This returns a PasswordAuthentication
object containing this information:
protected
PasswordAuthentication
getPasswordAuthentication
()
Tip
These two classes are almost exactly the same as the java.net.Authenticator
and java.net.PasswordAuthentication
classes. Everything you know about java.net.Authenticator
and java.net.PasswordAuthentication
is true of javax.mail.Authenticator
and javax.mail.PasswordAuthentication
. The only thing you have to watch out for is that if you import both java.net.*
and javax.mail.*
in a class, your source code will have to use fully qualified names like java.net.Authenticator
instead of short names like Authenticator
.
To add runtime password authentication to your programs, subclass Authenticator
and override getPasswordAuthentication()
with a method that knows how to securely ask the user for a password. One useful tool for this process is the JPasswordField
component from Swing. Example 4-1 demonstrates a Swing-based Authenticator
subclass that brings up a dialog to ask the user for their username and password.
import
javax.mail.*
;
import
javax.swing.*
;
import
java.awt.*
;
import
java.awt.event.*
;
public
class
MailAuthenticator
extends
Authenticator
{
private
JDialog
passwordDialog
=
new
JDialog
(
new
JFrame
(),
true
);
private
JTextField
usernameField
=
new
JTextField
(
20
);
private
JPasswordField
passwordField
=
new
JPasswordField
(
20
);
private
JButton
okButton
=
new
JButton
(
"OK"
);
public
MailAuthenticator
()
{
this
(
""
);
}
public
MailAuthenticator
(
String
username
)
{
JLabel
mainLabel
=
new
JLabel
(
"Please enter your username and password: "
);
JLabel
userLabel
=
new
JLabel
(
"Username: "
);
JLabel
passwordLabel
=
new
JLabel
(
"Password: "
);
Container
pane
=
passwordDialog
.
getContentPane
();
pane
.
setLayout
(
new
GridLayout
(
4
,
1
));
pane
.
add
(
mainLabel
);
JPanel
p2
=
new
JPanel
();
p2
.
add
(
userLabel
);
p2
.
add
(
usernameField
);
usernameField
.
setText
(
username
);
pane
.
add
(
p2
);
JPanel
p3
=
new
JPanel
();
p3
.
add
(
passwordLabel
);
p3
.
add
(
passwordField
);
pane
.
add
(
p3
);
JPanel
p4
=
new
JPanel
();
p4
.
add
(
okButton
);
pane
.
add
(
p4
);
passwordDialog
.
pack
();
ActionListener
listener
=
new
HideDialog
();
okButton
.
addActionListener
(
listener
);
usernameField
.
addActionListener
(
listener
);
passwordField
.
addActionListener
(
listener
);
}
class
HideDialog
implements
ActionListener
{
@Override
public
void
actionPerformed
(
ActionEvent
event
)
{
passwordDialog
.
setVisible
(
false
);
}
}
public
PasswordAuthentication
getPasswordAuthentication
()
{
passwordDialog
.
setVisible
(
true
);
// getPassword() returns an array of chars for security reasons.
// We need to convert that to a String for
// the PasswordAuthentication() constructor.
String
password
=
new
String
(
passwordField
.
getPassword
());
String
username
=
usernameField
.
getText
();
// Erase the password in case this is used again.
// The provider should cache the password if necessary.
passwordField
.
setText
(
""
);
return
new
PasswordAuthentication
(
username
,
password
);
}
}
Most of this code is just for handling the GUI. Figure 4-1 shows the rather simple dialog box this produces.
Interestingly, JPasswordField
takes more pains to be secure than PasswordAuthentication
does. JPasswordField
stores passwords as an array of chars so that when you’re done with the password, you can overwrite it with nulls. This means the password exists in memory for less time and the virtual memory system is less likely to swap the program out to disk and leave the password there in clear text. However, PasswordAuthentication
stores passwords as strings, which are immutable and therefore are more likely to be written to disk in a VM swap.
Modifying the POP client to support this style of authentication is straightforward, as Example 4-2 demonstrates. We replace the hardcoded username and password with nulls and pass an instance of MailAuthenticator
as the second argument to connect()
. The only other change is that we call System.exit()
at the end of the main()
method, since the program will no longer exit when the main()
method returns once the event dispatch thread has been started.
import
javax.mail.*
;
import
java.io.IOException
;
import
java.util.*
;
public
class
SecurePOP3Client
{
public
static
void
main
(
String
[]
args
)
{
Properties
props
=
new
Properties
();
String
host
=
"utopia.poly.edu"
;
String
provider
=
"pop3"
;
try
{
// Connect to the POP3 server
Session
session
=
Session
.
getInstance
(
props
,
new
MailAuthenticator
());
Store
store
=
session
.
getStore
(
provider
);
store
.
connect
(
host
,
null
,
null
);
// Open the folder
Folder
inbox
=
store
.
getFolder
(
"INBOX"
);
if
(
inbox
==
null
)
{
System
.
out
.
println
(
"No INBOX"
);
System
.
exit
(
1
);
}
inbox
.
open
(
Folder
.
READ_ONLY
);
// Get the messages from the server
Message
[]
messages
=
inbox
.
getMessages
();
for
(
int
i
=
0
;
i
<
messages
.
length
;
i
++)
{
System
.
out
.
println
(
"------------ Message "
+
(
i
+
1
)
+
" ------------"
);
messages
[
i
].
writeTo
(
System
.
out
);
}
// Close the connection
// but don't remove the messages from the server
inbox
.
close
(
false
);
store
.
close
();
}
catch
(
MessagingException
|
IOException
ex
)
{
ex
.
printStackTrace
();
}
// since we brought up a GUI returning from main() won't exit
System
.
exit
(
0
);
}
}
Get JavaMail API now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.