Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,19 @@
import java.util.List;
import java.util.Set;

import org.springframework.context.ApplicationContextException;
import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;

import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.authority.AuthorityUtils;
Expand Down Expand Up @@ -108,8 +114,9 @@
* @author Ben Alex
* @author colin sampaleanu
* @author Luke Taylor
* @author Yanming Zhou
*/
public class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService, MessageSourceAware {
public class JdbcDaoImpl implements UserDetailsService, MessageSourceAware, InitializingBean {

public static final String DEFAULT_USER_SCHEMA_DDL_LOCATION = "org/springframework/security/core/userdetails/jdbc/users.ddl";

Expand All @@ -131,6 +138,10 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService, M
+ "where gm.username = ? " + "and g.id = ga.group_id " + "and g.id = gm.group_id";
// @formatter:on

protected final Log logger = LogFactory.getLog(getClass());

private @Nullable JdbcTemplate jdbcTemplate;

protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();

private String authoritiesByUsernameQuery;
Expand All @@ -153,6 +164,60 @@ public JdbcDaoImpl() {
this.groupAuthoritiesByUsernameQuery = DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY;
}

/**
* Set the JDBC DataSource to be used by this DAO.
*/
public final void setDataSource(DataSource dataSource) {
if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}

public final @Nullable DataSource getDataSource() {
return (this.jdbcTemplate != null) ? this.jdbcTemplate.getDataSource() : null;
}

/**
* Set the JdbcTemplate for this DAO explicitly, as an alternative to specifying a
* DataSource.
*/
public final void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

/**
* Return the JdbcTemplate for this DAO, pre-initialized with the DataSource or set
* explicitly.
*/
public final @Nullable JdbcTemplate getJdbcTemplate() {
return this.jdbcTemplate;
}

@Override
public final void afterPropertiesSet() {
// Let abstract subclasses check their configuration.
checkDaoConfig();

// Let concrete implementations initialize themselves.
try {
initDao();
}
catch (Exception ex) {
throw new BeanInitializationException("Initialization of DAO failed", ex);
}
}

protected void checkDaoConfig() {
if (this.jdbcTemplate == null) {
throw new IllegalArgumentException("'dataSource' or 'jdbcTemplate' is required");
}
}

protected void initDao() {
Assert.isTrue(this.enableAuthorities || this.enableGroups,
"Use of either authorities or groups must be enabled");
}

/**
* @return the messages
*/
Expand All @@ -174,12 +239,6 @@ public String getUsersByUsernameQuery() {
return this.usersByUsernameQuery;
}

@Override
protected void initDao() throws ApplicationContextException {
Assert.isTrue(this.enableAuthorities || this.enableGroups,
"Use of either authorities or groups must be enabled");
}

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
List<UserDetails> users = loadUsersByUsername(username);
Expand Down Expand Up @@ -219,15 +278,15 @@ protected List<UserDetails> loadUsersByUsername(String username) {
return new User(username1, password, enabled, true, true, true, AuthorityUtils.NO_AUTHORITIES);
};
// @formatter:on
return getJdbc().query(this.usersByUsernameQuery, mapper, username);
return requireJdbcTemplate().query(this.usersByUsernameQuery, mapper, username);
}

/**
* Loads authorities by executing the SQL from <tt>authoritiesByUsernameQuery</tt>.
* @return a list of GrantedAuthority objects for the user
*/
protected List<GrantedAuthority> loadUserAuthorities(String username) {
return getJdbc().query(this.authoritiesByUsernameQuery, (rs, rowNum) -> {
return requireJdbcTemplate().query(this.authoritiesByUsernameQuery, (rs, rowNum) -> {
String roleName = JdbcDaoImpl.this.rolePrefix + rs.getString(2);
return new SimpleGrantedAuthority(roleName);
}, username);
Expand All @@ -239,7 +298,7 @@ protected List<GrantedAuthority> loadUserAuthorities(String username) {
* @return a list of GrantedAuthority objects for the user
*/
protected List<GrantedAuthority> loadGroupAuthorities(String username) {
return getJdbc().query(this.groupAuthoritiesByUsernameQuery, (rs, rowNum) -> {
return requireJdbcTemplate().query(this.groupAuthoritiesByUsernameQuery, (rs, rowNum) -> {
String roleName = getRolePrefix() + rs.getString(3);
return new SimpleGrantedAuthority(roleName);
}, username);
Expand Down Expand Up @@ -375,8 +434,8 @@ public void setMessageSource(MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}

private JdbcTemplate getJdbc() {
JdbcTemplate template = getJdbcTemplate();
protected JdbcTemplate requireJdbcTemplate() {
JdbcTemplate template = this.jdbcTemplate;
Assert.notNull(template, "JdbcTemplate cannot be null");
return template;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,9 @@
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;

import org.springframework.context.ApplicationContextException;
import org.springframework.core.log.LogMessage;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.security.access.AccessDeniedException;
Expand Down Expand Up @@ -67,6 +65,7 @@
*
* @author Luke Taylor
* @author Junhyeok Lee
* @author Yanming Zhou
* @since 2.0
*/
public class JdbcUserDetailsManager extends JdbcDaoImpl
Expand Down Expand Up @@ -209,7 +208,7 @@ public void setGrantedAuthorityMapper(RowMapper<GrantedAuthority> mapper) {
}

@Override
protected void initDao() throws ApplicationContextException {
protected void initDao() {
if (this.authenticationManager == null) {
this.logger.info(
"No authentication manager set. Reauthentication of users when changing passwords will not be performed.");
Expand Down Expand Up @@ -471,12 +470,6 @@ private int findGroupId(String group) {
return groupId;
}

private JdbcTemplate requireJdbcTemplate() {
JdbcTemplate jdbc = getJdbcTemplate();
Assert.notNull(jdbc, "JdbcTemplate cannot be null");
return jdbc;
}

/**
* Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
* the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
* @author Luke Taylor
* @author dae won
* @author Junhyeok Lee
* @author Yanming Zhou
*/
public class JdbcUserDetailsManagerTests {

Expand Down Expand Up @@ -104,7 +105,7 @@ public void initializeManagerAndCreateTables() {
this.manager.setDeleteUserAuthoritiesSql(JdbcUserDetailsManager.DEF_DELETE_USER_AUTHORITIES_SQL);
this.manager.setDeleteUserSql(JdbcUserDetailsManager.DEF_DELETE_USER_SQL);
this.manager.setChangePasswordSql(JdbcUserDetailsManager.DEF_CHANGE_PASSWORD_SQL);
this.manager.initDao();
this.manager.afterPropertiesSet();
this.template = this.manager.getJdbcTemplate();
this.template.execute("create table users(username varchar(20) not null primary key,"
+ "password varchar(20) not null, enabled boolean not null)");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,28 @@
import java.sql.SQLException;
import java.util.Date;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;

import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.log.LogMessage;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

/**
* JDBC based persistent login token repository implementation.
*
* @author Luke Taylor
* @author Yanming Zhou
* @since 2.0
*/
public class JdbcTokenRepositoryImpl extends JdbcDaoSupport implements PersistentTokenRepository {
public class JdbcTokenRepositoryImpl implements PersistentTokenRepository, InitializingBean {

/** Default SQL for creating the database table to store the tokens */
public static final String CREATE_TABLE_SQL = "create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, "
Expand All @@ -53,6 +59,10 @@ public class JdbcTokenRepositoryImpl extends JdbcDaoSupport implements Persisten
/** The default SQL used by <tt>removeUserTokens</tt> */
public static final String DEF_REMOVE_USER_TOKENS_SQL = "delete from persistent_logins where username = ?";

protected final Log logger = LogFactory.getLog(getClass());

private @Nullable JdbcTemplate jdbcTemplate;

private String tokensBySeriesSql = DEF_TOKEN_BY_SERIES_SQL;

private String insertTokenSql = DEF_INSERT_TOKEN_SQL;
Expand All @@ -64,6 +74,54 @@ public class JdbcTokenRepositoryImpl extends JdbcDaoSupport implements Persisten
private boolean createTableOnStartup;

@Override
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
// Let abstract subclasses check their configuration.
checkDaoConfig();

// Let concrete implementations initialize themselves.
try {
initDao();
}
catch (Exception ex) {
throw new BeanInitializationException("Initialization of DAO failed", ex);
}
}

/**
* Set the JDBC DataSource to be used by this DAO.
*/
public final void setDataSource(DataSource dataSource) {
if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}

public final @Nullable DataSource getDataSource() {
return (this.jdbcTemplate != null) ? this.jdbcTemplate.getDataSource() : null;
}

/**
* Set the JdbcTemplate for this DAO explicitly, as an alternative to specifying a
* DataSource.
*/
public final void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

/**
* Return the JdbcTemplate for this DAO, pre-initialized with the DataSource or set
* explicitly.
*/
public final @Nullable JdbcTemplate getJdbcTemplate() {
return this.jdbcTemplate;
}

protected void checkDaoConfig() {
if (this.jdbcTemplate == null) {
throw new IllegalArgumentException("'dataSource' or 'jdbcTemplate' is required");
}
}

protected void initDao() {
if (this.createTableOnStartup) {
getTemplate().execute(CREATE_TABLE_SQL);
Expand Down Expand Up @@ -128,7 +186,7 @@ public void setCreateTableOnStartup(boolean createTableOnStartup) {
}

private JdbcTemplate getTemplate() {
@Nullable JdbcTemplate result = super.getJdbcTemplate();
@Nullable JdbcTemplate result = this.jdbcTemplate;
if (result == null) {
throw new IllegalStateException("JdbcTemplate was removed");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@

/**
* @author Luke Taylor
* @author Yanming Zhou
*/
@ExtendWith(MockitoExtension.class)
public class JdbcTokenRepositoryImplTests {
Expand Down Expand Up @@ -78,7 +79,7 @@ public void populateDatabase() {
this.repo = new JdbcTokenRepositoryImpl();
ReflectionTestUtils.setField(this.repo, "logger", this.logger);
this.repo.setDataSource(dataSource);
this.repo.initDao();
this.repo.afterPropertiesSet();
this.template = this.repo.getJdbcTemplate();
this.template.execute("create table persistent_logins (username varchar(100) not null, "
+ "series varchar(100) not null, token varchar(500) not null, last_used timestamp not null)");
Expand Down Expand Up @@ -170,7 +171,7 @@ public void createTableOnStartupCreatesCorrectTable() {
this.repo = new JdbcTokenRepositoryImpl();
this.repo.setDataSource(dataSource);
this.repo.setCreateTableOnStartup(true);
this.repo.initDao();
this.repo.afterPropertiesSet();
this.template.queryForList("select username,series,token,last_used from persistent_logins");
}

Expand Down