Erstellen Sie eine Login-Anwendung mit Spring Boot, Spring Security, JPA
1. Das Zweck des Beispiel
Der Artikel wird nach ... geschrieben
- Spring Boot 2.x
- Spring Security
- JPA
- Thymeleaf (Or JSP)
- Database: MySQL, SQL Server, Oracle, Postgres
Im Artikel leite ich Ihnen bei der Erstellung der Applikation Login verwendend Spring Boot + Spring Security + JPA + Thymeleaf und den Operationsgrundsatz vom Spring Security.

Der Benutzer muss anmelden um die gesicherte Seite zu lesen

Der Benutzer, der in dem System angemeldet hat, wird erlaubt, die Seite nach seiner Rolle zu schauen. Wenn er die gesicherten Seite außer seiner Rolle zugriff, wird die Zugangberechtigung abgelehnt

2. Database vorbereiten
In Database haben wir 3 Tabelle APP_USER, APP_ROLE, USER_ROLE , dafür sollen Sie sich die Sorge. Außerdemm gibt es die andere Tabelle PERSISTENT_LOGINS, die durch Spring Remember Me API verwendet wird um die Information Token und die letzte Anmeldung des Benutzer zu speichern.

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. Das Projekt Spring Boot erstellen
Auf Eclipse erstellen Sie ein Projekt Spring Boot.

Geben Sie ein
- Name: SpringBootSecurityJPA
- Group: org.o7planning
- Artifact: SpringBootSecurityJPA
- Description: Spring Boot +Spring Security + JPA + Remember Me.
- Package: org.o7planning.sbsecurity

Zum nächsten brauchen Sie die Technologie und Bibliothek wählen, die verwendet werden (Im Artikel werden wir in die Database Oracle, MySQL, SQL Server oder Postgres verbinden).
Database Libraries:
- MySQL
- PostgresSQL
- SQL Server
- Web
- Thymeleaf
- Security

OK, das Projekt wird erstellt

SpringBootSecurityJpaApplication.java
package org.o7planning.sbsecurity;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootSecurityJpaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootSecurityJpaApplication.class, args);
}
}
4. pom.xml konfigurieren
Wenn Sie die Database Oracle verwendet, sollen Sie die notwendige Bibliothek für Oracle in die File pom.xml deklarieren:
** 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>
Wenn Sie in die Database SQL Service verbinden, können Sie eine der 2 Bibliothek JTDS oder Mssql-Jdbc verwenden:
** 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>
Die volle Inhalt der File 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>SpringBootSecurityJPA</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SpringBootSecurityJPA</name>
<description>Spring Boot +Spring Security + JPA + Remember Me</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-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-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.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
<!-- SQL Server Mssql-Jdbc Driver -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
<!-- SQL Server JTDS Driver -->
<dependency>
<groupId>net.sourceforge.jtds</groupId>
<artifactId>jtds</artifactId>
<version>1.3.1</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>
<repositories>
<!-- Repository for ORACLE JDBC Driver -->
<repository>
<id>codelds</id>
<url>https://code.lds.org/nexus/content/groups/main-repo</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
5. Datasource konfigurieren
Damit Spring in die Database verbinden kann, sollen Sie die notwendigen Parameter in der File application.properties konfigurieren.

application.properties (MySQL)
# ===============================
# DATABASE
# ===============================
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://tran-vmware-pc:3306/Test
spring.datasource.username=root
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
application.properties (ORACLE)
# ===============================
# DATABASE
# ===============================
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@tran-vmware-pc:1521:db12c
spring.datasource.username=Test
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle10gDialect
application.properties (SQL Server + Mssql-jdbc Driver)
# ===============================
# DATABASE
# ===============================
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
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
application.properties (SQL Server + JTDS driver)
# ===============================
# DATABASE
# ===============================
spring.datasource.driver-class-name=net.sourceforge.jtds.jdbc.Driver
spring.datasource.url=jdbc:jtds:sqlserver://tran-vmware-pc:1433/Test;instance=SQLEXPRESS
spring.datasource.username=sa
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServerDialect
application.properties (Postgres)
# ===============================
# DATABASE CONNECTION
# ===============================
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://tran-vmware-pc:5432/Test
spring.datasource.username=postgres
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
# Fix Postgres JPA Error:
# Method org.postgresql.jdbc.PgConnection.createClob() is not yet implemented.
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
6. Die Sicherheit konfigurieren & Remember Me

Diese Applikation hat einige Funktion (Seite), davon :
- /userInfo
Das ist die Seite der Benutzer-Information. Diese Seite fordert den Benutzer anzumelden und dessen Rolle ist ROLE_ADMIN oder ROLE_USER.
- /admin
Das ist die Seite für den Administrator , in die der Benutzer anmelden sollen und nur der Benutzer als Rolle von ROLE_ADMIN hat die Recht zum Zugang
- /. /welcome, /login, /logout, /403
Alle anderen Seite in der Applikation fordert den Zugang vom Benutzer nicht auf
Die Klasse WebSecurityConfig wird verwendet um die Sicherheit für die Applikation zu konfigurieren. Sie wird durch @Configuration annotiert. Die Annotation sagt Spring , dass sie eine Konfigurationsklasse ist. So wird sie durch Spring bei der Zeitpunkt des Laufen der Applikation analysiert

