Reactを使用したSpring Securityログインページ
1. 概要
Reactは、Facebookによって構築されたコンポーネントベースのJavaScriptライブラリです。 Reactを使用すると、複雑なWebアプリケーションを簡単に構築できます。 この記事では、SpringSecurityをReactログインページと連携させます。
前の例の既存のSpringSecurity構成を利用します。 したがって、Form Login with Spring Securityの作成に関する以前の記事に基づいて構築します。
2. Reactをセットアップする
まず、コマンド「create-react-app react”」を実行してuse the command-line tool create-react-app to create an applicationを実行しましょう。
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"
}
}
次に、use 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
プラグインの最新バージョンはhereにあります。
mvn compileを実行すると、このプラグインはnodeとnpmをダウンロードし、すべてのノードモジュールの依存関係をインストールして、reactプロジェクトをビルドします。
ここで説明する必要がある構成プロパティがいくつかあります。 プラグインがダウンロードするバージョンを認識できるように、nodeとnpmのバージョンを指定しました。
ReactログインページはSpringでは静的ページとして機能するため、npmの作業ディレクトリとして「src/main/webapp_ / WEB-INF / view / react_」を使用します。
3. Spring Securityの構成
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");
}
}
動的に提供されるJSPの代わりにNote that we add the login page “index.html” as a static resource。
次に、Spring Security構成を更新して、これらの静的リソースへのアクセスを許可します。
the previous form loginの記事で使用したように“login.jsp”を使用する代わりに、ここではLoginページとして“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. 反応コンポーネント
それでは、Reactで手を汚しましょう。 コンポーネントを使用してフォームログインを構築および管理します。
ES6(ECMAScript 2015)構文を使用してアプリケーションを構築することに注意してください。
4.1. 入力
ログインフォームの<input />要素をreact/src/Input.jsでバックアップするInputコンポーネントから始めましょう。
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
上記のように、<input />要素をReact制御コンポーネントにラップして、その状態を管理し、フィールド検証を実行できるようにします。
Reactは、PropTypesを使用して型を検証する方法を提供します。 具体的には、Input.propTypes = \{…}を使用して、ユーザーから渡されたプロパティのタイプを検証します。
PropType検証は開発でのみ機能することに注意してください。 PropTypeの検証は、コンポーネントに関して行っているすべての仮定が満たされていることを確認することです。
制作中のランダムなしゃっくりに驚かされるよりも、持っている方が良いです。
4.2. Form
次に、ログインフォームのベースとなるInputコンポーネントの複数のインスタンスを組み合わせた汎用フォームコンポーネントをファイルForm.jsに作成します。
Formコンポーネントでは、HTML<input/>要素の属性を取得し、それらからInputコンポーネントを作成します。
次に、Inputコンポーネントと検証エラーメッセージがForm:に挿入されます
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
次に、フィールド検証エラーとログインエラーを管理する方法を見てみましょう。
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関数を定義します。 Inputのフィールド検証にも使用したことを思い出してください。 Actually, handleError() is passed to the Input Components as a callback in the render() function。
renderError()を使用してエラーメッセージ要素を作成します。 Form’sコンストラクターは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))
}
}
}
すべてのフォームフィールドをFormDataにラップし、fetch APIを使用してサーバーに送信します。
ログインフォームにはsuccessUrlとfailureUrlが付いていることを忘れないでください。つまり、リクエストが成功したかどうかに関係なく、レスポンスにはリダイレクトが必要になります。
そのため、応答コールバックでリダイレクトを処理する必要があります。
4.3. フォームのレンダリング
必要なすべてのコンポーネントをセットアップしたので、引き続きそれらをDOMに配置できます。 基本的なHTML構造は次のとおりです(react/public/index.htmlの下にあります)。
最後に、フォームをreact/src/index.jsのID「container”」で<div/>にレンダリングします。
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'))
そのため、フォームには、usernameとpasswordの2つの入力フィールドと、送信ボタンが含まれています。
ここでは、失敗したURL/index.html?error=trueにリダイレクトした後のログインエラーを処理するため、追加のerror属性をFormコンポーネントに渡します。
これで、Reactを使用したSpringSecurityログインアプリケーションの構築が完了しました。 最後に行う必要があるのは、mvn compileを実行することです。
プロセス中、MavenプラグインはReactアプリケーションのビルドを支援し、ビルド結果をsrc/main/webapp/WEB-INF/view/react/buildで収集します。
5. 結論
この記事では、Reactログインアプリを構築し、SpringSecurityバックエンドと相互作用させる方法について説明しました。 より複雑なアプリケーションには、React RouterまたはReduxを使用した状態遷移とルーティングが含まれますが、それはこの記事の範囲を超えています。
いつものように、完全な実装はover on Githubで見つけることができます。 ローカルで実行するには、プロジェクトのルートフォルダーでmvn jetty:runを実行すると、http://localhost:8080でReactログインページにアクセスできます。