JAAS Authentication and Authorization in Java

Overview

Java Authentication and Authorization Service (JAAS) is a Java security framework that provides a pluggable and extensible architecture for authenticating users and enforcing access controls. It separates the concerns of authentication from authorization and allows for flexible security implementations.

Core Concepts

  • Subject: Represents the current user or service
  • Principal: Identity attribute (username, role, group)
  • Credential: Proof of identity (password, certificate)
  • LoginContext: Entry point for authentication
  • LoginModule: Pluggable authentication modules
  • CallbackHandler: Handles user interaction during login

Basic Setup and Dependencies

Maven Dependencies

<dependencies>
<!-- Core JAAS (part of Java SE) -->
<dependency>
<groupId>javax.security.auth</groupId>
<artifactId>jaas</artifactId>
<version>1.0.1</version>
<scope>system</scope>
<systemPath>${java.home}/lib/jaas.jar</systemPath>
</dependency>
<!-- For custom LoginModules -->
<dependency>
<groupId>javax.security</groupId>
<artifactId>jacc</artifactId>
<version>1.0</version>
</dependency>
</dependencies>

Basic JAAS Authentication

Example 1: Simple File-Based Authentication

import javax.security.auth.*;
import javax.security.auth.login.*;
import javax.security.auth.callback.*;
import java.io.*;
import java.util.*;
public class BasicJAASAuthentication {
public static void main(String[] args) {
try {
// Create LoginContext with configuration name and callback handler
LoginContext loginContext = new LoginContext("SimpleFileLogin", new SimpleCallbackHandler());
// Perform authentication
loginContext.login();
System.out.println("Authentication successful!");
// Get the authenticated subject
Subject subject = loginContext.getSubject();
System.out.println("Subject: " + subject);
System.out.println("Principals: " + subject.getPrincipals());
// Perform secured action
performSecuredAction(subject);
// Logout
loginContext.logout();
System.out.println("Logout successful!");
} catch (LoginException e) {
System.err.println("Authentication failed: " + e.getMessage());
}
}
private static void performSecuredAction(Subject subject) {
// Execute action with subject's permissions
Subject.doAs(subject, new PrivilegedAction<Void>() {
@Override
public Void run() {
System.out.println("Performing secured action as: " + 
Subject.getSubject(AccessController.getContext()));
// Perform some secured operation here
return null;
}
});
}
}
// Custom CallbackHandler for handling user interaction
class SimpleCallbackHandler implements CallbackHandler {
@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (Callback callback : callbacks) {
if (callback instanceof NameCallback) {
NameCallback nameCallback = (NameCallback) callback;
System.out.print(nameCallback.getPrompt());
nameCallback.setName(new Scanner(System.in).nextLine());
} else if (callback instanceof PasswordCallback) {
PasswordCallback passwordCallback = (PasswordCallback) callback;
System.out.print(passwordCallback.getPrompt());
passwordCallback.setPassword(new Scanner(System.in).nextLine().toCharArray());
} else {
throw new UnsupportedCallbackException(callback, "Unsupported callback type");
}
}
}
}

JAAS Configuration File (jaas.config)

SimpleFileLogin {
com.example.SimpleFileLoginModule required debug=true;
};
DatabaseLogin {
com.example.DatabaseLoginModule sufficient
dbUrl="jdbc:mysql://localhost:3306/authdb"
dbUser="auth_user"
dbPassword="auth_pass";
};
LDAPLogin {
com.example.LDAPLoginModule sufficient
ldapUrl="ldap://localhost:389"
baseDN="dc=example,dc=com";
};
CompositeLogin {
com.example.LDAPLoginModule sufficient
ldapUrl="ldap://localhost:389"
baseDN="dc=example,dc=com";
com.example.DatabaseLoginModule sufficient
dbUrl="jdbc:mysql://localhost:3306/authdb";
com.example.SimpleFileLoginModule required;
};

Custom LoginModule Implementation

Example 2: File-Based LoginModule

