On this page
JAAS Basics
Java Authentication and Authorization Service (JAAS) is a Java SE security API (since Java 1.4) for pluggable authentication. It defines a standard way to authenticate users and enforce access control.
Core Components
| Component | Role |
|---|---|
| LoginModule | Performs authentication |
| Subject | Represents a user/principal |
| CallbackHandler | Collects credentials from the user |
| Principal | Identity (username, group) |
| Credential | Proof of identity (password, certificate) |
Architecture
Application → LoginContext → LoginModule(s) → Subject
↕
CallbackHandler (username/password)
LoginModule Implementation
public class DatabaseLoginModule implements LoginModule {
private Subject subject;
private CallbackHandler handler;
private boolean succeeded = false;
private String username;
private char[] password;
@Override
public void initialize(Subject subject, CallbackHandler handler,
Map<String, ?> sharedState, Map<String, ?> options) {
this.subject = subject;
this.handler = handler;
}
@Override
public boolean login() throws LoginException {
Callback[] callbacks = new Callback[] {
new NameCallback("Username: "),
new PasswordCallback("Password: ", false)
};
try {
handler.handle(callbacks);
username = ((NameCallback) callbacks[0]).getName();
password = ((PasswordCallback) callbacks[1]).getPassword();
} catch (IOException | UnsupportedCallbackException e) {
throw new LoginException(e.getMessage());
}
if (!validateCredentials(username, password)) {
throw new FailedLoginException("Invalid credentials");
}
succeeded = true;
return true;
}
@Override
public boolean commit() throws LoginException {
if (!succeeded) return false;
subject.getPrincipals().add(new UserPrincipal(username));
return true;
}
@Override
public boolean abort() throws LoginException { return false; }
@Override
public boolean logout() throws LoginException {
subject.getPrincipals().remove(new UserPrincipal(username));
return true;
}
}
Using LoginContext
LoginContext lc = new LoginContext("DatabaseLogin",
new CallbackHandler() {
@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (Callback cb : callbacks) {
if (cb instanceof NameCallback) ((NameCallback) cb).setName("alice");
if (cb instanceof PasswordCallback) ((PasswordCallback) cb).setPassword("secret".toCharArray());
}
}
});
lc.login();
Subject subject = lc.getSubject();
// subject now contains UserPrincipal("alice")
lc.logout();
JAAS Configuration
jaas.config:
DatabaseLogin {
com.example.security.DatabaseLoginModule required
debug=true;
};
Run with:
java -Djava.security.auth.login.config=jaas.config MyApplication
Principal
public class UserPrincipal implements Principal {
private final String name;
public UserPrincipal(String name) { this.name = name; }
@Override
public String getName() { return name; }
@Override
public boolean equals(Object o) {
if (!(o instanceof UserPrincipal)) return false;
return name.equals(((UserPrincipal) o).name);
}
@Override
public int hashCode() { return name.hashCode(); }
}
JAAS vs Modern Frameworks
| Aspect | JAAS | Spring Security |
|---|---|---|
| Standard | Java SE standard | Spring ecosystem |
| Complexity | High boilerplate | Abstractions provided |
| OAuth2/JWT | Not built-in | Full support |
| Usage today | Legacy systems | Modern applications |
Best Practices
- Use JAAS only when required by legacy infrastructure (J2EE, Kerberos)
- Prefer Spring Security or Shiro for new Java applications
- Always clear password char arrays after use
- Chain multiple LoginModules for multi-factor authentication
- Store JAAS config outside the application JAR