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>