Forårsikkerhedsside med React

1. Oversigt

React er et komponentbaseret JavaScript-bibliotek bygget af Facebook. Med React kan vi nemt oprette komplekse webapplikationer. I denne artikel vil vi få Spring Security til at arbejde sammen med en React Login-side.

Vi drager fordel af de eksisterende Spring Security-konfigurationer af tidligere eksempler. Så vi bygger oven på en tidligere artikel om oprettelse af et formular-login med Spring Security.

2. Opsæt Reager

Lad os først brug kommandolinjeværktøjet create-react-app til at oprette en applikation ved at udføre kommandoen “Opret-reager-app reagerer ”.

Vi har en konfiguration som følgende i reagere / pakke.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 skubber ud "}}

Så gør vi det brug frontend-maven-plugin til at hjælpe med at opbygge vores React-projekt med Maven:

 com.github.eirslett frontend-maven-plugin 1.6 v8.11.3 6.1.0 src / main / webapp / WEB-INF / view / react installeringsnode og npm install-node-og-npm npm install npm npm kør build npm kør build 

Den seneste version af pluginet kan findes her.

Når vi løber mvn kompilere, dette plugin downloades knude og npm, installer alle nodemodulafhængigheder og opret react-projektet for os.

Der er flere konfigurationsegenskaber, vi skal forklare her. Vi specificerede versionerne af knude og npm, så pluginet ved, hvilken version der skal downloades.

Vores React-login-side fungerer som en statisk side i foråret, så vi bruger “src / main /webapp/ WEB-INF / se / reagere" som npm'S arbejdsmappe.

3. Forårssikkerhedskonfiguration

Før vi dykker ned i React-komponenterne, opdaterer vi Spring-konfigurationen for at betjene de statiske ressourcer i vores React-app:

@EnableWebMvc @Configuration offentlig klasse MvcConfig udvider WebMvcConfigurerAdapter {@Override public void addResourceHandlers (ResourceHandlerRegistry registry) {registry.addResourceHandler ("/ static / **") .addResourceLocations ("/ WEB-INF / stat / / / / / / / ); 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"); }}

Bemærk, at vi tilføjer login-siden “Indeks.html” som en statisk ressource i stedet for en dynamisk serveret JSP.

Dernæst opdaterer vi Spring Security-konfigurationen for at give adgang til disse statiske ressourcer.

I stedet for at bruge “Login.jsp” som vi gjorde i den forrige form for login-artikel, her bruger vi “Indeks.html” som vores Log på side:

