Spring Security-inlogpagina met React

1. Overzicht

React is een op componenten gebaseerde JavaScript-bibliotheek die is gebouwd door Facebook. Met React kunnen we met gemak complexe webapplicaties bouwen. In dit artikel gaan we Spring Security laten samenwerken met een React Login-pagina.

We zullen profiteren van de bestaande Spring Security-configuraties van eerdere voorbeelden. We zullen dus voortbouwen op een eerder artikel over het maken van een formulieraanmelding met Spring Security.

2. Stel React in

Laten we eerst gebruik het opdrachtregelprogramma create-react-app om een ​​applicatie te maken door het commando 'create-react-app react ”.

We hebben een configuratie zoals de volgende in react / package.json:

{"name": "react", "version": "0.1.0", "private": true, "afhankelijkheden": {"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 uitwerpen "}}

Dan zullen we gebruik de frontend-maven-plugin om ons React-project met Maven te helpen bouwen:

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

De laatste versie van de plug-in is hier te vinden.

Als we rennen mvn compileren, wordt deze plug-in gedownload knooppunt en npm, installeer alle afhankelijkheden van knooppuntmodules en bouw het react-project voor ons.

Er zijn verschillende configuratie-eigenschappen die we hier moeten uitleggen. We hebben de versies van knooppunt en npm, zodat de plug-in weet welke versie hij moet downloaden.

Onze React-inlogpagina zal in het voorjaar als een statische pagina dienen, dus gebruiken we "src / main /web applicatie/ WEB-INF / bekijk / reageer" net zo npm‘S werkmap.

3. Lente-beveiligingsconfiguratie

Voordat we in de React-componenten duiken, werken we de Spring-configuratie bij om de statische bronnen van onze React-app te bedienen:

@EnableWebMvc @Configuration public class MvcConfig breidt WebMvcConfigurerAdapter uit {@Override public void addResourceHandlers (ResourceHandlerRegistry-register) {registry.addResourceHandler ("/ static / **") .addResourceLocations ("/ WEB-build / view ); 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"); }}

Merk op dat we de inlogpagina toevoegen "Index.html" als een statische bron in plaats van een dynamisch bediende JSP.

Vervolgens werken we de Spring Security-configuratie bij om toegang tot deze statische bronnen mogelijk te maken.

In plaats van gebruiken "Login.jsp" zoals we deden in het vorige aanmeldingsartikel, gebruiken we hier "Index.html" Als onze Log in bladzijde:

@Configuration @EnableWebSecurity @Profile ("! Https") openbare klasse SecSecurityConfig breidt WebSecurityConfigurerAdapter uit {// ... @Override protected void configure (laatste HttpSecurity http) gooit uitzondering {http.csrf (). Disable (). AuthorizeRequests () / / ... .antMatchers (HttpMethod.GET, "/ index *", "/ static / **", "/*.js", "/*.json", "/*.ico") .permitAll () .anyRequest (). geverifieerd (). en () .formLogin (). loginPage ("/ index.html") .loginProcessingUrl ("/ perform_login") .defaultSuccessUrl ("/ homepage.html", true) .failureUrl (" /index.html?error=true ") // ...}}

Zoals we kunnen zien in het bovenstaande fragment wanneer we formuliergegevens posten op '/ perform_login", Spring zal ons omleiden naar"/homepage.html"Als de inloggegevens met succes overeenkomen en naar"/index.html?error=waar”Anders.

4. Reageer componenten

Laten we nu onze handen vuil maken aan React. We bouwen en beheren een aanmeldingsformulier met behulp van componenten.

Merk op dat we de syntaxis ES6 (ECMAScript 2015) zullen gebruiken om onze applicatie te bouwen.

4.1. Invoer

Laten we beginnen met een Invoer component die de elementen van het inlogformulier in react / src / Input.js:

import React, {Component} van 'react' importeer PropTypes van 'prop-types' class Input breidt Component {constructor (props) {super (props) this.state = {waarde: props.value? props.value: '', className: props.className? props.className: '', error: false}} // ... render () {const {handleError, ... opts} = this.props this.handleError = handleError return ()}} Input.propTypes = {naam : PropTypes.string, tijdelijke aanduiding: PropTypes.string, type: PropTypes.string, className: PropTypes.string, waarde: PropTypes.string, handleError: PropTypes.func} export standaard invoer

Zoals hierboven te zien is, wikkelen we de element in een door React bestuurde component om de status ervan te kunnen beheren en veldvalidatie uit te voeren.

React biedt een manier om de typen te valideren met PropTypes. In het bijzonder gebruiken we Input.propTypes = {…} om het type eigenschappen te valideren dat door de gebruiker is doorgegeven.

Let daar op PropType validatie werkt alleen voor ontwikkeling. PropType validatie is om te controleren of aan alle aannames die we over onze componenten maken, wordt voldaan.

Het is beter om het te hebben in plaats van verrast te worden door willekeurige hik in de productie.

4.2. Het formulier

Vervolgens bouwen we een generiek formulieronderdeel in het bestand Form.js die meerdere exemplaren van onze Invoer component waarop we ons inlogformulier kunnen baseren.

In de Het formulier component, nemen we attributen van HTML elementen en creëren Invoer componenten van hen.

Dan de Invoer componenten en validatiefoutmeldingen worden ingevoegd in de Het formulier:

import React, {Component} van 'react' import PropTypes van 'prop-types' import Input van './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 = {naam: PropTypes.string, actie: PropTypes.string, methode: PropTypes.string, invoer: PropTypes.array, fout: PropTypes.string} export standaardformulier

Laten we nu eens kijken hoe we veldvalidatiefouten en inlogfouten beheren:

class Form verlengt Component {constructor (props) {super (props) if (props.error) {this.state = {fout: 'verkeerde gebruikersnaam of wachtwoord!', errcount: 0}} anders {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}}))} anders {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) retourneren {errmsg}}} // ...}

