Saturday 15 July 2017

JAAS: Hello World Authentication Application

Follow the below steps and setup the application first. Run and experiment it, then go through the description that I provided at the end.

Step 1: Create new java project in Eclipse.

Open Eclipse, right click on Package explorer -> New -> Java Project.

Give the project name as 'jaas_tutorial'.

Step 2: Create package 'com.sample.handler' and define BasicAuthCallbackHandler class.

BasicAuthCallbackHandler.java
package com.sample.handler;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;

public class BasicAuthCallbackHandler implements CallbackHandler{

 
 @Override
 public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
  NameCallback nameCallBack = (NameCallback)callbacks[0];
  PasswordCallback passwordCallback = (PasswordCallback)callbacks[1];
  
  BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  
  System.out.println(nameCallBack.getPrompt());
  nameCallBack.setName(br.readLine());
  
  System.out.println(passwordCallback.getPrompt());
  passwordCallback.setPassword(br.readLine().toCharArray());
  
 }

}

Step 3: Create package 'com.sample.login' and define class BasicLoginModule like below.

BasicLoginModule.java
package com.smaple.login;

import java.io.IOException;
import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

public class BasicLoginModule implements LoginModule {

 private String username = "krishna";
 private String password = "krishna";
 CallbackHandler callbackHandler;

 @Override
 public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
   Map<String, ?> options) {
  this.callbackHandler = callbackHandler;
 }

 @Override
 public boolean login() throws LoginException {
  Callback[] callbackArray = new Callback[2];

  callbackArray[0] = new NameCallback("Enter logon id:");
  callbackArray[1] = new PasswordCallback("Enter password:", false);

  try {
   callbackHandler.handle(callbackArray);
  } catch (IOException | UnsupportedCallbackException e) {
   e.printStackTrace();
   throw new LoginException(e.getMessage());
  }

  String logonId = ((NameCallback) callbackArray[0]).getName();
  char[] passwordArr = ((PasswordCallback) callbackArray[1]).getPassword();
  String password = new String(passwordArr);

  if (username.equals(logonId) && this.password.equals(password)) {
   System.out.println("Login successful");
   return true;
  }

  throw new LoginException("Logon failed");
 }

 @Override
 public boolean commit() throws LoginException {
  return true;
 }

 @Override
 public boolean abort() throws LoginException {
  return false;
 }

 @Override
 public boolean logout() throws LoginException {
  return true;
 }

} 

Step 4: Create a file 'jaasAuth.config' with below content.

jaasAuth.config
JaasTutorial{
 com.smaple.login.BasicLoginModule required;
};

Step 5: Define class 'com.sample.app' and define the class Test like below.

Test.java
package com.sample.app;

import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import com.sample.handler.BasicAuthCallbackHandler;

public class Test {

 public static void main(String args[]) {
  System.setProperty("java.security.auth.login.config", "jaasAuth.config");
  LoginContext loginContext = null;

  try {
   loginContext = new LoginContext("JaasTutorial", new BasicAuthCallbackHandler());
  } catch (LoginException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
   return;
  }

  try {
   loginContext.login();
  } catch (LoginException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
}


Run the application with improper username and password.
Enter logon id:
kk
Enter password:
aaa
javax.security.auth.login.LoginException: Logon failed
 at com.smaple.login.BasicLoginModule.login(BasicLoginModule.java:50)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:498)
 at javax.security.auth.login.LoginContext.invoke(LoginContext.java:755)
 at javax.security.auth.login.LoginContext.access$000(LoginContext.java:195)
 at javax.security.auth.login.LoginContext$4.run(LoginContext.java:682)
 at javax.security.auth.login.LoginContext$4.run(LoginContext.java:680)
 at java.security.AccessController.doPrivileged(Native Method)
 at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:680)
 at javax.security.auth.login.LoginContext.login(LoginContext.java:587)
 at com.sample.app.Test.main(Test.java:23)

Run the application with username and password as ‘krishna’, you can able to see below output.
Enter logon id:
krishna
Enter password:
krishna
Login successful


There are 4 components in above application.
a.   jaasAuth.config file
b.   Initialize LoginContext
c.   BasicLoginModule
d.   BasicAuthCallbackHandler

a. jaasAuth.config
We should specify the login modules in this configuration file. You can give any name to this file.

You can specify one or more login modules and they will be executed in the order you defined.

