Рубрики
Без рубрики

Страница входа в систему Spring Security с React

Узнайте, как реализовать страницу входа в систему с помощью React, которая взаимодействует с серверной частью Spring Security.

Автор оригинала: Beau Tian.

1. Обзор

React -это библиотека JavaScript на основе компонентов, созданная Facebook. С помощью React мы можем легко создавать сложные веб-приложения. В этой статье мы собираемся заставить Spring Security работать вместе со страницей входа в систему React.

Мы воспользуемся преимуществами существующих конфигураций безопасности Spring в предыдущих примерах. Поэтому мы будем опираться на предыдущую статью о создании входа в форму с помощью Spring Security .

2. Настройка React

Во-первых, давайте используем инструмент командной строки create-react-app для создания приложения , выполнив команду ” create-react-app react” .

У нас будет конфигурация, подобная следующей в 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"
    }
}

Затем мы используем frontend-maven-плагин , чтобы помочь построить наш проект React с помощью 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
            
        
    

Последнюю версию плагина можно найти здесь .

Когда мы запустим mvn compile , этот плагин загрузит node и npm , установит все зависимости модулей узлов и построит для нас проект react.

Здесь мы должны объяснить несколько свойств конфигурации. Мы указали версии node и npm , чтобы плагин знал, какую версию загружать.

Наша страница входа в систему React будет служить статической страницей весной, поэтому мы используем ” src/main/|/webapp /WEB-INF/view/react ” в качестве рабочего каталога npm .

3. Конфигурация безопасности Пружины

Прежде чем мы погрузимся в компоненты React, мы обновим конфигурацию Spring, чтобы обслуживать статические ресурсы нашего приложения React:

@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");
    }
}

Обратите внимание, что мы добавляем страницу входа в систему “index.html” как статический ресурс вместо динамически обслуживаемого JSP.

Затем мы обновим конфигурацию безопасности Spring, чтобы разрешить доступ к этим статическим ресурсам.

Вместо использования “login.jsp” , как мы делали в предыдущей форме входа статьи, здесь мы используем “index.html” как наша Логин страница:

@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")
          //...
    }
}

Как мы видим из приведенного выше фрагмента, когда мы отправляем данные формы в ” /perform_login “, Spring перенаправит нас на ” /homepage.html “если учетные данные совпадают успешно и” /index.html?error=true ” в противном случае.

4. Реагирующие компоненты

А теперь давайте запачкаем руки. Мы создадим и будем управлять входом в форму с помощью компонентов.

Обратите внимание, что для создания нашего приложения мы будем использовать синтаксис ES6 (ECMAScript 2015).

4.1. Ввод

Давайте начнем с компонента Input , который поддерживает элементы /> формы входа в 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

Как было показано выше, мы помещаем элемент /> в компонент, управляемый реактором, чтобы иметь возможность управлять его состоянием и выполнять проверку в полевых условиях./>

React предоставляет способ проверки типов с помощью PropTypes . В частности, мы используем Input.prototypes = {…} для проверки типа свойств, переданных пользователем.

Обратите внимание, что Проптип валидация работает только для разработки. Проптип проверка заключается в проверке того, что все предположения, которые мы делаем о наших компонентах, выполняются.

Лучше иметь его, чем удивляться случайным сбоям в производстве.

4.2. Форма

Далее мы создадим общий компонент формы в файле Form.js , который объединяет несколько экземпляров нашего компонента Input , на котором мы можем основать нашу форму входа в систему.

В компоненте Form мы берем атрибуты элементов HTML и создаем Ввод компонентов из них.

Затем компоненты Input и сообщения об ошибках проверки вставляются в форму :

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

Теперь давайте посмотрим, как мы управляем ошибками проверки полей и ошибками входа в систему:

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}
} } //... }

В этом фрагменте мы определяем функцию HandleError для управления состоянием ошибки формы. Напомним, что мы также использовали его для проверки Ввода поля. На самом деле HandleError() передается компонентам ввода в качестве обратного вызова в функции render () .

Мы используем render Error() для построения элемента сообщения об ошибке. Обратите внимание, что конструктор формы использует свойство error . Это свойство указывает, не удалось ли выполнить действие входа в систему.

Затем идет обработчик отправки формы:

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))
        }
    }
}

Мы упаковываем все поля формы в Данные формы и отправляем их на сервер с помощью fetch API .

Давайте не будем забывать , что наша форма входа в систему поставляется с URL-адресом successful и failure , что означает, что независимо от того, будет ли запрос успешным или нет, ответ потребует перенаправления.

Вот почему нам нужно обрабатывать перенаправление при обратном вызове ответа.

4.3. Визуализация формы

Теперь, когда мы настроили все необходимые компоненты, мы можем продолжать помещать их в DOM. Базовая структура HTML выглядит следующим образом (найдите ее в разделе react/public/index.html ):




  
    
  
  

    

Наконец, мы визуализируем форму в с идентификатором ” контейнер” в 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'))

Таким образом , наша форма теперь содержит два поля ввода: имя пользователя и пароль , а также кнопку отправки.

Здесь мы передаем дополнительный атрибут error компоненту Form , потому что мы хотим обработать ошибку входа в систему после перенаправления на URL-адрес сбоя: /index.html?error=true .

ошибка входа в форму