Spring Boot Security with MySQL

Home / Spring Boot Security with MySQL

As we know security is the first concern when we working on any web application. Spring Boot also provide this feature to prevent our user from unauthorised access. Some time we also want Role wise authentication in our application means we want a user to access API only if he will authorised to use that. In this post we will see how we can manage a complete role wise authentication using MySQL in Spring boot, So to do that we have to follow below steps:

  1. Add required dependency in your pom.xml file.
  2. Add MySQL configuration in your application.properties file
  3. create user and authorities table in your MySQL.
  4. Enable security in your main class using @EnableWebSecurity annotaion.
  5. Create a Model class User.
  6. Create a configuration class that extends WebSecurityConfigurerAdapter and specify security config in this class.
  7. Create a DAO Class that Perform MySQL activity.
  8. Create a class that implements UserDetailsService and perform Spring Boot Security validation.
  9. Create Controller class for Rest Endpoint .

Follow above steps:

1- Add below dependency in your pom.xml file.

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-devtools</artifactId>
 <scope>runtime</scope>
 <optional>true</optional>
</dependency>

<dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
 <scope>runtime</scope>
</dependency>

2- Add MySQL configuration in your application.properties file.

spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/springBootSecurityMySQL
spring.datasource.username=vasu
spring.datasource.password=12345@
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

3- Create user and authorities table in MySQL.

CREATE TABLE IF NOT EXISTS `user` (
  `username` varchar(50) NOT NULL,
  `password` varchar(150) NOT NULL,
  `enabled` tinyint(1) NOT NULL,
  PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;



CREATE TABLE IF NOT EXISTS `authorities` (
  `username` varchar(50) NOT NULL,
  `authority` varchar(50) NOT NULL,
  UNIQUE KEY `ix_auth_username` (`username`,`authority`),
  CONSTRAINT `fk_authorities_users` FOREIGN KEY (`username`) REFERENCES `user` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

4- Enable security in your main class using @EnableWebSecurity annotaion.

package com.javadream;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

@EnableWebSecurity
@SpringBootApplication
public class SpringBootSecurityMySqlApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootSecurityMySqlApplication.class, args);
	}

}

5- Create a Model class User.

package com.javadream.model;

public class User {

	private String userName;
	private String password;
	private String role;

	public User() {
		super();
	}

	public User(String userName, String password, String role) {
		super();
		this.userName = userName;
		this.password = password;
		this.role = role;
	}

	@Override
	public String toString() {
		return "User [userName=" + userName + ", password=" + password + ", role=" + role + "]";
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getRole() {
		return role;
	}

	public void setRole(String role) {
		this.role = role;
	}

}

6- Create a configuration class that extends WebSecurityConfigurerAdapter

package com.javadream.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import com.javadream.service.UserAuthenticationService;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);

	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	@Autowired
	UserAuthenticationService authenticationService;

	@Autowired
	private PasswordEncoder encoder;

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {

		auth.userDetailsService(authenticationService).passwordEncoder(encoder);
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
		.antMatchers("/signUp/**").permitAll()
		.antMatchers("/userRoleAPIs/**")
		.hasAnyRole("USER")
		.antMatchers("/adminRoleAPIs/**")
		.hasAnyRole("ADMIN")
		.antMatchers("/anyRoleAPIs/**")
		.hasAnyRole("USER","ADMIN")
		.and().csrf().disable().formLogin();
	}

}

In above class we override configure method of WebSecurityConfigurerAdapter class. Inside this function we define the APIs end point and the role that can access that APIs. We do not define any type of security on SignUp endPoint because anybody can do Sign Up and we don’t required any authentication for Sing Up process.

7- Create a DAO Class that Perform MySQL activity .

package com.javadream.dao;

import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Repository;
import org.springframework.security.authentication.*;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import com.javadream.model.User;

@Repository
public class UserDao {
	private static final Logger logger = LoggerFactory.getLogger(UserDao.class);

	@Autowired
	private JdbcTemplate template;
	
	@Autowired
	private PasswordEncoder encoder;

