From aed9d2a1d88f0aad44bd0bea32dc5ac5b1021627 Mon Sep 17 00:00:00 2001
From: Colin Sampaleanu
Date: Wed, 14 Apr 2004 21:30:59 +0000
Subject: [PATCH] initial cut at allowing pluggable digest strategy for use in
password handling in DaoAuthenticationProvider
---
.../dao/DaoAuthenticationProvider.java | 30 +++++---
.../providers/dao/MD5PasswordEncoder.java | 51 +++++++++++++
.../providers/dao/PasswordEncoder.java | 38 ++++++++++
.../dao/PlaintextPasswordEncoder.java | 50 +++++++++++++
.../providers/dao/SHAPasswordEncoder.java | 51 +++++++++++++
.../userdetails/jdbc/JdbcDaoImpl.java | 75 +++++++++++--------
6 files changed, 253 insertions(+), 42 deletions(-)
create mode 100644 core/src/main/java/org/acegisecurity/providers/dao/MD5PasswordEncoder.java
create mode 100644 core/src/main/java/org/acegisecurity/providers/dao/PasswordEncoder.java
create mode 100644 core/src/main/java/org/acegisecurity/providers/dao/PlaintextPasswordEncoder.java
create mode 100644 core/src/main/java/org/acegisecurity/providers/dao/SHAPasswordEncoder.java
diff --git a/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java b/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java
index cd1249084b..0f06d285f3 100644
--- a/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java
+++ b/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java
@@ -46,6 +46,7 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
//~ Instance fields ========================================================
private AuthenticationDao authenticationDao;
+ private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
private boolean ignorePasswordCase = false;
private boolean ignoreUsernameCase = true;
@@ -89,6 +90,21 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
return ignoreUsernameCase;
}
+ /**
+ * Sets the PasswordEncoder instance to be used to encode and validate
+ * passwords. If not set, {@link PlaintextPasswordEncoder} will be used by
+ * default.
+ *
+ * @param passwordEncoder The passwordEncoder to use
+ */
+ public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
+ this.passwordEncoder = passwordEncoder;
+ }
+
+ public PasswordEncoder getPasswordEncoder() {
+ return passwordEncoder;
+ }
+
public void afterPropertiesSet() throws Exception {
if (this.authenticationDao == null) {
throw new IllegalArgumentException(
@@ -116,15 +132,9 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
throw new BadCredentialsException("Bad credentials presented");
}
- if (!user.getPassword().toLowerCase().equals(authentication.getCredentials()
- .toString()
- .toLowerCase())) {
- throw new BadCredentialsException("Bad credentials presented");
- }
-
- if ((!this.ignorePasswordCase)
- && (!user.getPassword().equals(authentication.getCredentials()
- .toString()))) {
+ if (!passwordEncoder.isPasswordValid(user.getPassword(),
+ authentication.getCredentials().toString(), user,
+ ignorePasswordCase)) {
throw new BadCredentialsException("Bad credentials presented");
}
@@ -133,7 +143,7 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
}
return new UsernamePasswordAuthenticationToken(user.getUsername(),
- user.getPassword(), user.getAuthorities());
+ authentication.getCredentials().toString(), user.getAuthorities());
}
public boolean supports(Class authentication) {
diff --git a/core/src/main/java/org/acegisecurity/providers/dao/MD5PasswordEncoder.java b/core/src/main/java/org/acegisecurity/providers/dao/MD5PasswordEncoder.java
new file mode 100644
index 0000000000..e51f359efd
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/providers/dao/MD5PasswordEncoder.java
@@ -0,0 +1,51 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.dao;
+
+import org.apache.commons.codec.digest.DigestUtils;
+
+
+/**
+ *
+ * MD5 implementation of PasswordEncoder.
The ignorePasswordCase parameter is not used for this implementation.
A null password is encoded to the same value as an empty ("") password.
+ *
+ *
+ * @author colin sampaleanu
+ * @version $Id$
+ */
+public class MD5PasswordEncoder implements PasswordEncoder {
+ //~ Methods ================================================================
+
+ /* (non-Javadoc)
+ * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#isPasswordValid(net.sf.acegisecurity.providers.dao.User, java.lang.String, boolean)
+ */
+ public boolean isPasswordValid(String encPass, String rawPass,
+ Object saltSource, boolean ignorePasswordCase) {
+ String pass1 = "" + encPass;
+ String pass2 = DigestUtils.md5Hex("" + rawPass);
+
+ return pass1.equals(pass2);
+ }
+
+ /* (non-Javadoc)
+ * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#encodePassword(java.lang.String, java.lang.Object)
+ */
+ public String encodePassword(String rawPass, Object saltSource) {
+ return DigestUtils.md5Hex("" + rawPass);
+ }
+}
diff --git a/core/src/main/java/org/acegisecurity/providers/dao/PasswordEncoder.java b/core/src/main/java/org/acegisecurity/providers/dao/PasswordEncoder.java
new file mode 100644
index 0000000000..c7a91340ad
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/providers/dao/PasswordEncoder.java
@@ -0,0 +1,38 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.dao;
+
+import org.springframework.dao.DataAccessException;
+
+
+/**
+ *
+ * Describes authentication operations on a password, so that digest algorithms
+ * can be abstracted
+ *
+ *
+ * @author colin sampaleanu
+ * @version $Id$
+ */
+public interface PasswordEncoder {
+ //~ Methods ================================================================
+
+ public boolean isPasswordValid(String encPass, String rawPass,
+ Object saltSource, boolean ignorePasswordCase)
+ throws DataAccessException;
+
+ public String encodePassword(String rawPass, Object saltSource);
+}
diff --git a/core/src/main/java/org/acegisecurity/providers/dao/PlaintextPasswordEncoder.java b/core/src/main/java/org/acegisecurity/providers/dao/PlaintextPasswordEncoder.java
new file mode 100644
index 0000000000..e289740732
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/providers/dao/PlaintextPasswordEncoder.java
@@ -0,0 +1,50 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.dao;
+
+/**
+ *
+ * Plaintext implementation of PasswordEncoder.
+ *
+ *
+ * @author colin sampaleanu
+ * @version $Id$
+ */
+public class PlaintextPasswordEncoder implements PasswordEncoder {
+ //~ Methods ================================================================
+
+ /* (non-Javadoc)
+ * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#isPasswordValid(net.sf.acegisecurity.providers.dao.User, java.lang.String, boolean)
+ */
+ public boolean isPasswordValid(String encPass, String rawPass,
+ Object saltSource, boolean ignorePasswordCase) {
+ String pass1 = "" + encPass;
+ String pass2 = "" + rawPass;
+
+ if (!ignorePasswordCase) {
+ return pass1.equals(pass2);
+ } else {
+ return pass1.equalsIgnoreCase(pass2);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#encodePassword(java.lang.String, java.lang.Object)
+ */
+ public String encodePassword(String rawPass, Object saltSource) {
+ return rawPass;
+ }
+}
diff --git a/core/src/main/java/org/acegisecurity/providers/dao/SHAPasswordEncoder.java b/core/src/main/java/org/acegisecurity/providers/dao/SHAPasswordEncoder.java
new file mode 100644
index 0000000000..9b830b0904
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/providers/dao/SHAPasswordEncoder.java
@@ -0,0 +1,51 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.providers.dao;
+
+import org.apache.commons.codec.digest.DigestUtils;
+
+
+/**
+ *
+ * SHA implementation of PasswordEncoder.
The ignorePasswordCase parameter is not used for this implementation.
A null password is encoded to the same value as an empty ("") password.
+ *
+ *
+ * @author colin sampaleanu
+ * @version $Id$
+ */
+public class SHAPasswordEncoder implements PasswordEncoder {
+ //~ Methods ================================================================
+
+ /* (non-Javadoc)
+ * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#isPasswordValid(net.sf.acegisecurity.providers.dao.User, java.lang.String, boolean)
+ */
+ public boolean isPasswordValid(String encPass, String rawPass,
+ Object saltSource, boolean ignorePasswordCase) {
+ String pass1 = "" + encPass;
+ String pass2 = DigestUtils.shaHex("" + rawPass);
+
+ return pass1.equals(pass2);
+ }
+
+ /* (non-Javadoc)
+ * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#encodePassword(java.lang.String, java.lang.Object)
+ */
+ public String encodePassword(String rawPass, Object saltSource) {
+ return DigestUtils.shaHex("" + rawPass);
+ }
+}
diff --git a/core/src/main/java/org/acegisecurity/userdetails/jdbc/JdbcDaoImpl.java b/core/src/main/java/org/acegisecurity/userdetails/jdbc/JdbcDaoImpl.java
index 7702b95d4d..922980296c 100644
--- a/core/src/main/java/org/acegisecurity/userdetails/jdbc/JdbcDaoImpl.java
+++ b/core/src/main/java/org/acegisecurity/userdetails/jdbc/JdbcDaoImpl.java
@@ -48,11 +48,14 @@ import javax.sql.DataSource;
*
*
*
- * A default database structure is assumed, which most users of this class will
+ * A default database structure is assumed, (see {@link
+ * #DEF_USERS_BY_USERNAME_QUERY} and {@link
+ * #DEF_AUTHORITIES_BY_USERNAME_QUERY}, which most users of this class will
* need to override, if using an existing scheme. This may be done by setting
- * the default query strings used, or if that does not provide enough
- * flexibility, setting the actual {@link MappingSqlQuery} instances used to
- * query the database.
+ * the default query strings used. If this does not provide enough
+ * flexibility, another strategy would be to subclass this class and override
+ * the {@link MappingSqlQuery} instances used, via the {@link
+ * #initMappingSqlQueries()} extension point.
*
*
* @author Ben Alex
@@ -68,10 +71,11 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao {
//~ Instance fields ========================================================
- protected MappingSqlQuery authoritiesByUsernameMapping;
- protected MappingSqlQuery usersByUsernameMapping;
- protected String authoritiesByUsernameQuery;
- protected String usersByUsernameQuery;
+ private MappingSqlQuery authoritiesByUsernameMapping;
+ private MappingSqlQuery usersByUsernameMapping;
+ private String authoritiesByUsernameQuery;
+ private String rolePrefix = "ROLE_";
+ private String usersByUsernameQuery;
//~ Constructors ===========================================================
@@ -82,16 +86,6 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao {
//~ Methods ================================================================
- /**
- * Allows the default MappingSqlQuery used for retrieving authorities by
- * username to be overriden. This may be used when overriding the query
- * string alone is inadequate. Note that there is no point in setting this
- * property and also specifying a query string, since there will be no
- * way for the instance set by this property to get at the query string.
- * As such, the MappingSqlQuery should be self contained.
- *
- * @param authoritiesByUsernameQuery The authoritiesByUsernameQuery to set.
- */
public void setAuthoritiesByUsernameMapping(
MappingSqlQuery authoritiesByUsernameQuery) {
this.authoritiesByUsernameMapping = authoritiesByUsernameQuery;
@@ -120,15 +114,23 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao {
}
/**
- * Allows the default MappingSqlQuery used for retrieving users by username
- * to be overriden. This may be used when overriding the query string
- * alone is inadequate. Note that there is no point in setting this
- * property and also specifying a query string, since there will be no
- * way for the instance set by this property to get at the query string.
- * As such, the MappingSqlQuery should be self contained.
+ * Allows a default role prefix to be specified. If this is set to a
+ * non-empty value, then it is automatically prepended to any roles read
+ * in from the db. This may for example be used to add the
+ * ROLE_
prefix expected to exist in role names (by default)
+ * by some other Acegi Security framework classes, in the case that the
+ * prefix is not already present in the db.
*
- * @param usersByUsernameQuery The MappingSqlQuery to set.
+ * @param rolePrefix the new prefix
*/
+ public void setRolePrefix(String rolePrefix) {
+ this.rolePrefix = rolePrefix;
+ }
+
+ public String getRolePrefix() {
+ return rolePrefix;
+ }
+
public void setUsersByUsernameMapping(MappingSqlQuery usersByUsernameQuery) {
this.usersByUsernameMapping = usersByUsernameQuery;
}
@@ -183,8 +185,17 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao {
}
protected void initDao() throws ApplicationContextException {
- usersByUsernameMapping = new UsersByUsernameMapping(getDataSource());
- authoritiesByUsernameMapping = new AuthoritiesByUsernameMapping(getDataSource());
+ initMappingSqlQueries();
+ }
+
+ /**
+ * Extension point to allow other MappingSqlQuery objects to be substituted
+ * in a subclass
+ */
+ protected void initMappingSqlQueries() {
+ setUsersByUsernameMapping(new UsersByUsernameMapping(getDataSource()));
+ setAuthoritiesByUsernameMapping(new AuthoritiesByUsernameMapping(
+ getDataSource()));
}
//~ Inner Classes ==========================================================
@@ -201,8 +212,8 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao {
protected Object mapRow(ResultSet rs, int rownum)
throws SQLException {
- GrantedAuthorityImpl authority = new GrantedAuthorityImpl(rs
- .getString("authority"));
+ String roleName = rolePrefix + rs.getString(1);
+ GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName);
return authority;
}
@@ -220,9 +231,9 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao {
protected Object mapRow(ResultSet rs, int rownum)
throws SQLException {
- String username = rs.getString("username");
- String password = rs.getString("password");
- boolean enabled = rs.getBoolean("enabled");
+ String username = rs.getString(1);
+ String password = rs.getString(2);
+ boolean enabled = rs.getBoolean(3);
User user = new User(username, password, enabled,
new GrantedAuthority[] {new GrantedAuthorityImpl("HOLDER")});