Spring Security
Spring Security bietet Möglichkeiten zur Authentifizierung und Autorisierung in einer Webanwendung. Wir können Spring Security in jeder servletbasierten Webanwendung verwenden.
Vorteile der Verwendung von Spring Security
Spring-Sicherheitsbeispiel-TutorialEinige der Vorteile der Verwendung von Spring Security sind:
- Bewährte Technologie, es ist besser, diese zu verwenden, als das Rad neu zu erfinden. Sicherheit ist etwas, bei dem wir besonders vorsichtig sein müssen, sonst ist unsere Anwendung für Angreifer anfällig.
- Verhindert einige der häufigsten Angriffe wie CSRF, Session-Fixierung-Angriffe.
- Einfach in jede Webanwendung zu integrieren. Wir müssen keine Webanwendungskonfigurationen ändern, Spring fügt automatisch Sicherheitsfilter in die Webanwendung ein.
- Bietet Unterstützung für Authentifizierung auf verschiedene Weisen – im Speicher, DAO, JDBC, LDAP und viele mehr.
- Bietet die Möglichkeit, spezifische URL-Muster zu ignorieren, gut für die Bereitstellung statischer HTML-, Bild-Dateien.
- Unterstützung für Gruppen und Rollen.
Spring Security Beispiel
Wir werden eine Webanwendung erstellen und sie mit Spring Security integrieren. Erstellen Sie eine Webanwendung mit der Option „Dynamic Web Project“ in Eclipse, damit unser Grundgerüst der Webanwendung bereit ist. Stellen Sie sicher, dass Sie es in ein Maven-Projekt umwandeln, da wir Maven für Build und Deployment verwenden. Sobald unsere Anwendung gesichert ist, wird die endgültige Projektstruktur wie das folgende Bild aussehen.
Wir werden uns drei Spring Security-Authentifizierungsmethoden ansehen.
- im Speicher
- DAO
- JDBC
Datenbankskript für JDBC-Authentifizierung
Für JDBC verwenden wir eine MySQL-Datenbank und haben folgendes Skript ausgeführt, um die Benutzerdetailtabellen zu erstellen.
CREATE TABLE `Employees` (
`username` varchar(20) NOT NULL DEFAULT '',
`password` varchar(20) NOT NULL DEFAULT '',
`enabled` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `Roles` (
`username` varchar(20) NOT NULL DEFAULT '',
`role` varchar(20) NOT NULL DEFAULT '',
PRIMARY KEY (`username`,`role`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `Employees` (`username`, `password`, `enabled`)
VALUES
('pankaj', 'pankaj123', 1);
INSERT INTO `Roles` (`username`, `role`)
VALUES
('pankaj', 'Admin'),
('pankaj', 'CEO');
commit;
Wir müssen auch JDBC DataSource als JNDI in unserem Servlet-Container konfigurieren, um mehr darüber zu erfahren, lesen Sie bitte das Tomcat JNDI DataSource-Beispiel.
Spring Security Maven-Abhängigkeiten
Hier ist unsere endgültige pom.xml-Datei.
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>WebappSpringSecurity</groupId>
<artifactId>WebappSpringSecurity</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!-- Spring Security Artifacts - START -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<!-- Spring Security Artifacts - END -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.0.2.RELEASE</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>
Spring Security Beispiel UserDetailsService DAO-Implementierung
Da wir auch eine DAO-basierte Authentifizierung verwenden werden, müssen wir das UserDetailsService-Interface implementieren und die Implementierung für die Methode loadUserByUsername() bereitstellen. Idealerweise sollten wir eine Ressource verwenden, um den Benutzer zu validieren, aber zur Vereinfachung machen wir nur eine grundlegende Validierung.
AppUserDetailsServiceDAO.java
package com.journaldev.webapp.spring.dao;
import java.util.Collection;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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;
public class AppUserDetailsServiceDAO implements UserDetailsService {
protected final Log logger = LogFactory.getLog(getClass());
@Override
public UserDetails loadUserByUsername(final String username)
throws UsernameNotFoundException {
logger.info("loadUserByUsername username="+username);
if(!username.equals("pankaj")){
throw new UsernameNotFoundException(username + " nicht gefunden");
}
// Erstellen von Dummy-Benutzerdetails, sollte JDBC-Operationen durchführen
return new UserDetails() {
private static final long serialVersionUID = 2059202961588104658L;
@Override
public boolean isEnabled() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public String getUsername() {
return username;
}
@Override
public String getPassword() {
return "pankaj123";
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> auths = new java.util.ArrayList<SimpleGrantedAuthority>();
auths.add(new SimpleGrantedAuthority("admin"));
return auths;
}
};
}
}
Beachten Sie, dass wir eine anonyme innere Klasse von UserDetails erstellen und zurückgeben. Sie können eine Implementierungsklasse dafür erstellen und dann instanziieren und zurückgeben. Normalerweise ist das der Weg, den man in tatsächlichen Anwendungen gehen sollte.
Spring Security Beispiel WebSecurityConfigurer-Implementierung
Wir können das WebSecurityConfigurer-Interface implementieren oder die Basisklasse WebSecurityConfigurerAdapter erweitern und die Methoden überschreiben.
SecurityConfig.java
package com.journaldev.webapp.spring.security;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import com.journaldev.webapp.spring.dao.AppUserDetailsServiceDAO;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
// In-Memory-Authentifizierung
// auth.inMemoryAuthentication().withUser("pankaj").password("pankaj123").roles("USER");
// Verwendung des benutzerdefinierten UserDetailsService DAO
// auth.userDetailsService(new AppUserDetailsServiceDAO());
// Verwendung von JDBC
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx
.lookup("java:/comp/env/jdbc/MyLocalDB");
final String findUserQuery = "select username,password,enabled "
+ "from Employees " + "where username = ?";
final String findRoles = "select username,role " + "from Roles "
+ "where username = ?";
auth.jdbcAuthentication().dataSource(ds)
.usersByUsernameQuery(findUserQuery)
.authoritiesByUsernameQuery(findRoles);
}
@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
// Spring Security sollte URLs, die auf .html enden, komplett ignorieren
.antMatchers("/*.html");
}
}
Beachten Sie, dass wir alle HTML-Dateien ignorieren, indem wir die Methode configure(WebSecurity web) überschreiben. Der Code zeigt, wie man JDBC-Authentifizierung einbindet. Wir müssen es konfigurieren, indem wir DataSource bereitstellen. Da wir benutzerdefinierte Tabellen verwenden, müssen wir auch die Select-Abfragen bereitstellen, um die Benutzerdetails und deren Rollen zu erhalten. Die Konfiguration der In-Memory- und DAO-basierten Authentifizierung ist einfach, sie sind im obigen Code kommentiert. Sie können sie entkommentieren, um sie zu verwenden, stellen Sie sicher, dass Sie jeweils nur eine Konfiguration haben. Die Annotationen @Configuration und @EnableWebSecurity sind erforderlich, damit das Spring-Framework weiß, dass diese Klasse für die Spring Security-Konfiguration verwendet wird. Die Spring Security-Konfiguration verwendet das Builder-Muster und basierend auf der Methode authenticate stehen einige der Methoden später nicht mehr zur Verfügung. Zum Beispiel gibt auth.userDetailsService() die Instanz von UserDetailsService zurück und dann können wir keine anderen Optionen mehr haben, wie z.B. können wir DataSource danach nicht mehr festlegen.
Integration von Spring Security Web mit Servlet API
Der letzte Teil besteht darin, unsere Spring Security-Konfigurationsklasse in das Servlet API zu integrieren. Dies kann einfach erfolgen, indem man die Klasse AbstractSecurityWebApplicationInitializer erweitert und die Sicherheitskonfigurationsklasse im Superklassenkonstruktor übergibt.
SecurityWebApplicationInitializer.java
package com.journaldev.webapp.spring.security;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityWebApplicationInitializer extends
AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(SecurityConfig.class);
}
}
Wenn unser Kontext startet, verwendet er ServletContext, um den ContextLoaderListener-Listener hinzuzufügen und unsere Konfigurationsklasse als Servlet-Filter zu registrieren. Beachten Sie, dass dies nur in Servlet-3-konformen Servlet-Containern funktionieren wird. Wenn Sie also Apache Tomcat verwenden, stellen Sie sicher, dass seine Version 7.0 oder höher ist. Unser Projekt ist bereit, deployen Sie es einfach in Ihrem bevorzugten Servlet-Container. Ich verwende Apache Tomcat-7, um diese Anwendung auszuführen. Die folgenden Bilder zeigen die Antwort in verschiedenen Szenarien.
Zugriff auf HTML-Seite ohne Sicherheit
Authentifizierung fehlgeschlagen bei schlechten Anmeldeinformationen
Startseite mit Spring Security JDBC-Authentifizierung
Startseite mit Spring Security UserDetailsService DAO-Authentifizierung
Startseite mit Spring Security In-Memory-Authentifizierung
Abmeldeseite
Wenn Sie einen Servlet-Container verwenden möchten, der die Servlet-Spezifikationen 3 nicht unterstützt, müssen Sie den DispatcherServlet über den Bereitstellungsdeskriptor registrieren. Das ist alles für den Spring Security-Leitfaden und seine Integration in servletbasierte Webanwendungen.