	public User getUserInfo(String userName) {
		try {
			String sql = "SELECT u.username name, u.password pass, a.authority role FROM "
					+ "user u INNER JOIN authorities a on u.username=a.username WHERE "
					+ "u.enabled =1 and u.username = ?";
			List<Map<String, Object>> userResponse = template.queryForList(sql, new Object[] { userName });
			logger.info("vvv::  userResponse= " + userResponse);
			
			if (userResponse != null) {
				User user = new User(userResponse.get(0).get("name").toString(),
						userResponse.get(0).get("pass").toString(),
						userResponse.get(0).get("role").toString());
				
				logger.info("vvv:: user= " + user);
				return user;
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;

	}
	
	
	public int insertUser(User user) {
		try {
			String userName = user.getUserName();
			String pass = user.getPassword();
			String role = user.getRole();
			logger.info("vvv::  userName= "+userName + " ,pass= "+pass + ", role= "+role);
			int updateStatus = template.update("insert into user(username,password,enabled) values(?,?,?)", new Object[] {userName,encoder.encode(pass),"1"});
			logger.info("vvv::  updateStatus= "+updateStatus);
			if(updateStatus == 1) {
				int insertIntoAuthTable = template.update("insert into authorities(username,authority) values(?,?)", new Object[] {userName,role});
                logger.info("vvv::  insertIntoAuthTable = "+insertIntoAuthTable);
                if(insertIntoAuthTable == 1) {
                	return 1;
                }
			}
			return 0;
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}

}

In above class we define a bean PasswordEncoder , because we don’t want to save our user password in Plain Text in MySQL table so we use PasswordEncoder to encrypt our password before save it to MySQL.

8- Create a class that implements UserDetailsService and perform Spring Boot Security validation.

package com.javadream.service;

import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import com.javadream.dao.UserDao;
import com.javadream.model.User;

@Service
public class UserAuthenticationService implements UserDetailsService {

	private static final Logger logger = LoggerFactory.getLogger(UserAuthenticationService.class);

	@Autowired
	private UserDao dao;

	@Autowired
	private BCryptPasswordEncoder encoder;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		try {
			logger.info("vvv::  calling loadUserByUsername for username= " + username);
			User userInfo = dao.getUserInfo(username);
			GrantedAuthority authority = new SimpleGrantedAuthority(userInfo.getRole());
			UserDetails userDetails = (UserDetails) new org.springframework.security.core.userdetails.User(
					userInfo.getUserName(), userInfo.getPassword(), Arrays.asList(authority));

			return userDetails;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		
	}

}

9- Create Controller Class for Rest EndPoint.

Here we define three controller to demonstrate how APIs will be access with valid role.

First We create a SignUp controller to Sign Up. APIs for Sign up is open to all any body can access that.

package com.javadream.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.javadream.dao.UserDao;
import com.javadream.model.User;

@RestController
@RequestMapping("signUp")
public class InsertController {
	private static final Logger logger = LoggerFactory.getLogger(InsertController.class);
	private static final String GLOBAL_ERROR = "Some technical Error try again!";

	@Autowired
	private UserDao dao;
	
	
	@PostMapping("/insert")
	public String insert(@RequestBody User user) {
		try {
			logger.info("vvv::  user= "+user);
			if(user == null) {
				return GLOBAL_ERROR;
			}
			int insertUser = dao.insertUser(user);
			return "Insert Status= "+insertUser;
		} catch (Exception e) {
			e.printStackTrace();
			return "Some Technical Error Try Again";
		}
		
	}
}

Second We create a UserController. APIs for this controller is accessed by those who have USER role.

package com.javadream.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.javadream.dao.UserDao;
import com.javadream.model.User;

@RestController
@RequestMapping("userRoleAPIs")
public class UserController {

	private static final Logger logger = LoggerFactory.getLogger(UserController.class);

	
	@GetMapping("/testAPI")
	public String demo() {
		return "Nice Try! You have learned Spring Boot Security With MySql";
	}
}

Third we create a AdminController. APIs for this controller is accessed by those who have ADMIN role.

package com.javadream.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/adminRoleAPIs")
public class AdminController {

	@GetMapping("/testAPI")
	public String demo() {
		return "Nice Try! You have learned Spring Boot Security With MySql";
	}
}

Fourth we create a AnyRoleController. APIs for this controller is accessed by those who any role ADMIN role or USER role.

package com.javadream.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/anyRoleAPIs")
public class AnyRoleController {

	
	@GetMapping("/testAPI")
	public String demo() {
		return "Nice Try! You have learned Spring Boot Security With MySql";
	}
}

Now Run this application and use below steps to test your application functionality.

  1. Make a Post Request to Sign Up URL using postman .
  2. Define role parameter accordingly for Admin role use parameter ROLE_ADMIN and for user role use parameter ROLE_USER
  3. Now try to access any API from any controller ( UserController, AdminController, AnyRoleController).
  4. when you hit API a default SignUp Page will open now enter your credentials if your user has valid credential and valid role then you will get the response otherwise 403 error will display.

Complete Code On GitHub: SpringBoot Role Wise authentication with MySQL

Help Others, Please Share

About Author

Leave a Reply

Your email address will not be published. Required fields are marked *

x