Spring Security JDBC Authentication with Password Encryption




I published a basic level tutorial on how to implement JDBC Authentication and Authorization using Spring Security last week. There are few best practices to be followed while implementing security. One such important thing to do is Password Encryption and I am going to cover all this in this article.

I updated the project I implemented for the previous tutorial to cover the following best practices,

1. Edited mysql queries to use userid as foreign key instead of username. This will help in case if the username needs to be changed in future.

2. Replaced passwords in database that are stored as plain text with encrypted passwords. This is very very important. If the database ever gets hacked, all the plain text passwords will be exposed and that would be a great disaster. So, passwords must be encrypted with a good hashing algorithm which will be very hard for any hacker to crack. Spring Security supports one of the best password hashing algorithm which is bcrypt. I found an interesting article about using bcrypt here, you can read it if you want to have a quick look at what this is.

3. Used Spring Security's default BCryptPassword Encoder to handle bcrypt encoded passwords.

4. Separated database, authentication and authorization related configuration from mvc configuration.

Let me now go step by step and explain the changes to be made.

1. First download the existing project from here.

2. Execute below mysql queries,

DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS user_roles;
CREATE  TABLE users (
  userid VARCHAR(5) NOT NULL,
  username VARCHAR(45) NOT NULL ,
  password VARCHAR(60) NOT NULL ,
  enabled TINYINT NOT NULL DEFAULT 1 ,
  PRIMARY KEY (userid));
  
CREATE TABLE user_roles (
  user_role_id int(11) NOT NULL AUTO_INCREMENT,
  userid varchar(5) NOT NULL,
  role varchar(45) NOT NULL,
  PRIMARY KEY (user_role_id),
  UNIQUE KEY uni_username_role (role,userid),
  KEY fk_username_idx (userid),
  CONSTRAINT fk_username FOREIGN KEY (userid) REFERENCES users (userid));

INSERT INTO users(userid,username,password,enabled)
VALUES ('001','priya','$2a$04$CO93CT2ObgMiSnMAWwoBkeFObJlMYi/wzzOnPlsTP44r7qVq0Jln2', true);
INSERT INTO users(userid,username,password,enabled)
VALUES ('002','naveen','$2a$04$j3JpPUp6CTAe.kMWmdRNC.Wie58xDNPfcYz0DBJxWkucJ6ekJuiJm', true);

INSERT INTO user_roles (userid, role)
VALUES ('002', 'ROLE_USER');
INSERT INTO user_roles (userid, role)
VALUES ('001', 'ROLE_ADMIN');
INSERT INTO user_roles (userid, role)
VALUES ('001', 'ROLE_USER');

Note that I have converted plain text passwords to encrypted passwords. I used this online bcrypt calculator for converting the passwords to bcrypt encoded hash values. You can do the same or use this small utility method to find out,

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class EncryptPassword{

 public static void main(String args[]) throws Exception {
  String cryptedPassword = new BCryptPasswordEncoder().encode("password");
  System.out.println(cryptedPassword);
 }
}

3. Add a new class in hello package to have all authentication related configuration to have a better clarity,

AuthenticationProvider.java


package hello;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;

@Configuration
public class AuthenticationProviderConfig {
 @Bean(name = "dataSource")
 public DriverManagerDataSource dataSource() {
     DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
     driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
     driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/userbase");
     driverManagerDataSource.setUsername("root");
     driverManagerDataSource.setPassword("root");
     return driverManagerDataSource;
 }
    
    @Bean(name="userDetailsService")
    public UserDetailsService userDetailsService(){
     JdbcDaoImpl jdbcImpl = new JdbcDaoImpl();
     jdbcImpl.setDataSource(dataSource());
     jdbcImpl.setUsersByUsernameQuery("select username,password, enabled from users where username=?");
     jdbcImpl.setAuthoritiesByUsernameQuery("select b.username, a.role from user_roles a, users b where b.username=? and a.userid=b.userid");
     return jdbcImpl;
    }
}


4. Remove datasource configuration from MvcConfig.java. MvcConfig must now look clean with only mvc related configuration like this,

MvcConfig.java

package hello;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter{

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/home").setViewName("home");
        registry.addViewController("/").setViewName("home");
        registry.addViewController("/hello").setViewName("hello");
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/403").setViewName("403");
    }    
    
    @Bean
 public InternalResourceViewResolver viewResolver() {
  InternalResourceViewResolver resolver = new InternalResourceViewResolver();
  resolver.setPrefix("/WEB-INF/jsp/");
  resolver.setSuffix(".jsp");
  return resolver;
 }    
}

5. Now add password encoder to security configuration class.

WebSecurityConfig.java


package hello;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

 @Autowired 
 UserDetailsService userDetailsService;
 
 @Autowired
 public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {    
  auth.userDetailsService(userDetailsService).passwordEncoder(passwordencoder());;
  
 } 
 
 @Override
 protected void configure(HttpSecurity http) throws Exception {

   http.authorizeRequests()
  .antMatchers("/hello").access("hasRole('ROLE_ADMIN')")  
  .anyRequest().permitAll()
  .and()
    .formLogin().loginPage("/login")
    .usernameParameter("username").passwordParameter("password")
  .and()
    .logout().logoutSuccessUrl("/login?logout") 
   .and()
   .exceptionHandling().accessDeniedPage("/403")
  .and()
    .csrf();
 }
 
 @Bean(name="passwordEncoder")
    public PasswordEncoder passwordencoder(){
     return new BCryptPasswordEncoder();
    }
}

Its very simple. Just create an object of Spring Security's default BCryptPasswordEncoder, set it to the AuthenticationManagerBuilder and we are done!

The user will type plain text for password on the website, spring security validates the bcrypt encoded version of the password entered by the user.

You can download this updated project from the link below.

DOWNLOAD

How it works




Keep yourself subscribed for getting programmingfree articles delivered directly to your inbox once in a month. Thanks for reading!

Subscribe to GET LATEST ARTICLES!


Related

Trending 7496506812751949135

Post a Comment

emo-but-icon

SUBSCRIBE


item