We use JAAS in a heavily loaded web server. The configuration file is loaded from a file,
System.setProperty("java.security.auth.login.config", "/config/jaas.config");
During profiling, we noticed that the configuration is loaded from file for every login attempt. This is an I/O operation we try to avoid. Is there anyway to store the JAAS configuration in memory?
Following code snippet connects to a PostgreSQL database (using pgjdbc and HikariCP) with an in-memory JAAS configuration, that is, no Configuration
files are required:
package com.vlkan.kerberos.auth;
import com.google.common.collect.ImmutableMap;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;
import static com.google.common.base.Preconditions.checkArgument;
public enum Main {
private static final String JAAS_CONFIG_NAME = "pgjdbc";
public static void main(String[] args) throws LoginException, SQLException {
String jdbcUrl = "jdbc:postgresql://host/dbname";
String jdbcDriver = "org.postgresql.Driver";
String username = "user";
String password = "pass";
Configuration jaasConfig = createJaasConfig();
Configuration.setConfiguration(jaasConfig);
HikariConfig hikariConfig = createHikariConfig(jdbcUrl, jdbcDriver, username, password);
HikariDataSource dataSource = new HikariDataSource(hikariConfig);
try (Connection connection = dataSource.getConnection()) {
try (PreparedStatement statement = connection.prepareStatement("SELECT 1")) {
try (ResultSet resultSet = statement.executeQuery()) {
boolean next = resultSet.next();
checkArgument(next, "no results");
int result = resultSet.getInt(1);
checkArgument(result == 1, "expecting: 1, found: %s", result);
System.out.println("ok");
}
}
}
}
private static HikariConfig createHikariConfig(String jdbcUrl, String jdbcDriver, String username, String password) {
HikariConfig config = new HikariConfig();
config.setDriverClassName(jdbcDriver);
config.setJdbcUrl(jdbcUrl);
config.setUsername(username);
config.setPassword(password);
fixKerberosProperties(config, username, password);
return config;
}
private static void fixKerberosProperties(HikariConfig config, String username, String password) {
Properties properties = new Properties();
properties.setProperty("user", username);
properties.setProperty("password", password);
properties.setProperty("JAASConfigName", JAAS_CONFIG_NAME);
config.setDataSourceProperties(properties);
}
private static Configuration createJaasConfig() {
// Create entry options.
Map<String, Object> options = ImmutableMap.of(
"useFirstPass", "false", // Do *not* use javax.security.auth.login.{name,password} from shared state.
"debug", "true" // Output debug (including plain text username and password!) messages.
);
// Create entries.
AppConfigurationEntry[] entries = {
new AppConfigurationEntry(
"com.sun.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
options)
};
// Create configuration.
return new Configuration() {
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
checkArgument(JAAS_CONFIG_NAME.equals(name));
return entries;
}
};
}
}
You could implement your own Configuration. The javadoc says:
The default Configuration implementation can be changed by setting the value of the "login.configuration.provider" security property (in the Java security properties file) to the fully qualified name of the desired Configuration implementation class.
The default implementation com.sun.security.auth.login.ConfigFile (source) appears to load the file each time the class is instantiated. You could cache the contents. No comment on the security aspects either way.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With