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
- Use strong password hashing in LoginModules
- Implement proper error handling to avoid information leakage
- Use secure communication for remote authentication
- Regularly update and patch custom LoginModules
- Implement proper session management in web applications
- Use principle of least privilege in authorization policies
- Audit and log authentication attempts
- Secure configuration files and sensitive data
- Use HTTPS for web-based authentication
- 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.