WebSecurityConfig.java
package org.o7planning.sbsecurity.config;
import javax.sql.DataSource;
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;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private DataSource dataSource;
@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");
// Config Remember Me.
http.authorizeRequests().and() //
.rememberMe().tokenRepository(this.persistentTokenRepository()) //
.tokenValiditySeconds(1 * 24 * 60 * 60); // 24h
}
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();
db.setDataSource(dataSource);
return db;
}
}
Was ist die Option "Remember Me"?
Der Benutzer grifft in einer Webseite zu und meldet an. Danach schließt er den Browser und meldet in die Webseite in einer Zeitpunkt an (z.B am nächsten Tag), und dann muss er wieder anmelden. Das macht kompliziert und unnötig. Die Option "Remember Me" erlaubt die Webseite die Information des Benutzer zu "erinnern". Dadurch kann der Benutzer zum nächsten Zugang der Webseite anmelden
Wenn der Benutzer in der Applikation mit der Option "Remember Me" anmeldet,wird Spring die Information vom letzten Anmeldung und token speichern. Token ist ein String (string), das kodiert (encryted) wird. Es enthaltet die notwendigen Informationen damit Spring automatisch anmeldet wenn der Benutzer in die Webseite zum nächsten Mal automatisch anmeldet.
Es gibt 2 übliche Maßnahmen, damit Spring die Information speichert:
Es gibt 2 übliche Maßnahmen, damit Spring die Information speichert:
- Memory
- Database
** WebSecurityConfig **
// Token stored in Table (Persistent_Logins)
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();
db.setDataSource(this.dataSource);
return db;
}
// Token stored in Memory (Of Web Server).
@Bean
public PersistentTokenRepository persistentTokenRepository() {
InMemoryTokenRepositoryImpl memory = new InMemoryTokenRepositoryImpl();
return memory;
}
7. Die Klasse Entity

AppRole.java
package org.o7planning.sbsecurity.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@Entity
@Table(name = "App_Role", //
uniqueConstraints = { //
@UniqueConstraint(name = "APP_ROLE_UK", columnNames = "Role_Name") })
public class AppRole {
@Id
@GeneratedValue
@Column(name = "Role_Id", nullable = false)
private Long roleId;
@Column(name = "Role_Name", length = 30, nullable = false)
private String roleName;
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
AppUser.java
package org.o7planning.sbsecurity.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@Entity
@Table(name = "App_User", //
uniqueConstraints = { //
@UniqueConstraint(name = "APP_USER_UK", columnNames = "User_Name") })
public class AppUser {
@Id
@GeneratedValue
@Column(name = "User_Id", nullable = false)
private Long userId;
@Column(name = "User_Name", length = 36, nullable = false)
private String userName;
@Column(name = "Encryted_Password", length = 128, nullable = false)
private String encrytedPassword;
@Column(name = "Enabled", length = 1, nullable = false)
private boolean enabled;
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;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
UserRole.java
package org.o7planning.sbsecurity.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@Entity
@Table(name = "User_Role", //
uniqueConstraints = { //
@UniqueConstraint(name = "USER_ROLE_UK", columnNames = { "User_Id", "Role_Id" }) })
public class UserRole {
@Id
@GeneratedValue
@Column(name = "Id", nullable = false)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "User_Id", nullable = false)
private AppUser appUser;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "Role_Id", nullable = false)
private AppRole appRole;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public AppUser getAppUser() {
return appUser;
}
public void setAppUser(AppUser appUser) {
this.appUser = appUser;
}
public AppRole getAppRole() {
return appRole;
}
public void setAppRole(AppRole appRole) {
this.appRole = appRole;
}
}
8. DAO, WebUtils

