Spring Security Beispiel Tutorial

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:

  1. 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.
  2. Verhindert einige der häufigsten Angriffe wie CSRF, Session-Fixierung.
  3. Einfach in jede Webanwendung zu integrieren. Wir müssen die Konfiguration der Webanwendung nicht ändern, Spring fügt automatisch Sicherheitsfilter in die Webanwendung ein.
  4. Bietet Unterstützung für die Authentifizierung auf unterschiedliche Weise – im Speicher, DAO, JDBC, LDAP und viele mehr.
  5. Bietet die Möglichkeit, bestimmte URL-Muster zu ignorieren, was gut ist, um statische HTML- und Bilddateien bereitzustellen.
  6. 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.

  1. in-memory
  2. DAO
  3. 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.

  1. 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
  2. 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.
  3. spring-security-config: Es wird zum Konfigurieren der Authentifizierungsanbieter verwendet, ob JDBC, DAO, LDAP usw. verwendet werden sollen.
  4. 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 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.

Spring Servlet Security Projekt herunterladen

Source:
https://www.digitalocean.com/community/tutorials/spring-security-example-tutorial