Anmeldeseite für Spring Security mit React
1. Überblick
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 vonForm Login with Spring Security auf.
2. Richten Sie React ein
Lassen Sie uns zunächstuse the command-line tool create-react-app to create an application ausführen, indem Sie den Befehl "create-react-app react”" ausführen.
Wir haben eine Konfiguration wie die folgende inreact/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 werden wiruse the frontend-maven-plugin to help build our React project with Maven:
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 unterhere.
Wenn wirmvn compile ausführen, lädt dieses Pluginnode undnpm herunter, installiert alle Knotenmodulabhängigkeiten und erstellt das Reaktionsprojekt für uns.
Es gibt verschiedene Konfigurationseigenschaften, die wir hier erklären müssen. Wir haben die Versionen vonnode undnpm 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 vonnpm.
3. Spring-Sicherheitskonfiguration
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");
}
}
Note that we add the login page “index.html” as a static resource anstelle einer dynamisch bereitgestellten JSP.
Als Nächstes aktualisieren wir die Spring Security-Konfiguration, um den Zugriff auf diese statischen Ressourcen zu ermöglichen.
Anstatt“login.jsp” wie inthe previous form login Artikel zu verwenden, verwenden wir hier“index.html” alsLogin 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 wir aus dem obigen Snippet sehen können, wenn wir Formulardaten an "/perform_login" senden, leitet Spring uns zu "/homepage.html" um, 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 eine Formularanmeldung mithilfe von Komponenten.
Beachten Sie, dass wir zum Erstellen unserer Anwendung die ES6-Syntax (ECMAScript 2015) verwenden.
4.1. Eingang
Beginnen wir mit einerInput-Komponente, die die<input />-Elemente des Anmeldeformulars inreact/src/Input.js unterstützt:
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 gezeigt, verpacken wir das<input />-Element in eine reaktionsgesteuerte Komponente, um ihren Status verwalten und eine Feldvalidierung durchführen zu können.
React bietet eine Möglichkeit, die Typen mitPropTypes zu validieren. Insbesondere verwenden wirInput.propTypes = \{…}, um den Typ der vom Benutzer übergebenen Eigenschaften zu überprüfen.
Beachten Sie, dass die Validierung vonPropTypenur für die Entwicklung funktioniert. Mit der Validierung vonPropTypeoll ü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. Form
Als Nächstes erstellen wir eine generische Formularkomponente in der DateiForm.js, die mehrere Instanzen unsererInput-Komponente kombiniert, auf denen wir unser Anmeldeformular basieren können.
In derForm-Komponente nehmen wir Attribute der HTML<input/>-Elemente und erstellen darausInput-Komponenten.
Dann werden dieInput-Komponenten und Validierungsfehlermeldungen in dieForm: 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 (
)
}
}
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 FunktionhandleError, um den Fehlerstatus des Formulars zu verwalten. Denken Sie daran, dass wir es auch für die Feldvalidierung vonInputverwendet haben. Actually, handleError() is passed to the Input Components as a callback in the render() function.
Wir verwendenrenderError(), um das Fehlermeldungselement zu erstellen. Beachten Sie, dass der KonstruktorForm’sdie Eigenschafterrorverwendet. Diese Eigenschaft gibt an, ob die Anmeldeaktion fehlschlägt.
Dann kommt der Formularübermittlungs-Handler:
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 inFormData und senden sie mitfetch API an den Server.
Vergessen wir nicht, dass unser Anmeldeformular mitsuccessUrl undfailureUrl geliefert wird. 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. Formularwiedergabe
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 unterreact/public/index.html):
Schließlich rendern wir das Formular in<div/> mit der ID "container” inreact/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:username undpassword sowie eine Schaltfläche zum Senden.
Hier übergeben wir ein zusätzlicheserror-Attribut an dieForm-Komponente, da wir Anmeldefehler nach der Umleitung zur Fehler-URL behandeln möchten:/index.html?error=true.
Jetzt haben wir die Erstellung einer Spring Security-Anmeldeanwendung mit React abgeschlossen. Als letztes müssen wirmvn compile ausführen.
Während des Vorgangs hilft das Maven-Plugin beim Erstellen unserer React-Anwendung und beim Erfassen des Erstellungsergebnisses insrc/main/webapp/WEB-INF/view/react/build.
5. Fazit
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 mitReact Router oderRedux umfassen. Dies würde jedoch den Rahmen dieses Artikels sprengen.
Wie immer kann die vollständige Implementierungover on Github gefunden werden. Um es lokal auszuführen, führen Siemvn jetty:run im Projektstammordner aus. Anschließend können Sie unterhttp://localhost:8080 auf die React-Anmeldeseite zugreifen.