import javax.security.auth.*;
import javax.security.auth.spi.*;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
import java.io.*;
import java.util.*;
import java.security.Principal;
public class SimpleFileLoginModule implements LoginModule {
private Subject subject;
private CallbackHandler callbackHandler;
private Map<String, ?> sharedState;
private Map<String, ?> options;
private String username;
private Set<Principal> principals;
private boolean authenticated = false;
private boolean committed = false;
// Configuration options
private String userFile;
private boolean debug;
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
this.sharedState = sharedState;
this.options = options;
// Read configuration options
this.userFile = (String) options.get("userFile");
this.debug = "true".equals(options.get("debug"));
if (this.userFile == null) {
this.userFile = "users.properties";
}
if (debug) {
System.out.println("SimpleFileLoginModule initialized");
}
}
@Override
public boolean login() throws LoginException {
if (callbackHandler == null) {
throw new LoginException("No CallbackHandler available");
}
try {
// Create callbacks for username and password
NameCallback nameCallback = new NameCallback("Username: ");
PasswordCallback passwordCallback = new PasswordCallback("Password: ", false);
Callback[] callbacks = new Callback[]{nameCallback, passwordCallback};
callbackHandler.handle(callbacks);
username = nameCallback.getName();
char[] password = passwordCallback.getPassword();
if (username == null || password == null) {
throw new LoginException("Username or password cannot be null");
}
// Authenticate against user file
authenticated = authenticateUser(username, new String(password));
if (authenticated) {
principals = new HashSet<>();
principals.add(new UsernamePrincipal(username));
principals.add(new RolePrincipal("USER")); // Default role
if (debug) {
System.out.println("Authentication successful for user: " + username);
}
return true;
} else {
throw new FailedLoginException("Invalid username or password");
}
} catch (IOException | UnsupportedCallbackException e) {
LoginException le = new LoginException("Authentication process failed");
le.initCause(e);
throw le;
}
}
private boolean authenticateUser(String username, String password) {
Properties users = new Properties();
try (InputStream input = new FileInputStream(userFile)) {
users.load(input);
String storedPassword = users.getProperty(username);
return storedPassword != null && storedPassword.equals(password);
} catch (IOException e) {
System.err.println("Error reading user file: " + e.getMessage());
return false;
}
}
@Override
public boolean commit() throws LoginException {
if (!authenticated) {
return false;
}
// Add principals to subject
subject.getPrincipals().addAll(principals);
committed = true;
if (debug) {
System.out.println("Login committed for user: " + username);
}
return true;
}
@Override
public boolean abort() throws LoginException {
if (!authenticated) {
return false;
}
if (!committed) {
authenticated = false;
username = null;
principals = null;
} else {
logout();
}
if (debug) {
System.out.println("Login aborted for user: " + username);
}
return true;
}
@Override
public boolean logout() throws LoginException {
subject.getPrincipals().removeAll(principals);
authenticated = false;
committed = false;
username = null;
principals = null;
if (debug) {
System.out.println("User logged out");
}
return true;
}
}
// Custom Principal implementations
class UsernamePrincipal implements Principal {
private final String name;
public UsernamePrincipal(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
UsernamePrincipal that = (UsernamePrincipal) obj;
return Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
@Override
public String toString() {
return "UsernamePrincipal: " + name;
}
}
class RolePrincipal implements Principal {
private final String role;
public RolePrincipal(String role) {
this.role = role;
}
@Override
public String getName() {
return role;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
RolePrincipal that = (RolePrincipal) obj;
return Objects.equals(role, that.role);
}
@Override
public int hashCode() {
return Objects.hash(role);
}
@Override
public String toString() {
return "RolePrincipal: " + role;
}
}

Example 3: Database LoginModule

import javax.security.auth.*;
import javax.security.auth.spi.*;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
import java.sql.*;
import java.util.*;
import java.security.Principal;
public class DatabaseLoginModule implements LoginModule {
private Subject subject;
private CallbackHandler callbackHandler;
private Map<String, ?> options;
private String username;
private Set<Principal> principals;
private boolean authenticated = false;
private boolean committed = false;
// Database configuration
private String dbUrl;
private String dbUser;
private String dbPassword;
private boolean debug;
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
this.options = options;
this.dbUrl = (String) options.get("dbUrl");
this.dbUser = (String) options.get("dbUser");
this.dbPassword = (String) options.get("dbPassword");
this.debug = "true".equals(options.get("debug"));
if (dbUrl == null) {
throw new IllegalArgumentException("Database URL is required");
}
}
@Override
public boolean login() throws LoginException {
if (callbackHandler == null) {
throw new LoginException("No CallbackHandler available");
}
try {
NameCallback nameCallback = new NameCallback("Username: ");
PasswordCallback passwordCallback = new PasswordCallback("Password: ", false);
Callback[] callbacks = new Callback[]{nameCallback, passwordCallback};
callbackHandler.handle(callbacks);
username = nameCallback.getName();
char[] password = passwordCallback.getPassword();
if (username == null || password == null) {
throw new LoginException("Username or password cannot be null");
}
// Authenticate against database
authenticated = authenticateAgainstDatabase(username, new String(password));
if (authenticated) {
principals = new HashSet<>();
principals.add(new UsernamePrincipal(username));
// Get user roles from database
Set<String> roles = getUserRoles(username);
for (String role : roles) {
principals.add(new RolePrincipal(role));
}
if (debug) {
System.out.println("Database authentication successful for user: " + username);
System.out.println("Roles: " + roles);
}
return true;
} else {
throw new FailedLoginException("Invalid database credentials");
}
} catch (Exception e) {
LoginException le = new LoginException("Database authentication failed");
le.initCause(e);
throw le;
}
}
private boolean authenticateAgainstDatabase(String username, String password) {
String sql = "SELECT password FROM users WHERE username = ? AND active = true";
try (Connection conn = DriverManager.getConnection(dbUrl, dbUser, dbPassword);
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, username);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
String storedPassword = rs.getString("password");
return storedPassword != null && storedPassword.equals(hashPassword(password));
}
} catch (SQLException e) {
System.err.println("Database error: " + e.getMessage());
}
return false;
}
private Set<String> getUserRoles(String username) {
Set<String> roles = new HashSet<>();
String sql = "SELECT r.role_name FROM user_roles ur " +
"JOIN roles r ON ur.role_id = r.id " +
"JOIN users u ON ur.user_id = u.id " +
"WHERE u.username = ?";
try (Connection conn = DriverManager.getConnection(dbUrl, dbUser, dbPassword);
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, username);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
roles.add(rs.getString("role_name"));
}
} catch (SQLException e) {
System.err.println("Error fetching user roles: " + e.getMessage());
}
return roles;
}
private String hashPassword(String password) {
// Simple hash for demonstration - use proper hashing in production
return Integer.toHexString(password.hashCode());
}
@Override
public boolean commit() throws LoginException {
if (!authenticated) {
return false;
}
subject.getPrincipals().addAll(principals);
committed = true;
return true;
}
@Override
public boolean abort() throws LoginException {
if (!authenticated) {
return false;
}
if (!committed) {
authenticated = false;
username = null;
principals = null;
} else {
logout();
}
return true;
}
@Override
public boolean logout() throws LoginException {
subject.getPrincipals().removeAll(principals);
authenticated = false;
committed = false;
username = null;
principals = null;
return true;
}
}