Syntax of configuration file
      Name {
            ModuleClass  Flag    ModuleOptions;
            ModuleClass  Flag    ModuleOptions;
            ModuleClass  Flag    ModuleOptions;
      };
      Name {
            ModuleClass  Flag    ModuleOptions;
            ModuleClass  Flag    ModuleOptions;
      };
      other {
            ModuleClass  Flag    ModuleOptions;
            ModuleClass  Flag    ModuleOptions;
      };

In this example, I defined the login module like below.

JaasTutorial{
         com.smaple.login.BasicLoginModule required;
};

Each entry in the configuration file has unique name and list of module classes that are used while authenticating the users. The login modules will execute the order they specified in the entry.

For ex:
JaasTutorial{
         com.smaple.login.BasicLoginModule required;
com.smaple.login.OtpLoginModule required;
};

In above case, BasicLoginModule is executed first and OtpLoginModule will be executed next.

What is other in the config file?
If application do not have specific entry, it uses the other module by default.


Flag attribute
It can be set to one of the four values.
a.   Required
b.   Requisite
c.   Sufficient
d.   Optional

Below table summarizes each attribute in detail.

Value
Description
Required
The  LoginModule is required to succeed. If it succeeds or fails, authentication still continues
 to proceed down the  LoginModule list.
Requisite
The  LoginModule is required to succeed. If it succeeds, authentication continues down the LoginModule list.  If it fails, control immediately returns to the application  (authentication does not proceed down the LoginModule list).
Sufficient  
The  LoginModule is not required to succeed.  If it does succeed, control immediately returns to the application (authentication does not proceed down the  LoginModule list). If it fails, authentication continues down the LoginModule list.
Optional
The  LoginModule is not required to succeed.  If it succeeds or fails,  authentication still continues to proceed down the LoginModule list.                                                              

Module Options
By using module options, we can pass the values directly to the login module. For example, we can define the option, whether we should log the information, while authenticating or not.

How to specify module options?
By using key=value pair syntax, we can specify the module options. the value must be enclosed in double quotes.

Ex:
JaasTutorial{
         com.smaple.login.BasicLoginModule required
                                  debug="true"
                                  cache="enable";
};

Is there any limit on number of module options?
No, you can specify any number of options.

Can I specify system properties?
Yes, you can specify the system properties in the form of ${system.property}

Ex:
JaasTutorial{
         com.smaple.login.BasicLoginModule required
                                  debug="true"
                                  cache="enable"
                                  userHome="${user.home}"
                                  appHome = "${user.home}${/}app";
};

I am going to explain the complete example about the module options in my next post.

b. Initialize LoginContext
LoginContext class reads the configuration file (Ex: jaasAuth.config) and initialize all the login modules defines the configuration file. Each login module initialized with subject, callbackHandler, sharedState and options (Check the initialize method of BasicLoginModule class). Subject specifies the entity that is currently being authenticated and the callbackHandler is used to communicate with external world, for example, I defined BasicAuthCallbackHandler class to get the username and password from user. 'sharedState' is used by login modules to share the information among them.

System.setProperty("java.security.auth.login.config", "jaasAuth.config");
LoginContext loginContext = new LoginContext("JaasTutorial", new BasicAuthCallbackHandler());

System property ‘java.security.auth.login.config’ is used to specify the login configuration file that should be used.


c. BasicLoginModule
All the authentication providers (login modules) must implement LoginModule interface. LoginContext use the login() method of the login module to check the authenticity of the users. The login() method performs actual authentication and return true (or) false (or) can throw LoginException.

Authentication process in login module is divided into two phases.

Phase1
LoginContext invokes the login method of login module.

Phase2
If the overall logincontext authentication is successful, then the commit method of the login module is invoked. If the overall LoginContext authentication succeeded and the LoginModule's own authentication succeeded, then the commit method associates the relevant Principals (authenticated identities) and Credentials (authentication data such as cryptographic keys) with the Subject located within the LoginModule.

If the LoginContext's overall authentication failed (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules did not succeed), then the abort method for each LoginModule gets invoked. In this case, the LoginModule removes/destroys any authentication state originally saved.

D. BasicAuthCallbackHandler
All callback handlers must implement CallbackHandler interface. These are used to interact with the application to retrieve authentication data like username and password.

Can I provide the jaas configuration file at run time?
Yes, you can do like below.
java -Djava.security.auth.login.config=jaasAuth.config App

Reference



Previous                                                 Next                                                 Home

No comments:

Post a Comment