Управление идентификацией здания, включая аутентификацию и авторизацию? Попробуйте Stormpath! Наш REST API и надежная поддержка Java SDK могут устранить риск для безопасности и могут быть реализованы за считанные минуты. Зарегистрируйтесь и никогда больше не создавайте аутентификацию!
React — одна из самых популярных библиотек для создания веб-приложений. С Spring Boot проще, чем когда-либо, создать серверную часть CRUD для вашего React-приложения . В этом руководстве мы свяжем их вместе, а затем используем Stormpath для добавления протоколов аутентификации и авторизации.
Мы начнем с создания статического представления данных с использованием React. Затем мы создадим REST-сервер с Spring Boot, свяжем его и добавим безопасность пользователя с помощью Stormpath. Все должно быть просто, даже если вы никогда раньше не использовали React.
Исходный код, который поддерживает этот пост, можно найти в этом репозитории GitHub .
Служить фронту
Обычно приложения React обслуживаются с использованием Node.js, но если вы являетесь разработчиком Java, вам, вероятно, будет очень удобно использовать этот подход Spring Boot.
Сначала вы поместите все приложение в один файл index.html
. Чтобы указать Spring Boot, чтобы он служил в качестве домашней страницы, вы можете использовать аннотацию @Controller
.
1
2
3
4
5
6
7
8
|
@Controller public class HomeController { @RequestMapping (value = "/" ) public String index() { return "index.html" ; } } |
Создайте пустой каталог и поместите приведенный выше код в src/main/java/tutorial/HomeController.java
. Spring Boot будет искать src/main/resources/static/index.html
при загрузке сайта.
1
2
3
4
5
6
7
8
|
<! DOCTYPE html> < html > < head > < title >React + Spring</ title > </ head > < body > </ body > </ html > |
Создайте pom.xml
и класс приложения Spring Boot. Используйте следующее для вашего POM.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
< project > < modelVersion >4.0.0</ modelVersion > < groupId >com.example</ groupId > < artifactId >demo</ artifactId > < version >0.0.1-SNAPSHOT</ version > < parent > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-parent</ artifactId > < version >1.4.1.RELEASE</ version > </ parent > < dependencies > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-web</ artifactId > </ dependency > </ dependencies > < build > < plugins > < plugin > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-maven-plugin</ artifactId > </ plugin > </ plugins > </ build > </ project > |
Поместите следующее в src/main/java/tutorial/Application.java
.
1
2
3
4
5
6
7
|
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application. class , args); } } |
Когда вы запускаете сервер с помощью mvn spring-boot:run
и посещаете localhost:8080
вы должны увидеть пустую страницу с заголовком «React + Spring».
Удалить перезагрузки
Обычно вам придется перезагружать сервер каждый раз, когда вы вносите изменения в интерфейс, что является проблемой. Использование инструментов разработчика Spring Boot позволяет нам обойти это. Добавьте следующую зависимость к вашему POM.
1
2
3
4
5
|
< dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-devtools</ artifactId > < optional >true</ optional > </ dependency > |
Также добавьте эту конфигурацию в ваш плагин Spring Boot Maven:
1
2
3
|
< configuration > < addResources >true</ addResources > </ configuration > |
Теперь, когда вы вносите изменения в свое приложение или перекомпилируете какие-либо классы, оно должно обновляться при обновлении браузера.
Реагировать на Barebones HTML
Реагировать! Самая простая страница React имеет три вещи: корневой элемент, импорт JavaScript и тег скрипта.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
<! DOCTYPE html> < html > < head > < title >React + Spring</ title > </ head > < body > < div id = 'root' ></ div > < script type = "text/babel" ></ script > </ body > </ html > |
Корневой элемент — это место, где React вставит наш HTML-код. При импорте используются три библиотеки — две для самого React, а другая для перевода нашего шаблона представления с помощью babel .
Примечание: для удобства тестирования мы загружаем библиотеки с использованием CDN, но обычно вы используете что-то вроде веб-пакета, чтобы объединить весь ваш Javascript в один файл.
Теперь поместите ваш код React внутри тега script.
Реагировать Основы
Как описано в уроке мышления в реакции , вы должны начать кодировать свое приложение, разбив интерфейс на components
.
1
2
3
4
|
< script type = "text/babel" > var Employee = React.createClass({}); var EmployeeTable = React.createClass({}); </ script > |
Здесь вы создали два — один для таблицы сотрудников, а другой для записи сотрудников. Затем каждому компоненту нужна функция рендеринга, которая описывает HTML для генерации.
01
02
03
04
05
06
07
08
09
10
11
12
|
< script type = "text/babel" > var Employee = React.createClass({ render: function() { return (< div >employee</ div >); } }); var EmployeeTable = React.createClass({ render: function() { return (< div >employee table</ div >); } }); </ script > |
Здесь приходит компилятор Babel для преобразования HTML-кода в правильные операторы React. Обратите внимание, как теги div
возвращаются из оператора render
.
Вы должны указать React вставить HTML родительского компонента в корневой элемент. Это делается с ReactDOM.render
метода ReactDOM.render
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
< script type = "text/babel" > var Employee = React.createClass({ render: function() { return (< div >employee</ div >); } }); var EmployeeTable = React.createClass({ render: function() { return (< div >employee table</ div >); } }); ReactDOM.render( < EmployeeTable />, document.getElementById('root') ); </ script > |
Обновив браузер, вы увидите текстовый элемент, который вы создали.
Чтобы увидеть HTML React, вставленный в корневой элемент, вы можете использовать инспектор браузера (Ctrl-Shift-J в Chrome).
Связывание компонентов вместе
Теперь, когда у вас есть компоненты, давайте свяжем их вместе. Вы можете начать с попытки рендеринга жестко закодированных данных; Вы будете использовать сервер REST позже.
Над командой ReactDOM
введите следующее:
1
2
3
4
5
6
|
var EMPLOYEES = [ {name: 'Joe Biden' , age: 45, years: 5}, {name: 'President Obama' , age: 54, years: 8}, {name: 'Crystal Mac' , age: 34, years: 12}, {name: 'James Henry' , age: 33, years: 2} ]; |
Затем добавьте employees={EMPLOYEES}
когда вы создаете таблицу.
1
2
3
|
ReactDOM.render( <EmployeeTable employees={EMPLOYEES} />, document.getElementById( 'root' ) ); |
Как и следовало ожидать, это передает данные в переменную с именем employees
. Внутри EmployeeTable вы можете получить к нему доступ, используя this.props
. Давайте использовать это для создания таблицы со строкой для каждого сотрудника.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
var EmployeeTable = React.createClass({ render: function () { var rows = []; this .props.employees.forEach( function (employee) { rows.push(<Employee employee={employee} />); }); return ( <table> <thead> <tr> <th>Name</th><th>Age</th><th>Years</th> </tr> </thead> <tbody>{rows}</tbody> </table>); } }); |
Это создает новый класс Employee для каждого элемента данных (устанавливает атрибут employee
) и помещает его в массив. Затем {rows}
добавляет необходимый HTML-код из дочернего класса.
Теперь все, что вам нужно сделать, это обновить метод рендеринга на Employee.
01
02
03
04
05
06
07
08
09
10
|
var Employee = React.createClass({ render: function () { return ( <tr> <td>{ this .props.employee.name}</td> <td>{ this .props.employee.age}</td> <td>{ this .props.employee.years}</td> </tr>); } }); |
Вы можете добавить Bootstrap, чтобы стол выглядел красиво. Добавьте следующее чуть ниже тегов импорта скрипта:
1
|
<link rel= "stylesheet" href= "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" > |
Затем окружите основную таблицу контейнером div и присвойте элементу таблицы имена классов Bootstrap.
01
02
03
04
05
06
07
08
09
10
11
12
|
< div className = "container" > < table className = "table table-striped" > < thead > < tr > < th >Name</ th > < th >Age</ th > < th >Years</ th > </ tr > </ thead > < tbody >{rows}</ tbody > </ table > </ div > |
Обновление вашего браузера должно дать хороший обзор данных, которые вы жестко запрограммировали!
Добавление реальных данных
Чтобы использовать объекты данных, поступающие с сервера, вам необходимо добавить сервер! Делать это с Spring Boot очень просто. Внутри src/main/java/tutorial/Employee.java
добавьте следующий код:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
@Data @Entity public class Employee { private @Id @GeneratedValue Long id; private String name; private int age; private int years; private Employee() {} public Employee(String name, int age, int years) { this .name = name; this .age = age; this .years = years; } } |
Это наш боб. Примечание: аннотация @Data
из проекта Lombok .
Теперь создайте репозиторий, используя Spring Data JPA.
1
|
public interface EmployeeRepository extends CrudRepository<Employee, Long> {} |
Чтобы загрузить данные, создайте реализацию CommandLineRunner
которая использует репозиторий для создания новых записей в базе данных.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
@Component public class DatabaseLoader implements CommandLineRunner { private final EmployeeRepository repository; @Autowired public DatabaseLoader(EmployeeRepository repository) { this .repository = repository; } @Override public void run(String... strings) throws Exception { this .repository.save( new Employee( "Joe Biden" , 45 , 5 )); this .repository.save( new Employee( "President Obama" , 54 , 8 )); this .repository.save( new Employee( "Crystal Mac" , 34 , 12 )); this .repository.save( new Employee( "James Henry" , 33 , 2 )); } } |
Единственное, что осталось — это вытащить зависимости. Добавление следующего в ваш pom.xml
позволит вашему репозиторию стать конечной точкой REST.
1
2
3
4
5
6
7
8
|
< dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-data-rest</ artifactId > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-data-jpa</ artifactId > </ dependency > |
Вам также необходимо включить Project Lombok (который позволяет игнорировать создание методов получения и установки для ваших компонентов).
1
2
3
4
5
6
|
< dependency > < groupId >org.projectlombok</ groupId > < artifactId >lombok</ artifactId > < version >1.16.10</ version > < scope >provided</ scope > </ dependency > |
И вам нужна база данных (которую настраивает Spring Boot). Вы можете использовать H2, который встроен (то есть в память / не будет перезагружаться).
1
2
3
4
|
< dependency > < groupId >com.h2database</ groupId > < artifactId >h2</ artifactId > </ dependency > |
Вот и все! Если вы перезагрузитесь сейчас, у вас будет работающий REST-сервер с данными.
Сопоставление URL
Если вы добавите следующее в src/main/resources/application.properties
тогда все ваши вызовы конечной точки REST будут на localhost:8080/api
1
|
spring.data.rest.basePath=/api |
Вызов localhost:8080/api/employees
из командной строки должен выдать список загруженных вами данных.
Реагировать и ОТДЫХАТЬ
Теперь вам нужно вытащить данные в представление React из конечной точки REST. Вы можете сделать это с помощью jQuery. Добавьте следующий импорт в ваш HTML:
1
|
|
Теперь создайте класс-оболочку, который возвращает EmployeeTable
в своем методе render.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
var App = React.createClass({ loadEmployeesFromServer: function () { var self = this ; $.ajax({ }).then( function (data) { self.setState({employees: data._embedded.employees}); }); }, getInitialState: function () { return {employees: []}; }, componentDidMount: function () { this .loadEmployeesFromServer(); }, render() { return ( <EmployeeTable employees={ this .state.employees}/> ); } }); |
Вы должны сначала установить state
, используя getInitialState
для инициализации, а затем componentDidMount
чтобы делать то, что нужно, когда все загружено.
Теперь замените основной ReactDOM.render
вашим новым классом.
1
|
ReactDOM.render(<App />, document.getElementById( 'root' ) ); |
При обновлении вы должны увидеть то же представление, что и раньше, за исключением того, что теперь данные загружаются с сервера.
интерактивность
Последнее, что вы хотите для своего интерфейса, это интерактивность. Давайте добавим кнопку delete
, чтобы увидеть, как это может работать.
Добавьте следующий столбец к вашему сотруднику визуализации.
1
2
3
|
< td > < button className = "btn btn-info" onClick={this.handleDelete}>Delete</ button > </ td > |
Вы напишите метод handleDelete
через секунду. После добавления еще одного заголовка в класс таблицы сотрудников рядом с каждой записью должны появиться кнопки.
Удаление с сервера
Прежде чем отправлять запросы на удаление в серверную часть, рекомендуется добавить уведомления. Для этого вы можете использовать Toastr, который позволит вам показывать всплывающие окна. Включите следующее в верхней части вашего HTML:
1
2
|
< link rel = "stylesheet" href = "https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.css" > |
Теперь в вашем скрипте вы можете отправлять сообщения с такими командами, как toastr.error('something went wrong')
.
Давайте проверим это! Измените свой класс сотрудника на следующее:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
var Employee = React.createClass({ getInitialState: function () { return {display: true }; }, handleDelete() { var self = this ; $.ajax({ url: self.props.employee._links.self.href, type: 'DELETE' , success: function (result) { self.setState({display: false }); }, error: function (xhr, ajaxOptions, thrownError) { toastr.error(xhr.responseJSON.message); } }); }, render: function () { if ( this .state.display== false ) return null ; else return ( <tr> <td>{ this .props.employee.name}</td> <td>{ this .props.employee.age}</td> <td>{ this .props.employee.years}</td> <td> <button className= "btn btn-info" onClick={ this .handleDelete}>Delete</button> </td> </tr> ); } }); |
Это устанавливает состояние display
которое определяет, следует ли отображать или нет. Если сотрудник успешно удален, эта переменная имеет значение true. Метод handleDelete
отправляет запрос на удаление на сервер (используя ссылку, полученную из запроса get). В случае успеха display
устанавливается на false, а рендер обновляется. В противном случае Toastr уведомляет пользователя о возникновении ошибки.
Попробуйте удалить запись и обновить страницу. Это должно остаться удаленным.
Примечание. Перезапуск сервера вернет те же данные, поскольку вы используете базу данных в памяти.
Добавить аутентификацию пользователя
Давайте добавим одну заключительную функцию в наше приложение React, Stormpath для аутентификации пользователей. Вам понадобится бесплатный аккаунт разработчика в Stormpath .
Первое, что вам нужно сделать, это поместить данные вашего приложения Stormpath в ваш файл application.properties
.
1
2
3
|
stormpath.application.href = <your app href> stormpath.client.apiKey. id = <your api key id > stormpath.client.apiKey.secret = <your api key secret> |
Примечание. Из соображений безопасности не следует хранить ключи Stormpath внутри файлов проекта. Скорее используйте переменные среды. Смотрите здесь
Затем добавьте стартер Stormpath в зависимости от Maven.
1
2
3
4
5
|
< dependency > < groupId >com.stormpath.spring</ groupId > < artifactId >stormpath-default-spring-boot-starter</ artifactId > < version >1.1.2</ version > </ dependency > |
Вам также необходимо переместить файл index.html
в src/main/resources/templates
Это связано с тем, что стартер Spring Boot в Stormpath по умолчанию использует библиотеку шаблонов Thymeleaf. Измените HomeController
на возвращаемый index
.
1
2
3
4
5
6
7
8
|
@Controller public class HomeController { @RequestMapping (value = "/" ) public String index() { return "index" ; } } |
Вам также необходимо переместить код React в отдельный файл. Это потому, что Thymeleaf не понравятся некоторые персонажи. Переместите код из тега script в src/main/webapp/public/app.js
Эта папка открыта для общего доступа по умолчанию. Затем импортируйте этот скрипт внизу вашего HTML.
1
|
< script type = "text/babel" src = "/public/app.js" ></ script > |
Затем создайте адаптер безопасности, вызовы которого применяются к stormpath()
.
1
2
3
4
5
6
7
|
@Configuration public class Security extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.apply(stormpath()); } } |
Теперь, когда вы перезагрузите свой сервер и попытаетесь перейти на домашнюю страницу, вам будет предложено ввести страницу входа.
При вводе данных, прилагаемых к приложению Stormpath, которое вы вводите в application.properties, вы должны перейти на страницу просмотра данных, как и раньше.
Выйти
Вы также должны быть в состоянии выйти из системы. Это так же просто, как добавить форму, которая отправляет сообщение в /logout
(который Stormpath устанавливает по умолчанию).
1
2
3
4
5
6
|
< div class = 'container' > < div id = 'root' ></ div > < form action = "/logout" method = "post" > < input class = "btn btn-danger center-block" type = "submit" value = "Logout" /> </ form > </ div > |
Вы можете окружить как корневой элемент React, так и форму контейнером Bootstrap для лучшего выравнивания.
Нажав кнопку выхода, вы вернетесь к экрану входа в систему, как и раньше.
Настройка авторизации
Наконец, вам нужно разрешить только пользователям с правильным доступом удалять сотрудников. Для блокировки вы можете использовать аннотацию PreAuthorize
Spring Security. Измените код хранилища на следующий:
1
2
3
4
5
6
|
public interface EmployeeRepository extends CrudRepository<Employee, Long> { @PreAuthorize ( "hasAuthority('ROLE_ADMIN')" ) @Override void delete(Long aLong); } |
Теперь только пользователи с полномочиями ROLE_ADMIN смогут удалять. Если вы перезагрузите сервер и попытаетесь нажать «Удалить», вы получите сообщение «Доступ запрещен».
Чтобы предоставить пользователю необходимые права, вам нужно добавить их в группу Stormpath через консоль администратора.
В этом примере есть группа Supervisor, прикрепленная к соответствующему приложению. Для интеграции с этой группой вам просто нужно заменить строку ROLE_ADMIN на HREF группы и перезапустить. Если пользователь, вошедший в систему, является членом группы Supervisor (см. Учетные записи), вам следует разрешить удалить.
Сделано и вычищено
Точно так же вы создали совместимое веб-приложение CRUD с авторизацией и React в качестве внешнего интерфейса. Надеюсь, вы нашли этот урок полезным! Если у вас есть какие-либо вопросы по интеграции React, Spring Boot и Stormpath, пожалуйста, оставьте комментарий.
Чтобы увидеть более полное приложение React с использованием бэкэнда Spring Boot, см. React.js и Spring Data REST .
Управление идентификацией здания, включая аутентификацию и авторизацию? Попробуйте Stormpath! Наш REST API и надежная поддержка Java SDK могут устранить риск для безопасности и могут быть реализованы за считанные минуты. Зарегистрируйтесь и никогда больше не создавайте аутентификацию!