Authorization with JAAS

Example 4: Role-Based Authorization

import javax.security.auth.Subject;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.util.Set;
public class JAASAuthorizationExample {
public static void main(String[] args) {
try {
// Authenticate user
LoginContext loginContext = new LoginContext("DatabaseLogin", new ConsoleCallbackHandler());
loginContext.login();
Subject subject = loginContext.getSubject();
System.out.println("Authenticated subject: " + subject);
// Check authorization
if (hasRole(subject, "ADMIN")) {
performAdminAction(subject);
} else if (hasRole(subject, "USER")) {
performUserAction(subject);
} else {
System.out.println("Insufficient privileges");
}
// Logout
loginContext.logout();
} catch (LoginException e) {
System.err.println("Authentication failed: " + e.getMessage());
}
}
public static boolean hasRole(Subject subject, String role) {
Set<RolePrincipal> roles = subject.getPrincipals(RolePrincipal.class);
return roles.stream().anyMatch(principal -> principal.getName().equals(role));
}
public static void performAdminAction(final Subject subject) {
Subject.doAsPrivileged(subject, new PrivilegedAction<Void>() {
@Override
public Void run() {
// Check permission at runtime
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new AdminPermission());
}
System.out.println("Performing ADMIN action");
// Admin-specific logic here
return null;
}
}, null);
}
public static void performUserAction(final Subject subject) {
Subject.doAs(subject, new PrivilegedAction<Void>() {
@Override
public Void run() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new UserPermission());
}
System.out.println("Performing USER action");
// User-specific logic here
return null;
}
});
}
}
// Custom Permissions
class AdminPermission extends java.security.BasicPermission {
public AdminPermission() {
super("admin");
}
public AdminPermission(String name, String actions) {
super(name, actions);
}
}
class UserPermission extends java.security.BasicPermission {
public UserPermission() {
super("user");
}
}
// Console-based CallbackHandler
class ConsoleCallbackHandler implements CallbackHandler {
@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
Scanner scanner = new Scanner(System.in);
for (Callback callback : callbacks) {
if (callback instanceof NameCallback) {
NameCallback nameCallback = (NameCallback) callback;
System.out.print(nameCallback.getPrompt());
nameCallback.setName(scanner.nextLine());
} else if (callback instanceof PasswordCallback) {
PasswordCallback passwordCallback = (PasswordCallback) callback;
System.out.print(passwordCallback.getPrompt());
passwordCallback.setPassword(scanner.nextLine().toCharArray());
} else {
throw new UnsupportedCallbackException(callback, "Unsupported callback");
}
}
}
}