Die Klasse DAO (Data Access Object) ist die Klasse zum Zugang in der Database, wie Query, Insert, Update, Delete. Die Klasse DAO werden oft durch @Repository annotiert um mit Spring zu sagen " bitte verwalte sie wie Spring BEAN.
Die Klasse AppUserDAO wird verwendet um mit der Tabelle APP_USER zu manipulieren. Sie hat eine Methode zur Findung nach einem Benutzer in Database, der dem Benutzername entspricht
AppUserDAO.java
package org.o7planning.sbsecurity.dao;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import org.o7planning.sbsecurity.entity.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
@Transactional
public class AppUserDAO {
@Autowired
private EntityManager entityManager;
public AppUser findUserAccount(String userName) {
try {
String sql = "Select e from " + AppUser.class.getName() + " e " //
+ " Where e.userName = :userName ";
Query query = entityManager.createQuery(sql, AppUser.class);
query.setParameter("userName", userName);
return (AppUser) query.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
}
AppRoleDAO.java
package org.o7planning.sbsecurity.dao;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.o7planning.sbsecurity.entity.UserRole;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
@Transactional
public class AppRoleDAO {
@Autowired
private EntityManager entityManager;
public List<String> getRoleNames(Long userId) {
String sql = "Select ur.appRole.roleName from " + UserRole.class.getName() + " ur " //
+ " where ur.appUser.userId = :userId ";
Query query = this.entityManager.createQuery(sql, String.class);
query.setParameter("userId", userId);
return query.getResultList();
}
}
-
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);
}
}
9. UserDetailsService
UserDetailsService ist eine Zentruminterface im Spring Security. Es ist eine Dienstleistung zur Findung nach "Konto des Benutzer und die Rolle des Benutzer". Sie wird durch Spring Security verwendet jedes Mal wenn der Benutzer in das System anmeldet. Deshalb sollen Sie eine Klasse zur Interface-Implementierung schreiben
Hier erstelle ich die Klasse UserDetailsServiceImpl, die Interface UserDetailsService implementiert. Die Klasse UserDetailsServiceImpl wird durch @Service annotiert um mit Spring zu sagen: "verwalte sie wie ein 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.entity.AppUser;
import org.o7planning.sbsecurity.dao.AppRoleDAO;
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;
}
}
10. 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) {
// After user login successfully.
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";
}
}
11. Thymeleaf Template

_menu.html wird wie ein Teil der Webseite verwendet. Es wird in die anderen Seite eingebetten um das Menu der Seite zu erstellen
_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>Remember Me?</td>
<td><input type="checkbox" name="remember-me" /></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>
Wenn der Benutzer in dem System angemeldet hat aber in einer unzugriffberechtigen Seite ( nicht seine Rolle) zugegriffen hat. Das System wird die Inhalt der Seite /403 anzeigen um den Zugang abgelehnt (Access Denied) zu informieren
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>
Anleitungen Spring Boot
- Installieren Sie die Spring Tool Suite für Eclipse
- Die Anleitung zum Sping für den Anfänger
- Die Anleitung zum Spring Boot für den Anfänger
- Gemeinsame Eigenschaften von Spring Boot
- Die Anleitung zu Spring Boot und Thymeleaf
- Die Anleitung zu Spring Boot und FreeMarker
- Die Anleitung zu Spring Boot und Groovy
- Die Anleitung zu Spring Boot und Mustache
- Die Anleitung zu Spring Boot und JSP
- Die Anleitung zu Spring Boot, Apache Tiles, JSP
- Verwenden Sie Logging im Spring Boot
- Anwendungsüberwachung mit Spring Boot Actuator
- Erstellen Sie eine mehrsprachige Webanwendung mit Spring Boot
- Verwenden Sie im Spring Boot mehrere ViewResolver
- Verwenden Sie Twitter Bootstrap im Spring Boot
- Die Anleitung zu Spring Boot Interceptor
- Die Anleitung zu Spring Boot, Spring JDBC und Spring Transaction
- Die Anleitung zu Spring JDBC
- Die Anleitung zu Spring Boot, JPA und Spring Transaction
- Die Anleitung zu Spring Boot und Spring Data JPA
- Die Anleitung zu Spring Boot, Hibernate und Spring Transaction
- Spring Boot, JPA und H2-Datenbank integrieren
- Die Anleitung zu Spring Boot und MongoDB
- Verwenden Sie mehrere DataSource mit Spring Boot und JPA
- Verwenden Sie mehrere DataSource mit Spring Boot und RoutingDataSource
- Erstellen Sie eine Login-Anwendung mit Spring Boot, Spring Security, Spring JDBC
- Erstellen Sie eine Login-Anwendung mit Spring Boot, Spring Security, JPA
- Erstellen Sie eine Benutzerregistrierungsanwendung mit Spring Boot, Spring Form Validation
- Beispiel für OAuth2 Social Login im Spring Boot
- Führen Sie geplante Hintergrundaufgaben in Spring aus
- CRUD Restful Web Service Beispiel mit Spring Boot
- Beispiel Spring Boot Restful Client mit RestTemplate
- CRUD-Beispiel mit Spring Boot, REST und AngularJS
- Sichere Spring Boot RESTful Service mit Basic Authentication
- Sicherer Spring Boot RESTful Service mit Auth0 JWT
- Beispiel Upload file mit Spring Boot
- Beispiel Download File mit Spring Boot
- Das Beispiel: Spring Boot File Upload mit jQuery Ajax
- Das Beispiel File Upload mit Spring Boot und AngularJS
- Erstellen Sie eine Warenkorb-Webanwendung mit Spring Boot, Hibernate
- Die Anleitung zu Spring Email
- Erstellen Sie eine einfache Chat-Anwendung mit Spring Boot und Websocket
- Stellen Sie die Spring Boot-Anwendung auf Tomcat Server bereit
- Stellen Sie die Spring Boot-Anwendung auf Oracle WebLogic Server bereit
- Installieren Sie ein kostenloses Let's Encrypt SSL-Zertifikat für Spring Boot
- Konfigurieren Sie Spring Boot so, dass HTTP zu HTTPS umgeleitet wird
Show More