Créer une application de connexion avec Spring Boot, Spring Security, Spring JDBC
1. Objectif de l'exemple
Ce document est basé sur :
Spring Boot 2.x
Spring Security
Spring JDBC
Thymeleaf
Database: MySQL, SQL Server, Oracle, Postgres
Dans cet article, je vais vous donner des instruction de la création une application de connexion avec Spring Boot + Spring Security + JDBC + Thymeleaf et expliquer le principe de fonctionnement de Spring Security.
Les utilisateurs doivent se connecter, ils peuvent voir les pages protégées:
Les utilisateurs qui se sont connectés au système sont autorisés à afficher seulement les pages dans leur portée et leur rôle. S'ils accèdent aux pages protégées situées au-delà de leur rôle, ils seront refusés.
2. Préparer la base de données
MySQL
-- Create table
create table APP_USER
(
USER_ID BIGINT not null,
USER_NAME VARCHAR(36) not null,
ENCRYTED_PASSWORD VARCHAR(128) not null,
ENABLED BIT not null
) ;
--
alter table APP_USER
add constraint APP_USER_PK primary key (USER_ID);
alter table APP_USER
add constraint APP_USER_UK unique (USER_NAME);
-- Create table
create table APP_ROLE
(
ROLE_ID BIGINT not null,
ROLE_NAME VARCHAR(30) not null
) ;
--
alter table APP_ROLE
add constraint APP_ROLE_PK primary key (ROLE_ID);
alter table APP_ROLE
add constraint APP_ROLE_UK unique (ROLE_NAME);
-- Create table
create table USER_ROLE
(
ID BIGINT not null,
USER_ID BIGINT not null,
ROLE_ID BIGINT not null
);
--
alter table USER_ROLE
add constraint USER_ROLE_PK primary key (ID);
alter table USER_ROLE
add constraint USER_ROLE_UK unique (USER_ID, ROLE_ID);
alter table USER_ROLE
add constraint USER_ROLE_FK1 foreign key (USER_ID)
references APP_USER (USER_ID);
alter table USER_ROLE
add constraint USER_ROLE_FK2 foreign key (ROLE_ID)
references APP_ROLE (ROLE_ID);
-- Used by Spring Remember Me API.
CREATE TABLE Persistent_Logins (
username varchar(64) not null,
series varchar(64) not null,
token varchar(64) not null,
last_used timestamp not null,
PRIMARY KEY (series)
);
--------------------------------------
insert into App_User (USER_ID, USER_NAME, ENCRYTED_PASSWORD, ENABLED)
values (2, 'dbuser1', '$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 1);
insert into App_User (USER_ID, USER_NAME, ENCRYTED_PASSWORD, ENABLED)
values (1, 'dbadmin1', '$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 1);
---
insert into app_role (ROLE_ID, ROLE_NAME)
values (1, 'ROLE_ADMIN');
insert into app_role (ROLE_ID, ROLE_NAME)
values (2, 'ROLE_USER');
---
insert into user_role (ID, USER_ID, ROLE_ID)
values (1, 1, 1);
insert into user_role (ID, USER_ID, ROLE_ID)
values (2, 1, 2);
insert into user_role (ID, USER_ID, ROLE_ID)
values (3, 2, 2);
---
SQL Server
-- Create table
create table APP_USER
(
USER_ID BIGINT not null,
USER_NAME VARCHAR(36) not null,
ENCRYTED_PASSWORD VARCHAR(128) not null,
ENABLED BIT not null
) ;
--
alter table APP_USER
add constraint APP_USER_PK primary key (USER_ID);
alter table APP_USER
add constraint APP_USER_UK unique (USER_NAME);
-- Create table
create table APP_ROLE
(
ROLE_ID BIGINT not null,
ROLE_NAME VARCHAR(30) not null
) ;
--
alter table APP_ROLE
add constraint APP_ROLE_PK primary key (ROLE_ID);
alter table APP_ROLE
add constraint APP_ROLE_UK unique (ROLE_NAME);
-- Create table
create table USER_ROLE
(
ID BIGINT not null,
USER_ID BIGINT not null,
ROLE_ID BIGINT not null
);
--
alter table USER_ROLE
add constraint USER_ROLE_PK primary key (ID);
alter table USER_ROLE
add constraint USER_ROLE_UK unique (USER_ID, ROLE_ID);
alter table USER_ROLE
add constraint USER_ROLE_FK1 foreign key (USER_ID)
references APP_USER (USER_ID);
alter table USER_ROLE
add constraint USER_ROLE_FK2 foreign key (ROLE_ID)
references APP_ROLE (ROLE_ID);
-- Used by Spring Remember Me API.
CREATE TABLE Persistent_Logins (
username varchar(64) not null,
series varchar(64) not null,
token varchar(64) not null,
last_used Datetime not null,
PRIMARY KEY (series)
);
--------------------------------------
insert into App_User (USER_ID, USER_NAME, ENCRYTED_PASSWORD, ENABLED)
values (2, 'dbuser1', '$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 1);
insert into App_User (USER_ID, USER_NAME, ENCRYTED_PASSWORD, ENABLED)
values (1, 'dbadmin1', '$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 1);
---
insert into app_role (ROLE_ID, ROLE_NAME)
values (1, 'ROLE_ADMIN');
insert into app_role (ROLE_ID, ROLE_NAME)
values (2, 'ROLE_USER');
---
insert into user_role (ID, USER_ID, ROLE_ID)
values (1, 1, 1);
insert into user_role (ID, USER_ID, ROLE_ID)
values (2, 1, 2);
insert into user_role (ID, USER_ID, ROLE_ID)
values (3, 2, 2);
ORACLE
-- Create table
create table APP_USER
(
USER_ID NUMBER(19) not null,
USER_NAME VARCHAR2(36) not null,
ENCRYTED_PASSWORD VARCHAR2(128) not null,
ENABLED NUMBER(1) not null
) ;
--
alter table APP_USER
add constraint APP_USER_PK primary key (USER_ID);
alter table APP_USER
add constraint APP_USER_UK unique (USER_NAME);
-- Create table
create table APP_ROLE
(
ROLE_ID NUMBER(19) not null,
ROLE_NAME VARCHAR2(30) not null
) ;
--
alter table APP_ROLE
add constraint APP_ROLE_PK primary key (ROLE_ID);
alter table APP_ROLE
add constraint APP_ROLE_UK unique (ROLE_NAME);
-- Create table
create table USER_ROLE
(
ID NUMBER(19) not null,
USER_ID NUMBER(19) not null,
ROLE_ID NUMBER(19) not null
);
--
alter table USER_ROLE
add constraint USER_ROLE_PK primary key (ID);
alter table USER_ROLE
add constraint USER_ROLE_UK unique (USER_ID, ROLE_ID);
alter table USER_ROLE
add constraint USER_ROLE_FK1 foreign key (USER_ID)
references APP_USER (USER_ID);
alter table USER_ROLE
add constraint USER_ROLE_FK2 foreign key (ROLE_ID)
references APP_ROLE (ROLE_ID);
-- Used by Spring Remember Me API.
CREATE TABLE Persistent_Logins (
username varchar2(64) not null,
series varchar2(64) not null,
token varchar2(64) not null,
last_used Date not null,
PRIMARY KEY (series)
);
--------------------------------------
insert into App_User (USER_ID, USER_NAME, ENCRYTED_PASSWORD, ENABLED)
values (2, 'dbuser1', '$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 1);
insert into App_User (USER_ID, USER_NAME, ENCRYTED_PASSWORD, ENABLED)
values (1, 'dbadmin1', '$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 1);
---
insert into app_role (ROLE_ID, ROLE_NAME)
values (1, 'ROLE_ADMIN');
insert into app_role (ROLE_ID, ROLE_NAME)
values (2, 'ROLE_USER');
---
insert into user_role (ID, USER_ID, ROLE_ID)
values (1, 1, 1);
insert into user_role (ID, USER_ID, ROLE_ID)
values (2, 1, 2);
insert into user_role (ID, USER_ID, ROLE_ID)
values (3, 2, 2);
---
Commit;
Postgres
-- Create table
create table APP_USER
(
USER_ID BIGINT not null,
USER_NAME VARCHAR(36) not null,
ENCRYTED_PASSWORD VARCHAR(128) not null,
ENABLED Int not null
) ;
--
alter table APP_USER
add constraint APP_USER_PK primary key (USER_ID);
alter table APP_USER
add constraint APP_USER_UK unique (USER_NAME);
-- Create table
create table APP_ROLE
(
ROLE_ID BIGINT not null,
ROLE_NAME VARCHAR(30) not null
) ;
--
alter table APP_ROLE
add constraint APP_ROLE_PK primary key (ROLE_ID);
alter table APP_ROLE
add constraint APP_ROLE_UK unique (ROLE_NAME);
-- Create table
create table USER_ROLE
(
ID BIGINT not null,
USER_ID BIGINT not null,
ROLE_ID BIGINT not null
);
--
alter table USER_ROLE
add constraint USER_ROLE_PK primary key (ID);
alter table USER_ROLE
add constraint USER_ROLE_UK unique (USER_ID, ROLE_ID);
alter table USER_ROLE
add constraint USER_ROLE_FK1 foreign key (USER_ID)
references APP_USER (USER_ID);
alter table USER_ROLE
add constraint USER_ROLE_FK2 foreign key (ROLE_ID)
references APP_ROLE (ROLE_ID);
-- Used by Spring Remember Me API.
CREATE TABLE Persistent_Logins (
username varchar(64) not null,
series varchar(64) not null,
token varchar(64) not null,
last_used timestamp not null,
PRIMARY KEY (series)
);
--------------------------------------
insert into App_User (USER_ID, USER_NAME, ENCRYTED_PASSWORD, ENABLED)
values (2, 'dbuser1', '$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 1);
insert into App_User (USER_ID, USER_NAME, ENCRYTED_PASSWORD, ENABLED)
values (1, 'dbadmin1', '$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 1);
---
insert into app_role (ROLE_ID, ROLE_NAME)
values (1, 'ROLE_ADMIN');
insert into app_role (ROLE_ID, ROLE_NAME)
values (2, 'ROLE_USER');
---
insert into user_role (ID, USER_ID, ROLE_ID)
values (1, 1, 1);
insert into user_role (ID, USER_ID, ROLE_ID)
values (2, 1, 2);
insert into user_role (ID, USER_ID, ROLE_ID)
values (3, 2, 2);
---
Commit;
3. Créer le projet Spring Boot
Sur Eclipse, créez un projet Spring Boot.
Saisissez :
- Name: SbSecurityJdbcThymeleaf
- Group: org.o7planning
- Artifact: SbSecurityJdbcThymeleaf
- Description: Spring Boot + JDBC + Security + Thymeleaf
- Package: org.o7planning.sbsecurity
Dans l'étape suivante, vous devez sélectionner les bibliothèques et les technologies utilisées (Dans cette publication, nous nous connecterons à la base de données Oracle, MySQL, SQL Server ou Postgres).
Database Libraries:
- MySQL
- PostgresSQL
- SQL Server
- Web
- Thymeleaf
- Security
OK le projet a été créé.
SbSecurityJdbcThymeleafApplication.java
package org.o7planning.sbsecurity;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SbSecurityJdbcThymeleafApplication {
public static void main(String[] args) {
SpringApplication.run(SbSecurityJdbcThymeleafApplication.class, args);
}
}
4. Configurer le fichier pom.xml
Si vous utilisez la base de données Oracle, vous devez déclarer une bibliothèque nécessaire de Oracle dans le fichier pom.xml:
** Oracle **
<dependencies>
.....
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
.....
</dependencies>
<repositories>
....
<!-- Repository for ORACLE JDBC Driver -->
<repository>
<id>codelds</id>
<url>https://code.lds.org/nexus/content/groups/main-repo</url>
</repository>
.....
</repositories>
Si vous vous connectez à la base de données SQL Service, vous pouvez utiliser une des deux bibliothèques JTDS ou Mssql-Jdbc:
** SQL Server **
<dependencies>
.....
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.sourceforge.jtds</groupId>
<artifactId>jtds</artifactId>
<scope>runtime</scope>
</dependency>
.....
</dependencies>
Le contenu complet du fichier pom.xml:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.o7planning</groupId>
<artifactId>SbSecurityJdbcThymeleaf</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SbSecurityJdbcThymeleaf</name>
<description>Spring Boot + JDBC + Security + Thymeleaf</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</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-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.sourceforge.jtds</groupId>
<artifactId>jtds</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<!-- Repository for ORACLE JDBC Driver -->
<repository>
<id>codelds</id>
<url>https://code.lds.org/nexus/content/groups/main-repo</url>
</repository>
</repositories>
</project>
5. Configurer la source de données
Pourque Spring puisse se connecter à la base de données, vous devez configurer les paramètres nécessaires dans le fichier application.properties.
application.properties (ORACLE)
# Database Connection
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:db12c
spring.datasource.username=Test
spring.datasource.password=test001
# Other properties ..
application.properties (SQL Server + JTDS driver)
# Database Connection
spring.datasource.driver-class-name=net.sourceforge.jtds.jdbc.Driver
spring.datasource.url=jdbc:jtds:sqlserver://localhost:1433/simplehr;instance=SQLEXPRESS
spring.datasource.username=sa
spring.datasource.password=12345
# Other properties ..
application.properties (SQL Server + Mssql-jdbc Driver)
# Database Connection
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.url=jdbc:sqlserver://tran-vmware-pc\\SQLEXPRESS:1433;databaseName=Test
spring.datasource.username=sa
spring.datasource.password=12345
# Other properties ..
application.properties (MySQL)
# Database Connection
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase
spring.datasource.username=root
spring.datasource.password=12345
# Other properties ..
application.properties (Postgres)
# Database Connection
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/mydatabase
spring.datasource.username=postgres
spring.datasource.password=12345
# Other properties ..
6. Sécurité de configuration
Cette application a certaines fonctions (pages), auxquelles :
- /userInfo
Voici est une page d'affichage de l'information utilisateur. Cette page requiert l'utilisateur de se connecter et de jouer le rôle tel que ROLE_ADMIN ou ROLE_USER.
- /admin
C'est une page d'administrateur. Elle exige des utilisateur de se connecter et seulement les personnes qui jouent le rôle ROLE_ADMIN peuvent y accéder.
- /. /welcome, /login, /logout, /403
Les autres pages de l'application ne demandent pas de utilisateurs à se connecter.
La classe WebSecurityConfig sert à configurer la sécurité de l'application. Elle est annotée (annotate) par @Configuration. Cette annotation informe à Spring qu'elle est une classe de configuration et par conséquent, elle sera analysée par Spring dans le même temps où les applications exécutent.
WebSecurityConfig.java
package org.o7planning.sbsecurity.config;
import org.o7planning.sbsecurity.service.UserDetailsServiceImpl;
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;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsServiceImpl userDetailsService;
@Bean
public BCryptPasswordEncoder passwordEncoder() {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
return bCryptPasswordEncoder;
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// Setting Service to find User in the database.
// And Setting PassswordEncoder
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
// The pages does not require login
http.authorizeRequests().antMatchers("/", "/login", "/logout").permitAll();
// /userInfo page requires login as ROLE_USER or ROLE_ADMIN.
// If no login, it will redirect to /login page.
http.authorizeRequests().antMatchers("/userInfo").access("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')");
// For ADMIN only.
http.authorizeRequests().antMatchers("/admin").access("hasRole('ROLE_ADMIN')");
// When the user has logged in as XX.
// But access a page that requires role YY,
// AccessDeniedException will be thrown.
http.authorizeRequests().and().exceptionHandling().accessDeniedPage("/403");
// Config for Login Form
http.authorizeRequests().and().formLogin()//
// Submit URL of login page.
.loginProcessingUrl("/j_spring_security_check") // Submit URL
.loginPage("/login")//
.defaultSuccessUrl("/userAccountInfo")//
.failureUrl("/login?error=true")//
.usernameParameter("username")//
.passwordParameter("password")
// Config for Logout Page
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/logoutSuccessful");
}
}
7. Model, Mapper, DAO, WebUtils
La classe AppUser représente un enregistrement dans le tableau APP_USER de la base de données.
AppUser.java
package org.o7planning.sbsecurity.model;
public class AppUser {
private Long userId;
private String userName;
private String encrytedPassword;
public AppUser() {
}
public AppUser(Long userId, String userName, String encrytedPassword) {
this.userId = userId;
this.userName = userName;
this.encrytedPassword = encrytedPassword;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getEncrytedPassword() {
return encrytedPassword;
}
public void setEncrytedPassword(String encrytedPassword) {
this.encrytedPassword = encrytedPassword;
}
@Override
public String toString() {
return this.userName + "/" + this.encrytedPassword;
}
}
La classe AppUserMapper sert à conjoindre (mapping) les colonnes dans le tableau APP_USER avec les champs (field) dans la classe AppUser (Basé sur l'instruction de requête).
AppUserMapper.java
package org.o7planning.sbsecurity.mapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.o7planning.sbsecurity.model.AppUser;
import org.springframework.jdbc.core.RowMapper;
public class AppUserMapper implements RowMapper<AppUser> {
public static final String BASE_SQL //
= "Select u.User_Id, u.User_Name, u.Encryted_Password From App_User u ";
@Override
public AppUser mapRow(ResultSet rs, int rowNum) throws SQLException {
Long userId = rs.getLong("User_Id");
String userName = rs.getString("User_Name");
String encrytedPassword = rs.getString("Encryted_Password");
return new AppUser(userId, userName, encrytedPassword);
}
}
Des classes DAO (Data Access Object) sont celles utilisées pour accéder à une base de données, par exemple : Requêter (Query), Insérer (Insert), Mettre à jour (Update), Supprimer (Delete). Les classes DAO sont habituellement annotées par @Repository pour annoncer à Spring de les gérer comme des Spring BEAN.
La classe AppUserDAO sert à manipuler le tableau APP_USER. Elle a une méthode de recherche un utilisateur dans la base de données correspondant à un nom d'utilisateur.
AppUserDAO.java
package org.o7planning.sbsecurity.dao;
import javax.sql.DataSource;
import org.o7planning.sbsecurity.mapper.AppUserMapper;
import org.o7planning.sbsecurity.model.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
@Transactional
public class AppUserDAO extends JdbcDaoSupport {
@Autowired
public AppUserDAO(DataSource dataSource) {
this.setDataSource(dataSource);
}
public AppUser findUserAccount(String userName) {
// Select .. from App_User u Where u.User_Name = ?
String sql = AppUserMapper.BASE_SQL + " where u.User_Name = ? ";
Object[] params = new Object[] { userName };
AppUserMapper mapper = new AppUserMapper();
try {
AppUser userInfo = this.getJdbcTemplate().queryForObject(sql, params, mapper);
return userInfo;
} catch (EmptyResultDataAccessException e) {
return null;
}
}
}
AppRoleDAO.java
package org.o7planning.sbsecurity.dao;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
@Transactional
public class AppRoleDAO extends JdbcDaoSupport {
@Autowired
public AppRoleDAO(DataSource dataSource) {
this.setDataSource(dataSource);
}
public List<String> getRoleNames(Long userId) {
String sql = "Select r.Role_Name " //
+ " from User_Role ur, App_Role r " //
+ " where ur.Role_Id = r.Role_Id and ur.User_Id = ? ";
Object[] params = new Object[] { userId };
List<String> roles = this.getJdbcTemplate().queryForList(sql, params, String.class);
return roles;
}
}
-
WebUtils.java
package org.o7planning.sbsecurity.utils;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
public class WebUtils {
public static String toString(User user) {
StringBuilder sb = new StringBuilder();
sb.append("UserName:").append(user.getUsername());
Collection<GrantedAuthority> authorities = user.getAuthorities();
if (authorities != null && !authorities.isEmpty()) {
sb.append(" (");
boolean first = true;
for (GrantedAuthority a : authorities) {
if (first) {
sb.append(a.getAuthority());
first = false;
} else {
sb.append(", ").append(a.getAuthority());
}
}
sb.append(")");
}
return sb.toString();
}
}
EncrytedPasswordUtils.java
package org.o7planning.sbsecurity.utils;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class EncrytedPasswordUtils {
// Encryte Password with BCryptPasswordEncoder
public static String encrytePassword(String password) {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder.encode(password);
}
public static void main(String[] args) {
String password = "123";
String encrytedPassword = encrytePassword(password);
System.out.println("Encryted Password: " + encrytedPassword);
}
}
8. UserDetailsService
UserDetailsService est une interface principale dans Spring Security. Elle est un service de recherche "Le compte d'utilisateur et ses rôles". Elle est utilisée par Spring Security chaque fois que l'utilisateur se connecte au système. Par conséquent, vous devez écrire une classe d'implément (implements) cette interface.
Ici, je crée la classe UserDetailsServiceImpl qui implémente (implements) l'interface UserDetailsService. La classe UserDetailsServiceImpl est annotée par @Service pour dire Spring de la gérer comme un Spring BEAN.
UserDetailsServiceImpl.java
package org.o7planning.sbsecurity.service;
import java.util.ArrayList;
import java.util.List;
import org.o7planning.sbsecurity.dao.AppUserDAO;
import org.o7planning.sbsecurity.dao.AppRoleDAO;
import org.o7planning.sbsecurity.model.AppUser;
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;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private AppUserDAO appUserDAO;
@Autowired
private AppRoleDAO appRoleDAO;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
AppUser appUser = this.appUserDAO.findUserAccount(userName);
if (appUser == null) {
System.out.println("User not found! " + userName);
throw new UsernameNotFoundException("User " + userName + " was not found in the database");
}
System.out.println("Found User: " + appUser);
// [ROLE_USER, ROLE_ADMIN,..]
List<String> roleNames = this.appRoleDAO.getRoleNames(appUser.getUserId());
List<GrantedAuthority> grantList = new ArrayList<GrantedAuthority>();
if (roleNames != null) {
for (String role : roleNames) {
// ROLE_USER, ROLE_ADMIN,..
GrantedAuthority authority = new SimpleGrantedAuthority(role);
grantList.add(authority);
}
}
UserDetails userDetails = (UserDetails) new User(appUser.getUserName(), //
appUser.getEncrytedPassword(), grantList);
return userDetails;
}
}
9. Controllers
MainController.java
package org.o7planning.sbsecurity.controller;
import java.security.Principal;
import org.o7planning.sbsecurity.utils.WebUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class MainController {
@RequestMapping(value = { "/", "/welcome" }, method = RequestMethod.GET)
public String welcomePage(Model model) {
model.addAttribute("title", "Welcome");
model.addAttribute("message", "This is welcome page!");
return "welcomePage";
}
@RequestMapping(value = "/admin", method = RequestMethod.GET)
public String adminPage(Model model, Principal principal) {
User loginedUser = (User) ((Authentication) principal).getPrincipal();
String userInfo = WebUtils.toString(loginedUser);
model.addAttribute("userInfo", userInfo);
return "adminPage";
}
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginPage(Model model) {
return "loginPage";
}
@RequestMapping(value = "/logoutSuccessful", method = RequestMethod.GET)
public String logoutSuccessfulPage(Model model) {
model.addAttribute("title", "Logout");
return "logoutSuccessfulPage";
}
@RequestMapping(value = "/userInfo", method = RequestMethod.GET)
public String userInfo(Model model, Principal principal) {
// (1) (en)
// After user login successfully.
// (vi)
// Sau khi user login thanh cong se co principal
String userName = principal.getName();
System.out.println("User Name: " + userName);
User loginedUser = (User) ((Authentication) principal).getPrincipal();
String userInfo = WebUtils.toString(loginedUser);
model.addAttribute("userInfo", userInfo);
return "userInfoPage";
}
@RequestMapping(value = "/403", method = RequestMethod.GET)
public String accessDenied(Model model, Principal principal) {
if (principal != null) {
User loginedUser = (User) ((Authentication) principal).getPrincipal();
String userInfo = WebUtils.toString(loginedUser);
model.addAttribute("userInfo", userInfo);
String message = "Hi " + principal.getName() //
+ "<br> You do not have permission to access this page!";
model.addAttribute("message", message);
}
return "403Page";
}
}
10. Thymeleaf Template
_menu.html est considéré comme une partie du site web. Il est immersé en autres sites pour créer le Menu de la page.
_menu.html
<div xmlns:th="http://www.thymeleaf.org"
style="border: 1px solid #ccc;padding:5px;margin-bottom:20px;">
<a th:href="@{/}">Home</a>
|
<a th:href="@{/userInfo}">User Info</a>
|
<a th:href="@{/admin}">Admin</a>
|
<a th:if="${#request.userPrincipal != null}" th:href="@{/logout}">Logout</a>
</div>
welcomePage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:utext="${title}"></title>
</head>
<body>
<!-- Include _menu.html -->
<th:block th:include="/_menu"></th:block>
<h2>Message : <span th:utext="${message}"></span></h2>
</body>
</html>
loginPage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login</title>
</head>
<body>
<!-- Include _menu.html -->
<th:block th:include="/_menu"></th:block>
<h1>Login</h1>
<!-- /login?error=true -->
<div th:if="${#request.getParameter('error') == 'true'}"
style="color:red;margin:10px 0px;">
Login Failed!!!<br />
Reason :
<span th:if="${#session!= null and #session.getAttribute('SPRING_SECURITY_LAST_EXCEPTION') != null}"
th:utext="${#session.getAttribute('SPRING_SECURITY_LAST_EXCEPTION').message}">
Static summary
</span>
</div>
<h3>Enter user name and password:</h3>
<form name='f' th:action="@{/j_spring_security_check}" method='POST'>
<table>
<tr>
<td>User:</td>
<td><input type='text' name='username' value=''></td>
</tr>
<tr>
<td>Password:</td>
<td><input type='password' name='password' /></td>
</tr>
<tr>
<td><input name="submit" type="submit" value="submit" /></td>
</tr>
</table>
</form>
<br>
Username/pass:
<ul>
<li>dbuser1/123</li>
<li>dbadmin1/123</li>
</ul>
</body>
</html>
logoutSuccessfulPage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Logout</title>
</head>
<body>
<!-- Include _menu.html -->
<th:block th:include="/_menu"></th:block>
<h1>Logout Successful!</h1>
</body>
</html>
userInfoPage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>User Info</title>
</head>
<body>
<!-- Include _menu.html -->
<th:block th:include="/_menu"></th:block>
<h2>User Info Page</h2>
<h3>Welcome : <span th:utext="${#request.userPrincipal.name}"></span></h3>
<b>This is protected page!</b>
<br/><br/>
<div th:if="${userInfo != null}" th:utext="${userInfo}"></div>
</body>
</html>
adminPage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:utext="${title}"></title>
</head>
<body>
<!-- Include _menu.html -->
<th:block th:include="/_menu"></th:block>
<h2>Admin Page</h2>
<h3>Welcome :
<span th:utext="${#request.userPrincipal.name}"></span>
</h3>
<b>This is protected page!</b>
<br/><br/>
<div th:if="${userInfo != null}" th:utext="${userInfo}"></div>
</body>
</html>
Si des utilisateurs se connectent au système, mais accèdent un site non autorisé (n'appartient pas à leur rôles), le système affichera le contenu de la page /403 pour informer que l'accès est refusé (Access Denied).
403Page.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Access Denied</title>
</head>
<body>
<!-- Include _menu.html -->
<th:block th:include="/_menu"></th:block>
<h3 th:if="${message != null}" th:utext="${message}" style="color: red;"></h3>
<div th:if="${userInfo != null}" th:utext="${userInfo}"></div>
</body>
</html>
Tutoriels Spring Boot
- Installer Spring Tool Suite pour Eclipse
- Le Tutoriel de Spring pour débutant
- Le Tutoriel de Spring Boot pour débutant
- Propriétés communes de Spring Boot
- Le Tutoriel de Spring Boot et Thymeleaf
- Le Tutoriel de Spring Boot et FreeMarker
- Le Tutoriel de Spring Boot et Groovy
- Le Tutoriel de Spring Boot et Mustache
- Le Tutoriel de Spring Boot et JSP
- Le Tutoriel de Spring Boot, Apache Tiles, JSP
- Utiliser Logging dans Spring Boot
- Surveillance des applications avec Spring Boot Actuator
- Créer une application Web multilingue avec Spring Boot
- Utiliser plusieurs ViewResolvers dans Spring Boot
- Utiliser Twitter Bootstrap dans Spring Boot
- Le Tutoriel de Spring Boot Interceptor
- Le Tutoriel de Spring Boot, Spring JDBC et Spring Transaction
- Le Tutoriel de Spring JDBC
- Le Tutoriel de Spring Boot, JPA et Spring Transaction
- Le Tutoriel de Spring Boot et Spring Data JPA
- Le Tutoriel de Spring Boot, Hibernate et Spring Transaction
- Intégration de Spring Spring, JPA et H2 Database
- Le Tutoriel de Spring Boot et MongoDB
- Utiliser plusieurs DataSources avec Spring Boot et JPA
- Utiliser plusieurs DataSources avec Spring Boot et RoutingDataSource
- Créer une application de connexion avec Spring Boot, Spring Security, Spring JDBC
- Créer une application de connexion avec Spring Boot, Spring Security, JPA
- Créer une application d'enregistrement d'utilisateur avec Spring Boot, Spring Form Validation
- Exemple de OAuth2 Social Login dans Spring Boot
- Exécuter des tâches planifiées en arrière-plan dans Spring
- Exemple CRUD Restful WebService avec Spring Boot
- Exemple Spring Boot Restful Client avec RestTemplate
- Exemple CRUD avec Spring Boot, REST et AngularJS
- Sécurité Spring RESTful Service utilisant Basic Authentication
- Sécuriser Spring Boot RESTful Service en utilisant Auth0 JWT
- Exemple Upload file avec Spring Boot
- Le exemple de Download file avec Spring Boot
- Le exemple de Upload file avec Spring Boot et jQuery Ajax
- Le exemple de Upload file avec Spring Boot et AngularJS
- Créer une application Web Panier avec Spring Boot, Hibernate
- Le Tutoriel de Spring Email
- Créer une application Chat simple avec Spring Boot et Websocket
- Déployer le application Spring Boot sur Tomcat Server
- Déployer le application Spring Boot sur Oracle WebLogic Server
- Installer un certificat SSL gratuit Let's Encrypt pour Spring Boot
- Configurer Spring Boot pour rediriger HTTP vers HTTPS
Show More