Example 5: Policy-Based Authorization

import java.io.File;
import java.io.FilePermission;
import java.security.Policy;
import java.security.Principal;
import java.security.ProtectionDomain;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.AllPermission;
import java.util.*;
public class JAASPolicy extends Policy {
private final Map<String, Set<Permission>> rolePermissions;
public JAASPolicy() {
rolePermissions = new HashMap<>();
initializeRolePermissions();
}
private void initializeRolePermissions() {
// Define permissions for each role
Set<Permission> adminPermissions = new HashSet<>();
adminPermissions.add(new AllPermission());
adminPermissions.add(new FilePermission("<<ALL FILES>>", "read,write,execute,delete"));
rolePermissions.put("ADMIN", adminPermissions);
Set<Permission> userPermissions = new HashSet<>();
userPermissions.add(new FilePermission("/home/user/-", "read,write"));
userPermissions.add(new RuntimePermission("accessDeclaredMembers"));
userPermissions.put("USER", userPermissions);
Set<Permission> guestPermissions = new HashSet<>();
guestPermissions.add(new FilePermission("/home/user/public/-", "read"));
rolePermissions.put("GUEST", guestPermissions);
}
@Override
public PermissionCollection getPermissions(ProtectionDomain domain) {
Permissions permissions = new Permissions();
// Add default permissions
addDefaultPermissions(permissions);
// Add role-based permissions
Principal[] principals = domain.getPrincipals();
if (principals != null) {
for (Principal principal : principals) {
if (principal instanceof RolePrincipal) {
String role = principal.getName();
Set<Permission> rolePerms = rolePermissions.get(role);
if (rolePerms != null) {
for (Permission perm : rolePerms) {
permissions.add(perm);
}
}
}
}
}
return permissions;
}
@Override
public PermissionCollection getPermissions(CodeSource codesource) {
return new Permissions(); // Empty permissions
}
@Override
public boolean implies(ProtectionDomain domain, Permission permission) {
return getPermissions(domain).implies(permission);
}
private void addDefaultPermissions(Permissions permissions) {
// Add basic permissions for all authenticated users
permissions.add(new RuntimePermission("accessDeclaredMembers"));
permissions.add(new PropertyPermission("user.*", "read"));
permissions.add(new FilePermission("/tmp/-", "read,write"));
}
}
// Policy setup
class PolicySetup {
static {
// Set custom policy
Policy.setPolicy(new JAASPolicy());
System.setSecurityManager(new SecurityManager());
}
public static void setup() {
// Static initializer does the setup
}
}

Advanced JAAS Features

Example 6: Multi-Module Authentication Stack

