1
1
.
.
2
2
.
.
5
5
D
D
B
B
I
I
n
n
f
f
o
o
[
[
G
G
]
]
This tutorial shows how to define/store Users in DB.
Users will be stored in the ACCOUNT Table for which we will create Account Entity.
Account Entity will have: Username, Password and Role.
We are using Account Entity to avoid confusion with built-in User Class .
Application Schema [Results]
DB Schema
Account (Loaded Data)
ID
USERNAME
PASSWORD
ROLE
1
john
johnpassword
ROLE_ADMIN
Spring Boot Starters
GROUP
DEPENDENCY
DESCRIPTION
Web
Spring Web
Enables @Controller, @RequestMapping and Tomcat Server
Security
Spring Security
Enables Spring Security
SQL
Spring Data JPA
Enables @Entity and @Id
SQL
H2 Database
Enables in-memory H2 Database
ACCOUNT
MyController
http://localhost:8080/Hello
Tomcat
hello()
Browser
Account
AccountRepository
SecurityConfig
AccountLoader
MyUserDetailsSer...
PasswordEncoder
Return User Object
M
M
y
y
U
U
s
s
e
e
r
r
D
D
e
e
t
t
a
a
i
i
l
l
s
s
S
S
e
e
r
r
v
v
i
i
c
c
e
e
.
.
j
j
a
a
v
v
a
a
If you
● declare Class that implements UserDetailsService (it is called Service because it SERVES UserDetails Object)
● and @Override its Method loadUserByUsername(String username)
● then this Method will be called when User enters Username and Password through Login Form
Method
● should return Object of Class that Implements UserDetails Interface
● which should contain: Username, Password, Authorities
● which in our case will be built-in User Class
Method Body is completely customizable
● so we take Account from DB based on username Input Parameter
● and from that Account we create and return User Object
This User Object
● will contain Password from DB
● which Spring will compare with the Password from Login Form
● and if they match Spring will Authenticate User by setting User Object Property enabled = true
MyUserDetailsService.java (Returns User Object with: Username, Password, Authorities)
@Service
public class MyUserDetailsService implements UserDetailsSer vice {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// Use DB Account to return User Object that contains: Username, Password, Authorities
}
}
S
S
e
e
c
c
u
u
r
r
i
i
t
t
y
y
C
C
o
o
n
n
f
f
i
i
g
g
.
.
j
j
a
a
v
v
a
a
SecurityConfig.java is needed only to specify PasswordEncoder.
If you don't need access to H@ Console then configure() is not necessary.
SecurityConfig.java
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
P
P
r
r
o
o
c
c
e
e
d
d
u
u
r
r
e
e
Create Project: springboot_dbauthentication (add Spring Boot Starters from the table)
Edit File: application.properties (specify H2 DB name, enable H2 Web Console)
Create Package: config (inside main package)
– Create Class: AccountLoader.java (inside package config)
– Create Class: SecurityConfig.java (inside package config)
Create Package: entities (inside main package)
– Create Class: Account.java (inside package entities)
Create Package: repositories (inside main package)
– Create Interface: AccountRepository.java (inside package repositories)
Create Package: services (inside main package)
– Create Class: MyUserDetailsService.java (inside package services)
Create Package: controllers (inside main package)
– Create Class: MyController.java (inside package controllers)
application.properties
# H2 CONSOLE
spring.h2.console.enabled = true
spring.datasource.url = jdbc:h2:mem:testdb
Account.java
package com.ivoronline.springboot_dbauthentication.entities;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer id;
public String username;
public String password;
public String role;
}
AccountRepository.java
package com.ivoronline.springboot_dbauthentication.repositories;
import com.ivoronline.springboot_dbauthentication.entities.Account;
import org.springframework.data.jpa.repository.JpaRepository;
public interface AccountRepository extends JpaRepository<Account, Integer> {
Account findByUsername(String Username);
}
AccountLoader.java
package com.ivoronline.springboot_dbauthentication.config;
import com.ivoronline.springboot_dbauthentication.entities.Account;
import com.ivoronline.springboot_dbauthentication.repositories.AccountRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Component
public class AccountLoader implements CommandLineRunner {
@Autowired private AccountRepository accountRepository;
@Override
@Transactional
public void run(String... args) throws Exception {
//CREATE ACCOUNT
Account account = new Account();
account.username = "john";
account.password = "johnpassword";
account.role = "ROLE_ADMIN";
//STORE ACCOUNT INTO DB
accountRepository.save(account);
}
}
MyUserDetailsService.java
package com.ivoronline.springboot_dbauthentication.services;
import com.ivoronline.springboot_dbauthentication.entities.Account;
import com.ivoronline.springboot_dbauthentication.repositories.AccountRepository;
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.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
AccountRepository accountRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//GET ACCOUNT FROM DB
Account account = accountRepository.findByUsername(username);
String password = account.password;
//CREATE AUTHORITIES
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(account.role));
//CREATE USER
User user = new User(username, password, true, true, true, true, authorities);
//RETURN USER
return user;
}
}
SecurityConfig.java
package com.ivoronline.springboot_dbauthentication.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//ALLOW ACCES TO H2 CONSOLE
httpSecurity.authorizeRequests(authorize -> { authorize.antMatchers("/h2-console/**").permitAll(); });
httpSecurity.headers().frameOptions().sameOrigin();
httpSecurity.csrf().disable();
//RESTRIT ACCESS TO EVERYTHING ELSE (BEHIND LOGIN FORM)
httpSecurity
.authorizeRequests().anyRequest().authenticated()
.and().formLogin()
.and().httpBasic();
}
}
MyController.java
package com.ivoronline.springboot_dbauthentication.controllers;
import com.ivoronline.springboot_dbauthentication.repositories.AccountRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MyController {
@Autowired
AccountRepository accountRepository;
@ResponseBody
@RequestMapping("/Hello")
public String hello() {
return "Hello from Controller";
}
}
R
R
e
e
s
s
u
u
l
l
t
t
s
s
http://localhost:8080/Hello
You get redirected to http://localhost:8080/login
– Username: john
– Password: johnpassword
– Sign in
You get redirected back to http://localhost:8080/Hello
http://localhost:8080/login - john - johnpassword (JSESSIONID Cookie is stored in the Browser)
Redirects to http://localhost:8080/Hello
http://localhost:8080/h2-console/login.do?jsessionid=cc7af091db475e2494725679cb7da334
Application Structure
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>