Reputation: 426
I am quite new to Java EE and have been having a lot of trouble just getting started on the application I want to create. What I would like is a Swing application client that connects to an EJB project. I am using Glassfish v3.1.1. What I have so far are two stateless beans, one of which is secured using @DeclareRoles and a JDBC realm in Glassfish, and the beginnings of a client.
When the client is run, you are able to select a username, type a password, and thus login. If you use the correct password, everything works (the client console spits on some "secure" information). If you, however, type the incorrect password, you are permanently locked out. The InitialContext.lookup does not call the CallbackHandler again to check the new password, it continues using the incorrect credentials.
Can someone please tell me how to do this correctly? Am I using the correct method for this situation - there is an enormous amount of info on the web but basically 0 examples of what I am trying to do? Everything seems to apply only to J2EE or Servlets! Here is some relevant code.
glassfish-ejb-jar.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-ejb-jar PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 EJB 3.1//EN" "http://glassfish.org/dtds/glassfish-ejb-jar_3_1-1.dtd">
<glassfish-ejb-jar>
<security-role-mapping>
<role-name>Admin</role-name>
<group-name>Admin</group-name>
</security-role-mapping>
<security-role-mapping>
<role-name>Employee</role-name>
<group-name>Employee</group-name>
</security-role-mapping>
<enterprise-beans>
<ejb>
<ejb-name>LoginBean</ejb-name>
<jndi-name>ejb/machineryhub/LoginService</jndi-name>
</ejb>
<ejb>
<ejb-name>EmployeeBean</ejb-name>
<jndi-name>ejb/machineryhub/EmployeeService</jndi-name>
<ior-security-config>
<as-context>
<auth_method>username_password</auth_method>
<realm>machineryhub</realm>
<required>true</required>
</as-context>
</ior-security-config>
</ejb>
</enterprise-beans>
</glassfish-ejb-jar>
Will I need to add the <ior-security-config>
block to every secured bean I create?
application-client.xml:
<?xml version="1.0" encoding="UTF-8"?>
<application-client version="6" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application-client_6.xsd">
<display-name>MachineryHub</display-name>
<ejb-ref>
<ejb-ref-name>LoginBean</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<remote>machineryhub.service.LoginService</remote>
</ejb-ref>
<ejb-ref>
<ejb-ref-name>EmployeeBean</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<remote>machineryhub.service.EmployeeService</remote>
</ejb-ref>
<callback-handler>machineryhub.LoginCallbackHandler</callback-handler>
</application-client>
machineryhub.LoginCallbackHandler:
public class LoginCallbackHandler implements CallbackHandler {
@Override
public void handle(Callback[] clbcks) throws IOException, UnsupportedCallbackException {
LoginFrame l = LoginFrame.instance;
for (Callback cb : clbcks) {
if (cb instanceof NameCallback) {
NameCallback ncb = (NameCallback) cb;
ncb.setName(l.usernameCombo.getSelectedItem().toString());
} else if (cb instanceof PasswordCallback) {
PasswordCallback pcb = (PasswordCallback) cb;
pcb.setPassword(l.passwordText.getPassword());
} else {
throw new UnsupportedCallbackException(cb);
}
}
}
}
And now for the long one, the swing application client.
machineryhub.LoginFrame
public class LoginFrame extends JFrame implements ActionListener {
public static LoginFrame instance;
public static void main(String[] args) {
// Handle uncaught exceptions in the main and Swing threads
ExceptionHandler.registerExceptionHandler();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(new SubstanceMistSilverLookAndFeel());
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
(new LoginFrame()).setVisible(true);
} catch (final Exception exception) {
ExceptionHandler.handle(Thread.currentThread(), exception);
}
}
});
}
public JComboBox usernameCombo;
public JPasswordField passwordText;
private JButton loginButton;
public LoginFrame() {
// Window Setup
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Login :: MachineryHub");
this.setLocationRelativeTo(null);
this.setIconImages(IconFactory.application_images);
// Create GUI
createGui();
usernameCombo.requestFocusInWindow();
LoginFrame.instance = this;
}
private void createGui() {
// Content Pane
final JPanel contentPanel = new JPanel();
List<String> usernames = getLoginService().getUsernames();
Collections.sort(usernames);
usernameCombo = new JComboBox(usernames.toArray());
passwordText = new JPasswordField(15);
passwordText.setActionCommand("Login");
passwordText.addActionListener(this);
loginButton = new JButton("Login", IconFactory.getImageIcon(IconFactory.Icon.KEY, 16));
loginButton.setActionCommand("Login");
loginButton.addActionListener(this);
GroupLayout layout = new GroupLayout(contentPanel);
contentPanel.setLayout(layout);
layout.setAutoCreateContainerGaps(true);
layout.setAutoCreateGaps(true);
layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).addComponent(usernameCombo).addGroup(layout.createSequentialGroup().addComponent(passwordText).addComponent(loginButton)));
layout.setVerticalGroup(layout.createSequentialGroup().addComponent(usernameCombo, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE).addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE).addComponent(passwordText).addComponent(loginButton)));
this.setContentPane(contentPanel);
this.pack();
}
@Override
public void actionPerformed(final ActionEvent e) {
if (e == null || e.getActionCommand() == null) {
return;
}
if (e.getActionCommand().equals("Login")) {
loginButton.setEnabled(false);
passwordText.setEnabled(false);
usernameCombo.setEnabled(false);
loginButton.setIcon(IconFactory.getImageIcon(IconFactory.SpecialImage.LOADING));
try {
Context c = new InitialContext();
EmployeeService es = (EmployeeService) c.lookup("ejb/machineryhub/EmployeeService");
System.out.println("Number of employees: " + es.getAllEmployees().size());
this.dispose();
} catch (NamingException exception) {
loginButton.setEnabled(true);
passwordText.setEnabled(true);
usernameCombo.setEnabled(true);
loginButton.setIcon(IconFactory.getImageIcon(IconFactory.Icon.KEY, 16));
JOptionPane.showMessageDialog(LoginFrame.this, "Login Error: " + exception.getMessage(), "Login Error! :: MachineryHub", JOptionPane.ERROR_MESSAGE);
}
}
}
private LoginService getLoginService() {
try {
Context c = new InitialContext();
return (LoginService) c.lookup("ejb/machineryhub/LoginService");
} catch (NamingException ne) {
throw new RuntimeException(ne);
}
}
}
Upvotes: 3
Views: 2659
Reputation: 426
I'm not positive this is the best or recommended way to solve this problem, but I have found a way to do what I need. The solution lies in using the ProgrammaticLogin
class. I removed the LoginCallbackHandler
class and the reference from application-client.xml
. Then in the login code, just before creating InitialContext
, I used the following very simple two lines:
ProgrammaticLogin pl = new ProgrammaticLogin();
pl.login(usernameCombo.getSelectedItem().toString(), passwordText.getPassword());
And this seems to work regardless of how many times I enter the wrong password (you could put a limit on this as well with a simple counter). I feel a little stupid for taking so long to figure this out, but this class didn't show up in Netbeans so I assumed it was something that was no longer valid in Java EE 6. However, it is simply a matter of adding Glassfish/modules/security.jar
to the libraries for it to show up.
Upvotes: 3