public class MultiModuleAuthentication {
public static void main(String[] args) {
try {
// Configuration with multiple LoginModules
LoginContext loginContext = new LoginContext(
"CompositeLogin", 
new InteractiveCallbackHandler()
);
loginContext.login();
Subject subject = loginContext.getSubject();
System.out.println("Authentication successful!");
System.out.println("Subject principals:");
subject.getPrincipals().forEach(System.out::println);
// Execute with subject's permissions
Subject.doAs(subject, (PrivilegedAction<Void>) () -> {
System.out.println("Executing with subject's permissions");
// Perform authorized actions
return null;
});
loginContext.logout();
} catch (LoginException e) {
System.err.println("Authentication failed: " + e.getMessage());
}
}
}
class InteractiveCallbackHandler implements CallbackHandler {
private Scanner scanner = new Scanner(System.in);
@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (Callback callback : callbacks) {
if (callback instanceof NameCallback) {
NameCallback nameCallback = (NameCallback) callback;
System.out.print(nameCallback.getPrompt());
String name = scanner.nextLine();
nameCallback.setName(name);
} else if (callback instanceof PasswordCallback) {
PasswordCallback passwordCallback = (PasswordCallback) callback;
System.out.print(passwordCallback.getPrompt());
String password = scanner.nextLine();
passwordCallback.setPassword(password.toCharArray());
} else if (callback instanceof TextOutputCallback) {
TextOutputCallback textCallback = (TextOutputCallback) callback;
switch (textCallback.getMessageType()) {
case TextOutputCallback.INFORMATION:
System.out.println("INFO: " + textCallback.getMessage());
break;
case TextOutputCallback.WARNING:
System.out.println("WARNING: " + textCallback.getMessage());
break;
case TextOutputCallback.ERROR:
System.out.println("ERROR: " + textCallback.getMessage());
break;
}
} else if (callback instanceof ChoiceCallback) {
ChoiceCallback choiceCallback = (ChoiceCallback) callback;
System.out.println(choiceCallback.getPrompt());
String[] options = choiceCallback.getOptions();
for (int i = 0; i < options.length; i++) {
System.out.println((i + 1) + ". " + options[i]);
}
System.out.print("Enter choice: ");
int choice = scanner.nextInt();
scanner.nextLine(); // consume newline
choiceCallback.setSelectedIndex(choice - 1);
} else {
throw new UnsupportedCallbackException(callback, "Unsupported callback type");
}
}
}
}

Example 7: JAAS in Web Applications

// Web application authentication filter
public class JAASAuthFilter implements Filter {
private static final String LOGIN_CONFIG = "WebAppLogin";
@Override
public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpSession session = httpRequest.getSession();
try {
// Check if user is already authenticated
Subject subject = (Subject) session.getAttribute("javax.security.auth.subject");
if (subject == null) {
// Attempt authentication
subject = authenticateUser(httpRequest);
if (subject != null) {
session.setAttribute("javax.security.auth.subject", subject);
} else {
httpResponse.sendRedirect("/login.jsp");
return;
}
}
// Continue with authenticated user
chain.doFilter(request, response);
} catch (LoginException e) {
httpResponse.sendRedirect("/login.jsp?error=auth_failed");
}
}
private Subject authenticateUser(HttpServletRequest request) throws LoginException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if (username == null || password == null) {
return null;
}
CallbackHandler callbackHandler = new HttpRequestCallbackHandler(username, password);
LoginContext loginContext = new LoginContext(LOGIN_CONFIG, callbackHandler);
loginContext.login();
return loginContext.getSubject();
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// Initialization code
}
@Override
public void destroy() {
// Cleanup code
}
}
// CallbackHandler for HTTP requests
class HttpRequestCallbackHandler implements CallbackHandler {
private final String username;
private final String password;
public HttpRequestCallbackHandler(String username, String password) {
this.username = username;
this.password = password;
}
@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (Callback callback : callbacks) {
if (callback instanceof NameCallback) {
((NameCallback) callback).setName(username);
} else if (callback instanceof PasswordCallback) {
((PasswordCallback) callback).setPassword(password.toCharArray());
} else {
throw new UnsupportedCallbackException(callback, "Unsupported callback type");
}
}
}
}
// Role-based web authorization
public class RoleAuthorizationFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpSession session = httpRequest.getSession();
Subject subject = (Subject) session.getAttribute("javax.security.auth.subject");
if (subject == null || !hasRequiredRole(subject, httpRequest)) {
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Insufficient privileges");
return;
}
chain.doFilter(request, response);
}
private boolean hasRequiredRole(Subject subject, HttpServletRequest request) {
String requiredRole = getRequiredRole(request);
if (requiredRole == null) {
return true; // No specific role required
}
return subject.getPrincipals(RolePrincipal.class).stream()
.anyMatch(principal -> principal.getName().equals(requiredRole));
}
private String getRequiredRole(HttpServletRequest request) {
// Extract required role from request URL or configuration
String path = request.getServletPath();
if (path.startsWith("/admin/")) {
return "ADMIN";
} else if (path.startsWith("/user/")) {
return "USER";
} else if (path.startsWith("/api/")) {
return "API_USER";
}
return null;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}

JAAS Configuration Management

