Authentication & Authorization of NetBeans Platform Applications
Join the DZone community and get the full member experience.
Join For FreeAuthentication and authorization for NetBeans Platform applications had me going for a while, until I thought of it as a non-linear service. (I.e., it doesn't need to happen at application start up, but anywhere once a TopComponent is opened). I haven't found anything on other sites and believe this to be a headache for many NetBeans Platform Newbies like me.
So I rolled my own implementation that looks like this.
On the implementation (only some of the methods below):
And finally, usage in a TopComponent:
So I rolled my own implementation that looks like this.
Service Interface
public interface AppUserService {
//Authenticate the user and store him/her in the implementation's private member
public void authenticateAppUser(String userName,char[] password);
//Checks if the implementation's private member is authenticated
public boolean isAppUserAuthenticated();
//Checks if user in the implementation class's private member is authorized
public boolean isAppUserAuthorized(String appFunction);
//If the user requires 2 or more roles to access a function
public boolean isAppUserAuthorized(String[] appFunctions);
//Some users (i.e. regional managers) must be able to access more than one AppEntity (office/company etc.)
public List<AppEntityService> getAppEntities();
//Uses a custom JPanel and a NotifyDescriptor implementation
public void displayAppUserLogin();
//Same as display login
public void displayNotAuthorized();
//Primary key: MySQL Type BINARY(16) for GUIDs
public byte[] getUserGUID();
//Hack to get a singleton, the implementation calls: private static synchronized AppUserService getSingleton()
public AppUserService getDefault();
//Load the AppUser's Authorized functions from the database
public void loadAuthorizedFunctions();
//Some Candy (that is, not really needed): A central place in the implementation to create Application Specific functions. Typically like "User Editor","Customer Viewer" etc.
public void checkCreateAppFunctions();
//More Candy: Create a default user with a complicated password that can open the application on a fresh install
public void checkCreateDefaultAdministrator();
//Log the user off. That is, set the implementation's private members to null
public void revokeAuthentication();
}
On the implementation (only some of the methods below):
// The hack that was mentioned (perhaps a lack of knowledge about Lookups)
@Override
public AppUserService getDefault() {
return getSingleton();
}
private static synchronized AppUserService getSingleton() {
if (instance == null) {
instance = new AppUserProvider();
}
return instance;
}
@Override
public void displayAppUserLogin() {
LoginDialogJPanel loginPanel = new LoginDialogJPanel();
NotifyDescriptor nd;
// Let's give the user 3 tries
for (int i = 0; i < 3; i++) {
nd = new NotifyDescriptor(
loginPanel,
"Authentication Required",
NotifyDescriptor.OK_CANCEL_OPTION,
NotifyDescriptor.PLAIN_MESSAGE,
null,
NotifyDescriptor.YES_OPTION
);
if (DialogDisplayer.getDefault().notify(nd) == NotifyDescriptor.OK_OPTION) {
authenticateAppUser(loginPanel.getAppUserID(), loginPanel.getPassword());
if (isAppUserAuthenticated){
break;
}
} else {
break;
}
}
}
@Override
public void checkCreateAppFunctions() {
//Call a function to check/create these in the database
appFunctionCheckCreate("Customer Viewer");
appFunctionCheckCreate("Customer Editor");
}
//This function uses the jasypt library for password encryption
//The following MUST always be included in the about box's credit section.
//Java Simplified Encryption Library found at http://www.jasypt.org/
//To review the license goto http://www.jasypt.org/license.html
@Override
public void authenticateAppUser(String userName, char[] password) {
checkCreateDefaultAdministrator();
EntityManagerFactory emf = null;
EntityManager em = null;
Appuser usr = null;
try{
emf = Persistence.createEntityManagerFactory("CUSTOMERPU");
em = emf.createEntityManager();
usr = (Appuser) em.createNamedQuery("Appuser.findByAppUserID").setParameter("appUserID", userName).getSingleResult();
} catch (Exception ex){
Logger logger = Logger.getLogger(this.getClass().getName());
logger.log(Level.WARNING, "Database exception", ex);
}
if (usr != null) {
//jasypt class instance:
StrongPasswordEncryptor passwordEncryptor = new StrongPasswordEncryptor();
String pass = new String(password);
if (passwordEncryptor.checkPassword(pass, usr.getPassword())) {
this.isAppUserAuthenticated = true;
appUserGUID = usr.getAppUserGUID();
loadAuthorizedFunctions();
loadEntities();
} else {
this.isAppUserAuthenticated = false;
}
}
}
And finally, usage in a TopComponent:
@Override
protected void componentActivated() {
AppUserService aus = Lookup.getDefault().lookup(AppUserService.class).getDefault();
this.setVisible(false);
if (!aus.isAppUserAuthenticated()){
aus.displayAppUserLogin();
}
if (!aus.isAppUserAuthenticated()){
this.close();
return;
}
if (!aus.isAppUserAuthorized("Customer Editor")){
aus.displayNotAuthorized();
this.close();
return;
}
this.setVisible(true);
super.componentActivated();
}
//To avoid weird (Unauthorized Dialogue screen shows when the app loads) behavior at start up or for multiple users on the PC log in, use:
@Override
public int getPersistenceType() {
return TopComponent.PERSISTENCE_NEVER;
}
Since the form can be closed from the "componentActivated" method, null checks must be done for any code assuming initialized variables:
@Override
public void componentClosed() {
if (result != null){
result.removeLookupListener(this);
result = null;
}
}
Diagram
Conclusion
One thing that would have been nice would be to dynamically set the "userdir" once the user has logged on. So far it doesn't seem plausible since log in happens after the folder system has been initialized. Therefore the PERSISTENCE_NEVER part actually play in the design's favor.
Warm Regards from a sunny South Africa. :-D
application
authentication
NetBeans
Opinions expressed by DZone contributors are their own.
Trending
-
Front-End: Cache Strategies You Should Know
-
Apache Kafka vs. Message Queue: Trade-Offs, Integration, Migration
-
Logging Best Practices Revisited [Video]
-
Observability Architecture: Financial Payments Introduction
Comments