In dit fragment definiëren we de handleError functie om de foutstatus van het formulier te beheren. Bedenk dat we het ook voor hebben gebruikt Invoer veldvalidatie. Werkelijk, handleError () wordt doorgegeven aan de Invoercomponenten als terugbellen in het render () functie.

We gebruiken renderError () om het foutberichtelement te construeren. Let daar op Formulier constructor verbruikt een fout eigendom. Deze eigenschap geeft aan of de inlogactie mislukt.

Dan komt de handler voor het indienen van formulieren:

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)}). dan (v => {if (v.redirected) window.location = v.url}) .catch (e => console.warn (e))}}}

We verpakken alle formuliervelden in FormData en stuur het naar de server met behulp van de halen API.

Laten we niet vergeten dat ons inlogformulier wordt geleverd met een successUrl en failureUrl, wat betekent dat ongeacht of het verzoek succesvol is of niet, het antwoord een omleiding vereist.

Daarom moeten we omleiding afhandelen in de terugroepactie.

4.3. Formulierweergave

Nu we alle componenten hebben ingesteld die we nodig hebben, kunnen we ze in de DOM blijven plaatsen. De basis HTML-structuur is als volgt (vind deze onder react / public / index.html):

Ten slotte zullen we het formulier in het met id 'container ' in reageer / src / index.js:

importeer React vanuit 'react' importeer ReactDOM vanuit 'react-dom' import './index.css' importeer Formulier van './Form' const inputs = [{naam: "gebruikersnaam", placeholder: "gebruikersnaam", type: " text "}, {naam:" wachtwoord ", placeholder:" wachtwoord ", type:" wachtwoord "}, {type:" submit ", waarde:" Submit ", className:" btn "}] const props = {naam: 'loginForm', methode: 'POST', action: '/ perform_login', inputs: inputs} const params = nieuwe URLSearchParams (window.location.search) ReactDOM.render (, document.getElementById ('container'))

Ons formulier bevat nu dus twee invoervelden: gebruikersnaam en wachtwoord, en een verzendknop.

Hier passeren we een extra fout attribuut aan de Het formulier component omdat we inlogfout willen afhandelen na omleiding naar de fout-URL: /index.html?error=waar.

Nu zijn we klaar met het bouwen van een Spring Security-inlogtoepassing met React. Het laatste dat we moeten doen, is rennen mvn compileren.

Tijdens het proces helpt de Maven-plug-in bij het bouwen van onze React-applicatie en het verzamelen van het build-resultaat in src / main / webapp / WEB-INF / view / react / build.

5. Conclusie

In dit artikel hebben we besproken hoe u een React-inlog-app kunt bouwen en deze kunt laten communiceren met een Spring Security-backend. Een complexere applicatie zou een toestandsovergang en routing met behulp van React Router of Redux met zich meebrengen, maar dat valt buiten het bestek van dit artikel.

Zoals altijd is de volledige implementatie te vinden op GitHub. Om het lokaal uit te voeren, voert u uit mvn steiger: run in de hoofdmap van het project, dan hebben we toegang tot de React-inlogpagina op // localhost: 8080.