Example 8: Dynamic Configuration

import javax.security.auth.login.Configuration;
import java.security.Security;
import java.util.*;
public class DynamicJAASConfiguration extends Configuration {
private final Map<String, AppConfigurationEntry[]> configurations;
public DynamicJAASConfiguration() {
this.configurations = new HashMap<>();
}
public void addConfiguration(String name, AppConfigurationEntry[] entries) {
configurations.put(name, entries);
}
public void removeConfiguration(String name) {
configurations.remove(name);
}
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
return configurations.get(name);
}
// Method to create configuration from properties
public static DynamicJAASConfiguration fromProperties(Properties props) {
DynamicJAASConfiguration config = new DynamicJAASConfiguration();
// Parse properties and create configurations
Set<String> configNames = new HashSet<>();
for (String key : props.stringPropertyNames()) {
if (key.contains(".")) {
configNames.add(key.substring(0, key.indexOf('.')));
}
}
for (String configName : configNames) {
AppConfigurationEntry[] entries = parseConfigurationEntries(props, configName);
config.addConfiguration(configName, entries);
}
return config;
}
private static AppConfigurationEntry[] parseConfigurationEntries(Properties props, String configName) {
List<AppConfigurationEntry> entries = new ArrayList<>();
int index = 0;
while (true) {
String entryKey = configName + "." + index;
String moduleClass = props.getProperty(entryKey + ".module");
if (moduleClass == null) {
break;
}
String flag = props.getProperty(entryKey + ".flag", "required");
Map<String, String> options = new HashMap<>();
// Parse options
for (String propName : props.stringPropertyNames()) {
if (propName.startsWith(entryKey + ".option.")) {
String optionName = propName.substring(entryKey.length() + 8); // ".option." length
options.put(optionName, props.getProperty(propName));
}
}
AppConfigurationEntry.LoginModuleControlControlFlag controlFlag;
switch (flag.toLowerCase()) {
case "required":
controlFlag = AppConfigurationEntry.LoginModuleControlControlFlag.REQUIRED;
break;
case "requisite":
controlFlag = AppConfigurationEntry.LoginModuleControlControlFlag.REQUISITE;
break;
case "sufficient":
controlFlag = AppConfigurationEntry.LoginModuleControlControlFlag.SUFFICIENT;
break;
case "optional":
controlFlag = AppConfigurationEntry.LoginModuleControlControlFlag.OPTIONAL;
break;
default:
controlFlag = AppConfigurationEntry.LoginModuleControlControlFlag.REQUIRED;
}
entries.add(new AppConfigurationEntry(
moduleClass,
controlFlag,
options
));
index++;
}
return entries.toArray(new AppConfigurationEntry[0]);
}
}
// Usage example
public class DynamicConfigurationExample {
public static void main(String[] args) {
Properties props = new Properties();
props.setProperty("WebLogin.0.module", "com.example.DatabaseLoginModule");
props.setProperty("WebLogin.0.flag", "required");
props.setProperty("WebLogin.0.option.dbUrl", "jdbc:mysql://localhost/auth");
props.setProperty("WebLogin.0.option.debug", "true");
props.setProperty("WebLogin.1.module", "com.example.LDAPLoginModule");
props.setProperty("WebLogin.1.flag", "sufficient");
props.setProperty("WebLogin.1.option.ldapUrl", "ldap://localhost");
DynamicJAASConfiguration config = DynamicJAASConfiguration.fromProperties(props);
Configuration.setConfiguration(config);
// Now use the configuration
try {
LoginContext ctx = new LoginContext("WebLogin", new SimpleCallbackHandler());
ctx.login();
// ... rest of authentication logic
} catch (LoginException e) {
e.printStackTrace();
}
}
}

Best Practices and Security Considerations

  1. Use strong password hashing in LoginModules
  2. Implement proper error handling to avoid information leakage
  3. Use secure communication for remote authentication
  4. Regularly update and patch custom LoginModules
  5. Implement proper session management in web applications
  6. Use principle of least privilege in authorization policies
  7. Audit and log authentication attempts
  8. Secure configuration files and sensitive data
  9. Use HTTPS for web-based authentication
  10. Implement account lockout mechanisms for brute force protection

JAAS provides a robust framework for implementing authentication and authorization in Java applications. By following these patterns and best practices, you can build secure, flexible, and maintainable security solutions.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper