Spring Security bietet Möglichkeiten zur Durchführung von Authentifizierung und Autorisierung in einer Webanwendung. Wir können Spring Security in jeder auf Servlets basierenden Webanwendung verwenden.
Spring Security
Einige der Vorteile der Verwendung von Spring Security sind:
- Erprobte Technologie, es ist besser, dies zu verwenden, als das Rad neu zu erfinden. Sicherheit ist etwas, bei dem wir besonders vorsichtig sein müssen, sonst wird unsere Anwendung anfällig für Angriffe.
- Verhindert einige der häufigsten Angriffe wie CSRF, Session-Fixierung.
- Einfach in jede Webanwendung zu integrieren. Wir müssen die Konfiguration der Webanwendung nicht ändern, Spring fügt automatisch Sicherheitsfilter in die Webanwendung ein.
- Bietet Unterstützung für die Authentifizierung auf unterschiedliche Weise – im Speicher, DAO, JDBC, LDAP und viele mehr.
- Bietet die Möglichkeit, bestimmte URL-Muster zu ignorieren, was gut ist, um statische HTML- und Bilddateien bereitzustellen.
- Unterstützung für Gruppen und Rollen.
Beispiel für Spring Security
Wir werden eine Webanwendung erstellen und sie mit Spring Security integrieren. Erstellen Sie eine Webanwendung mit der Option „Dynamic Web Project“ in Eclipse, damit unsere Grundstruktur für die Webanwendung bereit ist. Stellen Sie sicher, dass Sie sie in ein Maven-Projekt umwandeln, da wir Maven für den Build und die Bereitstellung verwenden. Wenn Sie mit diesen Schritten nicht vertraut sind, lesen Sie bitte das Java Web Application Tutorial. Sobald unsere Anwendung gesichert ist, wird die endgültige Projektstruktur wie im folgenden Bild aussehen. Wir werden uns drei Spring Security-Authentifizierungsmethoden ansehen.
- in-memory
- DAO
- JDBC
Für JDBC verwende ich eine MySQL-Datenbank und habe das folgende 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 Example.
Spring Security Maven Dependencies
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>
Wir haben folgende Abhängigkeiten im Zusammenhang mit dem Spring Framework.
- spring-jdbc: Dies wird für JDBC-Operationen durch die JDBC-Authentifizierungsmethode verwendet. Es erfordert die Einrichtung einer DataSource als JNDI. Für ein vollständiges Beispiel zur Verwendung davon siehe Spring DataSource JNDI Beispiel
- spring-security-taglibs: Spring Security-Tag-Bibliothek, ich habe sie verwendet, um Benutzerrollen auf der JSP-Seite anzuzeigen. In den meisten Fällen werden Sie es jedoch nicht benötigen.
- spring-security-config: Es wird zum Konfigurieren der Authentifizierungsanbieter verwendet, ob JDBC, DAO, LDAP usw. verwendet werden sollen.
- spring-security-web: Dieses Komponente integriert die Spring Security in die Servlet-API. Wir benötigen es, um unsere Sicherheitskonfiguration in die Webanwendung einzufügen.
Beachten Sie auch, dass wir das Servlet-API-3.0-Feature verwenden werden, um Listener und Filter programmgesteuert hinzuzufügen. Aus diesem Grund sollte die Servlet-API-Version in den Abhängigkeiten 3.0 oder höher sein.
Spring Security Beispiel Ansichtsseiten
Wir haben JSP- und HTML-Seiten in unserer Anwendung. Wir möchten die Authentifizierung auf allen Seiten außer den HTML-Seiten anwenden. health.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Health Check</title>
</head>
<body>
<h3>Service is up and running!!</h3>
</body>
</html>
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="https://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="https://www.springframework.org/security/tags" prefix="sec" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Home Page</title>
</head>
<body>
<h3>Home Page</h3>
<p>
Hello <b><c:out value="${pageContext.request.remoteUser}"/></b><br>
Roles: <b><sec:authentication property="principal.authorities" /></b>
</p>
<form action="logout" method="post">
<input type="submit" value="Logout" />
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
</body>
</html>
I have included index.jsp
as welcome-file
in the application deployment descriptor. Spring Security takes care of CSRF attack, so when we are submitting form for logout, we are sending the CSRF token back to server to delete it. The CSRF object set by Spring Security component is _csrf and we are using it’s property name and token value to pass along in the logout request. Let’s look at the Spring Security configurations now.
Spring Security Beispiel Benutzerdetailservice DAO Implementierung
Da wir auch eine auf DAO basierende Authentifizierung verwenden werden, müssen wir das Interface UserDetailsService
implementieren und die Implementierung für die Methode loadUserByUsername()
bereitstellen. Idealerweise sollten wir eine Ressource verwenden, um den Benutzer zu validieren, aber ich mache hier aus Einfachheitsgründen 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 + " not found");
}
// 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 auths = new java.util.ArrayList();
auths.add(new SimpleGrantedAuthority("admin"));
return auths;
}
};
}
}
Beachten Sie, dass ich eine anonyme innere Klasse von UserDetails
erstelle und sie zurückgebe. Sie können eine Implementierungsklasse dafür erstellen und sie 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 Interface WebSecurityConfigurer
implementieren oder die Basisklassenimplementierung 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 von benutzerdefiniertem 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 mit .html enden, vollständig ignorieren
.antMatchers("/*.html");
}
}
Beachten Sie, dass alle HTML-Dateien durch Überschreiben der configure(WebSecurity web)
-Methode ignoriert werden. Der Code zeigt, wie die JDBC-Authentifizierung eingebunden wird. Sie müssen es konfigurieren, indem Sie den DataSource bereitstellen. Da wir benutzerdefinierte Tabellen verwenden, müssen Sie auch die Select-Abfragen bereitstellen, um die Benutzerdetails und deren Rollen zu erhalten. Die Konfiguration von In-Memory- und DAO-basierter Authentifizierung ist einfach, sie sind im obigen Code auskommentiert. Sie können sie auskommentieren, um sie zu verwenden. Stellen Sie sicher, dass immer nur eine Konfiguration aktiv ist. Die Annotationen @Configuration
und @EnableWebSecurity
sind erforderlich, damit das Spring-Framework weiß, dass diese Klasse für die Spring-Sicherheitskonfiguration verwendet wird. Die Spring Security-Konfiguration verwendet das Builder Pattern und basiert auf der authenticate-Methode. Einige Methoden stehen später nicht mehr zur Verfügung. Zum Beispiel gibt auth.userDetailsService()
die Instanz von UserDetailsService
zurück, und dann können keine anderen Optionen festgelegt werden, wie zum Beispiel das Festlegen des DataSource nachfolgend.
Integrieren von Spring Security Web mit Servlet API
Der letzte Schritt besteht darin, unsere Spring Security Konfigurationsklasse in die Servlet-API zu integrieren. Dies kann einfach durch Erweitern der Klasse AbstractSecurityWebApplicationInitializer
und Übergeben der Sicherheitskonfigurationsklasse im Konstruktor der Superklasse erfolgen. 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);
}
}
Beim Starten unseres Kontexts verwendet er den ServletContext, um den ContextLoaderListener-Listener hinzuzufügen und unsere Konfigurationsklasse als Servlet-Filter zu registrieren. Beachten Sie, dass dies nur in Servlet-3-kompatiblen Servlet-Containern funktioniert. Wenn Sie Apache Tomcat verwenden, stellen Sie sicher, dass dessen 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 für ungültige 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. Weitere Details finden Sie in der JavaDoc von WebApplicationInitializer
. Das war alles für das Beispiel-Tutorial zu Spring Security und seine Integration in eine servletbasierte Webanwendung. Laden Sie das Beispielprojekt über den unten stehenden Link herunter und probieren Sie es aus, um mehr zu erfahren.
Source:
https://www.digitalocean.com/community/tutorials/spring-security-example-tutorial