37

Have config (applicationContext-security.xml):

<authentication-manager alias="authenticationManager">
    <authentication-provider>
    <password-encoder hash="sha"/>
        <jdbc-user-service data-source-ref="dataSource"/>
    </authentication-provider>
</authentication-manager>

from other side have SQLs from my dataSource(it's JdbcDaoImpl):

...
    public static final String DEF_USERS_BY_USERNAME_QUERY =
            "select username,password,enabled " +
            "from users " +
            "where username = ?";
...

There is now word about sha in this code,so password selected from standard Spring Security users table not encoded.

Perhaps, I should provide some sha attribute for password column in my hibernate mapping config here:

<class name="model.UserDetails" table="users">
    <id name="id">
        <generator class="increment"/>
    </id>
    <property name="username" column="username"/>
    <property name="password" column="password"/>
    <property name="enabled" column="enabled"/>
    <property name="mail" column="mail"/>
    <property name="city" column="city"/>
    <property name="confirmed" column="confirmed"/>
    <property name="confirmationCode" column="confirmation_code"/>

    <set name="authorities" cascade="all" inverse="true">
        <key column="id" not-null="true"/>
        <one-to-many class="model.Authority"/>
    </set>

</class>

For now password saved to DB as is,but should be encoded.

How to friend applicationContext config and DB queries to be the same password encoding?

falsarella
  • 12,217
  • 9
  • 69
  • 115
sergionni
  • 13,290
  • 42
  • 132
  • 189

7 Answers7

82

If you are choosing a hashing system yourself, rather than building an app using an existing database which already contains hashed passwords, then you should make sure your hashing algorithm also uses a salt. Don't just use a plain digest.

A good choice is bcrypt, which we now support directly in Spring Security 3.1 via the BCryptPasswordEncoder (implemented using jBCrypt). This automatically generates a salt and concatenates it with the hash value in a single String.

Some databases have built-in support for hashing (e.g. Postgres). Otherwise, you need to hash the password yourself before passing it to JDBC:

String password = "plaintextPassword";
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String hashedPassword = passwordEncoder.encode(password);

That's all you need to do to encode the passwords when you create a user.

For authentication, you would use something like:

<bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

<bean id="authProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
  <property name="userDetailsService" ref="yourJdbcUserService" />
  <property name="passwordEncoder" ref="encoder" />
</bean>
Lorenzo Polidori
  • 10,332
  • 10
  • 51
  • 60
Shaun the Sheep
  • 22,353
  • 1
  • 72
  • 100
  • about salt,I know, it's the next phase of my security challenge)) – sergionni Dec 16 '11 at 09:25
  • 4
    If you use something like bcrypt, then it is automatically handled for you, so there isn't really any challenge :-). You don't have to handle the salt yourself. – Shaun the Sheep Dec 16 '11 at 10:58
  • @LukeTaylor Could you show how it would be configured in the applicationContext-security.xml? – falsarella Dec 16 '11 at 11:22
  • @falsarella You mean for authentication? I've added the config for an AuthenticationProvider bean. – Shaun the Sheep Dec 16 '11 at 15:35
  • @LukeTaylor,do you consider `org.springframework.security.crypto.password.PasswordEncoder` in your example?thank you. – sergionni Dec 30 '11 at 13:46
  • 2
    I am wondering how BCryptPasswordEncoder works. Does it generate one salt for all passwords or a separate salt for each ? If so where does it store the user-salt ? – Adelin Apr 08 '15 at 14:25
  • Or maybe I am mixing Hashing with Encoding ?! – Adelin Apr 08 '15 at 14:30
  • Another question I am asking myself why doesn't Spring Security provide a Hashing Algorithm rather than encrypting one. – Adelin Apr 08 '15 at 14:36
  • 1
    BCrypt isn't an encryption algorithm and the salt is stored in the same string. Perhaps the [Wikipedia article on Bcrypt](http://en.wikipedia.org/wiki/Bcrypt) will help. – Shaun the Sheep Apr 08 '15 at 14:44
12

A little more explanation on the accepted answer. Hope it helps someone.

Hash the password yourself before putting it to database:

String password = "plaintextPassword";
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String hashedPassword = passwordEncoder.encode(password);

Add BCryptPasswordEncoder bean to your security-config.xml

Add passwordEncoder as a property to Authentication Provider class. Autowire it or provide setter and getter methods.

@AutoWired
private BCryptPasswordEncoder passwordEncoder;

Get the property while you authendicate user for login

<bean id="dbAuthenticationProvider" class="mypackage.auth.spring.DBAuthenticationProvider" >
    <property name="dataSource" ref="routingDataSource"></property>
    <property name="passwordEncoder" ref="encoder" />
    <property name="passwordQuery"
        value="select password as password from tbl where username=:username">
    </property> 
</bean>

And in the authenticating class match both passwords

 new BCryptPasswordEncoder().matches(plainTextPasswdFromUserInput, hashedPasswdFromDb)
Venugopal Madathil
  • 2,031
  • 3
  • 34
  • 44
5

In a simple way can you do something like in applicationContext-security.xml

<authentication-manager alias="authenticationManager">
   <authentication-provider>
    <password-encoder ref="encoder"/>
    <jdbc-user-service data-source-ref="dataSource"
       users-by-username-query="
          select username,password, enabled 
          from principal where username=?" 
       authorities-by-username-query="
          select p.username, a.authority from principal p, authority a
          where p.id = a.principal_id and p.username=?" 
    />
   </authentication-provider>
</authentication-manager> 

  <beans:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

In Java

public static String encodePasswordWithBCrypt(String plainPassword){
    return new BCryptPasswordEncoder().encode(plainPassword);
}

Then test it

System.out.println(encodePasswordWithBCrypt("fsdfd"));
Sully
  • 14,672
  • 5
  • 54
  • 79
user2368100
  • 51
  • 1
  • 1
5

Using Spring Security 3.1, try this:

<authentication-manager alias="authenticationManager">
    <authentication-provider user-service-ref="service">
        <password-encoder hash="sha"/>
        <jdbc-user-service data-source-ref="dataSource"/>
    </authentication-provider>
</authentication-manager>

<beans:bean id="dataSource" ...>
    ...
</beans:bean>

<beans:bean id="service" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
        <beans:property name="dataSource" ref="dataSource"/>
        ...
</beans:bean>

What's new: authentication-provider points to service and service points to datasource.

Edit: In Java you will have to encode the password with something like this:

DigestUtils.sha(request.getParameter("password"));

Warn: Be careful! Do not mix SHA with MD5!

If you set the password-encoder of the authentication-provider as SHA, you need to encode in Java the same way to keep consistent. But if you enconde in Java as MD5, as the sample you found, do not forget to set the hash to "md5". DigestUtils also provides md5 encoder:

DigestUtils.md5(request.getParameter("password"));
falsarella
  • 12,217
  • 9
  • 69
  • 115
  • yes,I know,but you didn't ask on my question how to provide,that user password saved encoded to DB – sergionni Dec 15 '11 at 18:02
  • 1
    Well, I don't know what you mean by provide, but to persist, I have used [DigestUtils](http://commons.apache.org/codec/apidocs/org/apache/commons/codec/digest/DigestUtils.html). – falsarella Dec 15 '11 at 18:09
  • 1
    @sergionni: sorry, but I don't know a configuration of applicationContext to automatically save the password encoded, as I said, I have used DigestUtils from Apache commons. – falsarella Dec 15 '11 at 18:14
  • thank you for point,never heard about digest.So,should I provide digest in order my password to be encrypted?this example:http://static.springsource.org/spring-security/site/docs/3.0.x/reference/basic.html – sergionni Dec 15 '11 at 19:11
  • ahhh,looks like I understand, I should encode my password in Java DAO class, when persisting my `UserDetails` entity with following:`MessageDigest messageDigest = MessageDigest.getInstance("MD5"); ...`here is full explanation:http://stackoverflow.com/questions/1821082/spring-security-encypt-md5 – sergionni Dec 15 '11 at 19:30
  • @sergionni Look at my edit, I wrote a sample to show how the DigestUtils implementation makes it easy to encode. Simple and clean. – falsarella Dec 16 '11 at 11:18
  • falsarella, thank your for one more hint in encoding and digesting.This code looks short and clean. – sergionni Dec 16 '11 at 12:40
4

Just a tip for doing it with annotations

@Configuration
@EnableWebSecurity
@PropertySource("classpath://configs.properties")
public class SecurityContextConfig extends WebSecurityConfigurerAdapter {


@Autowired
@Qualifier("userDetailsService")
private UserDetailsService userDetailsService;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(getPasswordEncoder());
}


@Bean(name = "passwordEncoder")
public PasswordEncoder getPasswordEncoder(){
    return new BCryptPasswordEncoder();     
 }

}
Alireza Fattahi
  • 42,517
  • 14
  • 123
  • 173
2

The accepted answer is right. I tested it with spring 3.1 and BCrypt encode algorithm.

When create a user.

PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
userEntity.setPassword(passwordEncoder.encode(userEntity.getPassword()));
userDao.save(userEntity);

When the user login, Remember, use the plain password (not hashed). just like:

Authentication request = new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
Authentication result = authenticationManager.authenticate(request);
SecurityContextHolder.getContext().setAuthentication(result);

Here is security-config:

    <bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
        <property name="userDetailsService" ref="userService" />
        <property name="hideUserNotFoundExceptions" value="false" />
        <property name="passwordEncoder" ref="encoder" />
    </bean>

Hope it will help somebody!

maoyang
  • 1,067
  • 1
  • 11
  • 11
1

with 3.1.x this mapping doesnt work for auth. Working way is:

<beans:bean id='bCryptPasswordEncoder' class='org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder'></beans:bean>

<authentication-manager>
  <authentication-provider user-service-ref="userDetailsService">
          <password-encoder ref="bCryptPasswordEncoder"/>
  </authentication-provider>
</authentication-manager>
z0mb1ek
  • 899
  • 9
  • 16