@Configuration @EnableWebSecurity @Profile ("! Https") offentlig klasse SecSecurityConfig udvider WebSecurityConfigurerAdapter {// ... @ Override-beskyttet ugyldig konfiguration (endelig HttpSecurity http) kaster undtagelse {http.csrf (). Deaktiver (). AuthorizeRequests () / / ... .antMatchers (HttpMethod.GET, "/ index *", "/ static / **", "/*.js", "/*.json", "/*.ico") .permitAll () .anyRequest (). godkendt () .and () .formLogin (). loginPage ("/ index.html") .loginProcessingUrl ("/ perform_login") .defaultSuccessUrl ("/ homepage.html", true) .failureUrl (" /index.html?error=true ") // ...}}

Som vi kan se fra uddraget ovenfor, når vi sender formulardata til “/ udfør_login“, Foråret vil omdirigere os til“/hjemmeside.html”Hvis legitimationsoplysningerne matcher med succes og til“/index.html?fejl = sandt" Ellers.

4. Reager komponenter

Lad os nu få vores hænder beskidte på React. Vi bygger og administrerer et formularlogin ved hjælp af komponenter.

Bemærk, at vi bruger ES6 (ECMAScript 2015) syntaks til at opbygge vores applikation.

4.1. Indgang

Lad os starte med en Indgang komponent, der bakker elementer i loginformularen i reager / src / Input.js:

import React, {Component} fra 'react' import PropTypes fra 'prop-types' klasse Input udvider komponent {constructor (rekvisitter) {super (rekvisitter) this.state = {værdi: 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, pladsholder: PropTypes.string, type: PropTypes.string, className: PropTypes.string, værdi: PropTypes.string, handleError: PropTypes.func} eksport standard Input

Som det ses ovenfor, pakker vi ind element i en React-kontrolleret komponent for at kunne styre dens tilstand og udføre feltvalidering.

React giver en måde at validere typerne ved hjælp af PropTypes. Specifikt bruger vi Input.propTypes = {…} for at validere typen af ​​egenskaber, der sendes ind af brugeren.

Noter det PropType validering fungerer kun til udvikling. PropType validering er at kontrollere, at alle de antagelser, vi gør om vores komponenter, bliver opfyldt.

Det er bedre at have det i stedet for at blive overrasket over tilfældige hikke i produktionen.

4.2. Form

Derefter bygger vi en generisk formkomponent i filen Form.js der kombinerer flere forekomster af vores Indgang komponent, som vi kan basere vores loginformular på.

I Form komponent, tager vi attributter af HTML elementer og skabe Indgang komponenter fra dem.

Derefter Indgang komponenter og valideringsfejlmeddelelser indsættes i Form:

import React, {Component} fra 'react' import PropTypes from 'prop-types' import Input from './Input' class Form extends Component {// ... render () {const inputs = this.props.inputs.map (({navn, pladsholder, type, værdi, className}, indeks) => ()) const-fejl = this.renderError () return ({this.form = fm}}> {input} {fejl})}} Formular .propTypes = {navn: PropTypes.string, handling: PropTypes.string, metode: PropTypes.string, input: PropTypes.array, fejl: PropTypes.string} eksport standardformular

Lad os nu se på, hvordan vi administrerer feltvalideringsfejl og loginfejl:

klasse Form udvider komponent {constructor (rekvisitter) {super (rekvisitter) hvis (props.error) {this.state = {fail: 'forkert brugernavn eller adgangskode!', errcount: 0}} andet {this.state = {errcount: 0}}} handleError = (field, errmsg) => {if (! Field) return hvis (errmsg) {this.setState ((prevState) => ({failure: '', errcount: prevState.errcount + 1, errmsgs) : {... prevState.errmsgs, [field]: errmsg}})} ellers {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) returnerer {errmsg}}} // ...}

I dette uddrag definerer vi handleError funktion til at styre formens fejltilstand. Husk at vi også brugte det til Indgang feltvalidering. Rent faktisk, handleError () overføres til Indgangskomponenter som tilbagekald i gengive () fungere.

Vi bruger renderError () at konstruere fejlmeddelelseselementet. Noter det Formularer konstruktøren bruger en fejl ejendom. Denne egenskab angiver, om loginhandlingen mislykkes.

Derefter kommer formularen til indsendelse af formular:

klasse Form udvider komponent {// ... 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)}). derefter (v => {if (v.omdirigeret) window.location = v.url}) .fangst (e => console.warn (e))}}}

Vi pakker alle formularfelter ind i FormData og send det til serveren ved hjælp af hente API.

Lad os ikke glemme, at vores loginformular leveres med en succesUrl og fejlUrl, hvilket betyder, at uanset om anmodningen er vellykket eller ej, vil svaret kræve en omdirigering.

Derfor er vi nødt til at håndtere omdirigering i tilbagekaldet.

4.3. Form gengivelse

Nu hvor vi har oprettet alle de komponenter, vi har brug for, kan vi fortsætte med at placere dem i DOM. Den grundlæggende HTML-struktur er som følger (find den under reager / offentlig / index.html):

Endelig gengiver vi formularen til med id “beholder" i reager / src / index.js:

import React from 'react' import ReactDOM from 'react-dom' import './index.css' import Form fra './Form' const inputs = [{name: "brugernavn", pladsholder: "brugernavn", type: " tekst "}, {name:" password ", placeholder:" password ", type:" password "}, {type:" submit ", value:" Submit ", className:" btn "}] const props = {name: 'loginForm', metode: 'POST', handling: '/ perform_login', indgange: input} const params = ny URLSearchParams (window.location.search) ReactDOM.render (, document.getElementById ('container'))

Så vores formular indeholder nu to inputfelter: brugernavn og adgangskodeog en send-knap.

Her passerer vi en ekstra fejl attribut til Form komponent, fordi vi vil håndtere loginfejl efter omdirigering til fejl URL: /index.html?fejl = sandt.

Nu er vi færdige med at oprette en Spring Security login-applikation ved hjælp af React. Den sidste ting, vi skal gøre, er at løbe mvn kompilere.

Under processen hjælper Maven-pluginet med at opbygge vores React-applikation og samle build-resultatet i src / main / webapp / WEB-INF / view / react / build.

5. Konklusion

I denne artikel har vi dækket, hvordan man opbygger en React login-app og lader den interagere med en Spring Security-backend. En mere kompleks applikation ville involvere tilstandsovergang og routing ved hjælp af React Router eller Redux, men det ville være uden for denne artikels anvendelsesområde.

Som altid kan den fulde implementering findes på GitHub. For at køre det lokalt skal du udføre mvn anløbsbro: kør i projektets rodmappe, så kan vi få adgang til siden React login // localhost: 8080.