I was struggling with setting up Salted SHA-256 passwords for customers. With salt as emails.
I found out some stuff to make it work. Thought of sharing so it might help others.
1. CHANGES IN applicationContext-security.xml
a. Setup salt-source: i.e. which property should be used as salt. I wanted it to be username [which is same as email by default]. You are free to use your own customer property.
NOTE: If you want your own salt [same for all users, you can skip this step]
Code: Select all
<bean id="blSaltSource" class="org.springframework.security.authentication.dao.ReflectionSaltSource">
<property name="userPropertyToUse" value="username" />
</bean>
b. Define a bean for your password encoder: I used SHA-256 algorithm.
Code: Select all
<bean id="mdPasswordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
<constructor-arg value="256"/>
</bean>
c. Configure Authentication Manager to use your encoder and salt: Third line in the below code refers to the bean defined in step (b). Fourth line in the below code refers to the bean being used for salting.
Code: Select all
<sec:authentication-manager alias="blAuthenticationManager">
<sec:authentication-provider user-service-ref="blUserDetailsService">
<sec:password-encoder hash="sha-256" ref="mdPasswordEncoder">
<sec:salt-source ref="blSaltSource" />
</sec:password-encoder>
</sec:authentication-provider>
</sec:authentication-manager>
If you want a common salt for all users, you can use the below code:-
Code: Select all
<sec:authentication-manager alias="blAuthenticationManager">
<sec:authentication-provider user-service-ref="blUserDetailsService">
<sec:password-encoder hash="sha-256" ref="mdPasswordEncoder">
<sec:salt-source system-wide="--your-salt-string--" />
</sec:password-encoder>
</sec:authentication-provider>
</sec:authentication-manager>
2. CREATE A UTILITY CLASS FOR PROVIDING ShaPasswordEncoder Object: This will be used for hashing the password entered by the user on front-end for validation.
Code: Select all
public class CommonUtils {
private static final ShaPasswordEncoder encoder = new ShaPasswordEncoder(256);
public static ShaPasswordEncoder getCustomerPasswordEncoder() {
return encoder;
}
}
3. OVERRIDE CustomerServiceImpl.registerCustomer() in CORE MODULE: This class uses the above utility class and encodes the password before registration.
Code: Select all
public class MDCustomerServiceImpl extends CustomerServiceImpl {
@Override
public Customer registerCustomer(Customer customer, String password, String passwordConfirm) {
ShaPasswordEncoder encoder = CommonUtils.getCustomerPasswordEncoder();
password = encoder.encodePassword(password, customer.getUsername());
passwordConfirm = encoder.encodePassword(passwordConfirm, customer.getUsername());
return super.registerCustomer(customer, password, passwordConfirm);
}
}
In applicationContext.xml, register this new class with the original bean id [which belonged to CustomerServiceImpl]
Code: Select all
<bean id="blCustomerService" class="<path_to_above_class>.MDCustomerServiceImpl" />
4. RegisterController CUSTOMIZATION: This is required because of a tricky bug in broadleaf 4.0
The bug is, when a user registers, the password is set as the hashed one. [WHICH IS OK]
NEXT, the login service is called. During the login, the password is picked up from the database [which is already hashed].
As a part of authentication, this is hashed again and there is a password mismatch.
Ideally, under normal scenario, during login the user will enter a plain password and will be hashed once to validate. In this case, the hashed password is picked from database and is re-hashed, which fails the authentication.
I copied the code from BroadleafRegisterController's processRegister method into RegisterController's processRegister method.
a. At the first line of processRegister method add the below code
Code: Select all
String unencodedPassword = registerCustomerForm.getPassword(); //store non-hashed password in a variable
b. Replace the following code
Code: Select all
loginService.loginCustomer(registerCustomerForm.getCustomer());
BY
Code: Select all
//#### Store hashed password in a variable
String codedPassword = registerCustomerForm.getCustomer().getPassword();
//#### SET UNENCODED PASSWORD AS PLAIN ONE FROM ABOVE
newCustomer.setUnencodedPassword(unencodedPassword);
//#### REPLACE WITH "newCustomer" for loginService.
loginService.loginCustomer(newCustomer);
//AFTER LOGIN RE-SET THE HASHED PASSWORD - Because loginService resets it to unencodedPassword.
newCustomer.setUnencodedPassword(codedPassword);
newCustomer.setPassword(codedPassword);
customerService.saveCustomer(newCustomer);
Now, for all the operations, the password will be hashed.
NOTE: This is the process for hashing passwords by SHA-256 algorithm, which is salted by the user's email-id on user-facing website and NOT admin panel.
____________________________________________________________________________
HOW TO RESET PASSWORD IF FORGOTTEN: In case you lose your password and you need to quickly reset it from database, you can use the following process to do so.
1. Choose a password in clear text: For example, you want to set your password as Magic2015
2. Next, choose a salt: It can be a user-property or a simple text value. For e.g. we want to use the salt as abc@def.com
3. Now, place your string in the format: <password_in_clear_text>{<your_salt>}. In our case, the string will be Magic2015{abc@def.com}
4. Use any online utility to hash this string using SHA-256 algorithm. The hashed string of above string will be 9f9c9b9682bd06a01cc47fed8929d70a4751bd011ccb36df82ecac68b65bd4a3
5. Insert this into database.
Code: Select all
insert into BLC_CUSTOMER
set password = '9f9c9b9682bd06a01cc47fed8929d70a4751bd011ccb36df82ecac68b65bd4a3'
where email = 'abc@def.com';
6. Now, you can login using credentials abc@def.com as username and Magic2015 as password