Spring Security Login Page mit React

1. Übersicht

React ist eine komponentenbasierte JavaScript-Bibliothek, die von Facebook erstellt wurde. Mit React können wir problemlos komplexe Webanwendungen erstellen. In diesem Artikel wird Spring Security mit einer React Login-Seite zusammenarbeiten.

Wir werden die vorhandenen Spring Security-Konfigurationen der vorherigen Beispiele nutzen. Wir bauen also auf einem früheren Artikel über das Erstellen eines Formular-Logins mit Spring Security auf.

2. Richten Sie React ein

Lassen Sie uns zunächst das Kommandozeilen - Tool verwenden , erstellen reagieren-App , eine Anwendung zu erstellen , indem Sie den Befehl ausführen „ erstellen reagieren-App reagieren“ .

Wir haben eine Konfiguration wie die folgende in react / package.json :

{ "name": "react", "version": "0.1.0", "private": true, "dependencies": { "react": "^16.4.1", "react-dom": "^16.4.1", "react-scripts": "1.1.4" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" } }

Dann verwenden wir das Frontend-Maven-Plugin, um unser React-Projekt mit Maven zu erstellen:

 com.github.eirslett frontend-maven-plugin 1.6  v8.11.3 6.1.0 src/main/webapp/WEB-INF/view/react    install node and npm  install-node-and-npm    npm install  npm    npm run build  npm   run build    

Die neueste Version des Plugins finden Sie hier.

Wenn wir mvn compile ausführen , lädt dieses Plugin Node und Npm herunter , installiert alle Knotenmodulabhängigkeiten und erstellt das Reaktionsprojekt für uns.

Es gibt verschiedene Konfigurationseigenschaften, die wir hier erläutern müssen. Wir haben die Versionen von node und npm angegeben , damit das Plugin weiß, welche Version heruntergeladen werden soll.

Unsere React-Anmeldeseite wird im Frühjahr als statische Seite dienen. Daher verwenden wir " src / main / webapp / WEB-INF / view / react " als Arbeitsverzeichnis von npm .

3. Spring Security-Konfiguration

Bevor wir uns mit den React-Komponenten befassen, aktualisieren wir die Spring-Konfiguration, um die statischen Ressourcen unserer React-App bereitzustellen:

@EnableWebMvc @Configuration public class MvcConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers( ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**") .addResourceLocations("/WEB-INF/view/react/build/static/"); registry.addResourceHandler("/*.js") .addResourceLocations("/WEB-INF/view/react/build/"); registry.addResourceHandler("/*.json") .addResourceLocations("/WEB-INF/view/react/build/"); registry.addResourceHandler("/*.ico") .addResourceLocations("/WEB-INF/view/react/build/"); registry.addResourceHandler("/index.html") .addResourceLocations("/WEB-INF/view/react/build/index.html"); } }

Beachten Sie, dass wir die Anmeldeseite "index.html" als statische Ressource anstelle einer dynamisch bereitgestellten JSP hinzufügen .

Als Nächstes aktualisieren wir die Spring Security-Konfiguration, um den Zugriff auf diese statischen Ressourcen zu ermöglichen.

Anstelle der Verwendung von „login.jsp“ , wie wir in der bisherigen Form Login Artikel haben, hier verwenden wir „index.html“ als unsere Login - Seite:

@Configuration @EnableWebSecurity @Profile("!https") public class SecSecurityConfig extends WebSecurityConfigurerAdapter { //... @Override protected void configure(final HttpSecurity http) throws Exception { http.csrf().disable().authorizeRequests() //... .antMatchers( HttpMethod.GET, "/index*", "/static/**", "/*.js", "/*.json", "/*.ico") .permitAll() .anyRequest().authenticated() .and() .formLogin().loginPage("/index.html") .loginProcessingUrl("/perform_login") .defaultSuccessUrl("/homepage.html",true) .failureUrl("/index.html?error=true") //... } }

Wie aus dem obigen Snippet hervorgeht , leitet Spring uns beim Posten von Formulardaten an " / perform_login " zu " /homepage.html " weiter, wenn die Anmeldeinformationen erfolgreich übereinstimmen, und ansonsten zu " /index.html?error=true ".

4. Komponenten reagieren

Lassen Sie uns jetzt React die Hände schmutzig machen. Wir erstellen und verwalten ein Formular-Login mit Komponenten.

Beachten Sie, dass wir zum Erstellen unserer Anwendung die ES6-Syntax (ECMAScript 2015) verwenden.

4.1. Eingang

Beginnen wir mit einer Eingabekomponente , die das unterstütztElemente des Anmeldeformulars in react / src / Input.js :

import React, { Component } from 'react' import PropTypes from 'prop-types' class Input extends Component { constructor(props){ super(props) this.state = { value: props.value? props.value : '', className: props.className? props.className : '', error: false } } //... render () { const {handleError, ...opts} = this.props this.handleError = handleError return (  ) } } Input.propTypes = { name: PropTypes.string, placeholder: PropTypes.string, type: PropTypes.string, className: PropTypes.string, value: PropTypes.string, handleError: PropTypes.func } export default Input

Wie oben zu sehen, wickeln wir die Element in eine reaktionsgesteuerte Komponente, um ihren Status verwalten und eine Feldvalidierung durchführen zu können.

React bietet eine Möglichkeit, die Typen mithilfe von PropTypes zu validieren . Insbesondere verwenden wir Input.propTypes = {…} , um den Typ der vom Benutzer übergebenen Eigenschaften zu überprüfen .

Beachten Sie, dass die PropType- Validierung nur für die Entwicklung funktioniert. Mit der PropType- Validierung soll überprüft werden, ob alle Annahmen, die wir zu unseren Komponenten treffen, erfüllt sind.

Es ist besser, es zu haben, als sich von zufälligen Schluckaufen in der Produktion überraschen zu lassen.

4.2. Bilden

Als nächstes werden wir eine generische Form Komponente in der Datei erstellen Form.js , die mehrere Instanzen unserer kombiniert Eingang , auf der Komponente wir unser Anmeldeformular aufbauen können.

In der Formularkomponente übernehmen wir HTML-AttributeElemente und erstellen Sie daraus Eingabekomponenten .

Anschließend werden die Eingabekomponenten und Validierungsfehlermeldungen in das Formular eingefügt :

import React, { Component } from 'react' import PropTypes from 'prop-types' import Input from './Input' class Form extends Component { //... render() { const inputs = this.props.inputs.map( ({name, placeholder, type, value, className}, index) => (  ) ) const errors = this.renderError() return (  {this.form=fm}} > {inputs} {errors}  ) } } Form.propTypes = { name: PropTypes.string, action: PropTypes.string, method: PropTypes.string, inputs: PropTypes.array, error: PropTypes.string } export default Form

Schauen wir uns nun an, wie wir Feldvalidierungsfehler und Anmeldefehler verwalten:

class Form extends Component { constructor(props) { super(props) if(props.error) { this.state = { failure: 'wrong username or password!', errcount: 0 } } else { this.state = { errcount: 0 } } } handleError = (field, errmsg) => { if(!field) return if(errmsg) { this.setState((prevState) => ({ failure: '', errcount: prevState.errcount + 1, errmsgs: {...prevState.errmsgs, [field]: errmsg} })) } else { this.setState((prevState) => ({ failure: '', errcount: prevState.errcount===1? 0 : prevState.errcount-1, errmsgs: {...prevState.errmsgs, [field]: ''} })) } } renderError = () => { if(this.state.errcount || this.state.failure) { const errmsg = this.state.failure || Object.values(this.state.errmsgs).find(v=>v) return {errmsg} } } //... }

In diesem Snippet definieren wir die Funktion handleError , um den Fehlerstatus des Formulars zu verwalten. Denken Sie daran, dass wir es auch für die Validierung von Eingabefeldern verwendet haben. Eigentlich handelt () wird auf die übergebenen Eingabekomponenten als Callback in der Render () Funktion .

Wir verwenden renderError () , um das Fehlermeldungselement zu erstellen . Beachten Sie, dass der Konstruktor von Form eine Fehlereigenschaft verwendet . Diese Eigenschaft gibt an, ob die Anmeldeaktion fehlschlägt.

Dann kommt der Formularübermittler:

class Form extends Component { //... handleSubmit = (event) => { event.preventDefault() if(!this.state.errcount) { const data = new FormData(this.form) fetch(this.form.action, { method: this.form.method, body: new URLSearchParams(data) }) .then(v => { if(v.redirected) window.location = v.url }) .catch(e => console.warn(e)) } } }

Wir verpacken alle Formularfelder in FormData und senden sie mithilfe der Abruf- API an den Server .

Vergessen wir nicht, dass unser Anmeldeformular eine SuccessUrl und eine FailureUrl enthält. Dies bedeutet, dass die Antwort unabhängig davon, ob die Anforderung erfolgreich ist oder nicht, eine Umleitung erfordern würde.

Aus diesem Grund müssen wir die Umleitung im Antwortrückruf behandeln.

4.3. Formular rendern

Nachdem wir alle benötigten Komponenten eingerichtet haben, können wir sie weiterhin im DOM ablegen. Die grundlegende HTML-Struktur lautet wie folgt (finden Sie sie unter react / public / index.html ):

Zum Schluss rendern wir das Formular in das mit der ID " container" in react / src / index.js :

import React from 'react' import ReactDOM from 'react-dom' import './index.css' import Form from './Form' const inputs = [{ name: "username", placeholder: "username", type: "text" },{ name: "password", placeholder: "password", type: "password" },{ type: "submit", value: "Submit", className: "btn" }] const props = { name: 'loginForm', method: 'POST', action: '/perform_login', inputs: inputs } const params = new URLSearchParams(window.location.search) ReactDOM.render( , document.getElementById('container'))

Unser Formular enthält jetzt zwei Eingabefelder: Benutzername und Passwort sowie eine Schaltfläche zum Senden.

Hier geben wir ein zusätzliches Fehler - Attribut auf die Formularkomponente , weil wir nach Umleitung Login Fehler behandeln wollen , zum Scheitern URL: /index.html?error=true .

Jetzt haben wir die Erstellung einer Spring Security-Anmeldeanwendung mit React abgeschlossen. Als letztes müssen wir mvn compile ausführen .

Während des Prozesses hilft das Maven-Plugin beim Erstellen unserer React-Anwendung und beim Sammeln des Build-Ergebnisses in src / main / webapp / WEB-INF / view / react / build .

5. Schlussfolgerung

In diesem Artikel wurde erläutert, wie Sie eine React-Anmelde-App erstellen und mit einem Spring Security-Backend interagieren lassen. Eine komplexere Anwendung würde den Statusübergang und das Routing mit React Router oder Redux beinhalten, aber das würde den Rahmen dieses Artikels sprengen.

Wie immer finden Sie die vollständige Implementierung auf GitHub. Um es lokal auszuführen, führen Sie mvn jetty: run im Projektstammordner aus. Anschließend können Sie unter // localhost: 8080 auf die React-Anmeldeseite zugreifen .