Angular существует уже довольно давно. Angular JS, оригинальная версия, была одной из первых JavaScript MVC-фреймворков, которая доминировала в веб-ландшафте и приобрела сильную базу поклонников разработчиков в начале 2010-х годов. Это все еще идет хорошо, если недавний выпуск Angular 9 является каким-либо показателем!
Angular 9 идеально сочетается с Spring Boot, одной из самых популярных сред разработки приложений Java и REST API. Spring Boot произвел революцию в том, как Java-разработчики пишут приложения Spring, представив предварительно сконфигурированные стартеры с интеллектуальными настройками по умолчанию.
Читайте дальше, чтобы узнать, как вы можете использовать этот динамический дуэт для создания приложения CRUD.
Что нового в Angular 9?
Наиболее заметная новая особенность Angular 9 — Ivy. Айви — новый компилятор и рендеринг Angular. Рендерер — это механизм, который берет ваши компоненты и шаблоны и переводит их в инструкции для управления DOM. Плющ является внутренним компонентом, поэтому вы не взаимодействуете с ним напрямую. Тем не менее, это может оказать значительное влияние на ваш код, приводя к гораздо меньшим пакетам JavaScript и повышению производительности.
Проще говоря: обновление до Angular 9 сделает ваши веб-приложения быстрее!
Другие заметные особенности в Angular 9 включают в себя:
- Улучшения в Angular CLI для поддержки интернационализации (i18n).
- Типобезопасные улучшения
TestBed
для модульного тестирования.
Чтобы использовать функцию Angular CLI i18n, вам просто нужно добавить i18n
атрибут к вашим текстовым элементам, а затем запустить, ng xi18n
чтобы извлечь локализуемый текст в файл.
TestBed.inject(Component)
это новый способ получения ссылок на компоненты в тестах. TestBed.get(Component)
не рекомендуется.
Что нового в Spring Boot 2.2?
Spring Boot 2.2 был выпущен в сентябре 2019 года и фокусируется на повышении производительности и уменьшении использования памяти. Он добавляет поддержку Java 13, поддержку RSocket и возможность группировки индикаторов состояния. Индикаторы группировки могут быть полезны, если вы развертываете в Kubernetes и хотите использовать разные группы для тестов «живучести» и «готовности».
В этой статье я покажу вам, как создать приложение CRUD с Angular 9 и Spring Boot 2.2. Попутно я приложу все усилия, чтобы использовать советы по безопасности и способы повышения безопасности ваших приложений.
Предпосылки:
- Узел 12+.
- Java 11+.
- Okta аккаунт разработчика .
Чтобы установить Node и Java в подсистеме Mac, Linux или Windows для Linux (WSL), вы можете использовать Homebrew .
Оболочка
1
brew install node
2
brew tap AdoptOpenJDK/openjdk
3
brew cask install adoptopenjdk11
Вы также можете использовать SDKMAN! установить Java 11.
Оболочка
xxxxxxxxxx
1
sdk install java 11.0.5.hs-adpt
Вы можете обратиться к оглавлению ниже для шагов в этом руководстве.
Создать приложение Angular 9
Чтобы создать приложение Angular 9, сначала необходимо установить Angular CLI.
Оболочка
xxxxxxxxxx
1
npm install -g @angular/cli@9.0.2
Затем создайте каталог на angular-spring-boot
. Откройте окно терминала и перейдите в этот каталог. Запустите, ng new
чтобы создать приложение Angular.
Оболочка
xxxxxxxxxx
1
ng new notes --routing --style css
Этот процесс займет минуту или две, в зависимости от скорости вашего интернета и аппаратного обеспечения. По завершении перейдите в каталог и запустите ng serve
.
Оболочка
xxxxxxxxxx
1
ng serve
Откройте браузер http://localhost:4200
, и вы увидите домашнюю страницу по умолчанию.
Остановите ng serve
процесс, используя Ctrl+ Cв вашем терминале.
Вам также может понравиться:
Spring Boot Security + JWT » Hello World » Пример .
Добавить угловую аутентификацию с использованием OpenID Connect
OpenID Connect (OIDC) позже идентифицируется на основе спецификации OAuth 2.0. Он использует веб-токены JSON (JWT) для предоставления токена идентификатора и других функций, таких как обнаружение и /userinfo
конечная точка.
Okta имеет API аутентификации и управления пользователями, которые сокращают время разработки благодаря мгновенной масштабируемой пользовательской инфраструктуре. Интуитивно понятный API и экспертная поддержка Okta облегчают разработчикам аутентификацию, управление и защиту пользователей + роли в любом приложении.
Чтобы добавить поддержку входа в OIDC в ваше приложение Angular 9, вам сначала потребуется бесплатная учетная запись разработчика Okta. После того как вы создали свою учетную запись и выполнили вход в свою панель инструментов Okta, создайте новое приложение OIDC, выполнив следующие действия:
1. Перейдите в Приложения > Добавить приложение .
2. Выберите Одностраничное приложение и нажмите Далее .
3. Введите имя (например, Angular 9
) и измените URI перенаправления входа наhttp://localhost:4200/implicit/callback
4. Нажмите Готово .
Настройки вашего приложения должны выглядеть следующим образом.
Внизу экрана вы увидите идентификатор клиента вашего приложения. Вы также можете заметить, что выбрано Использовать PKCE . Этот параметр обеспечивает максимальный уровень безопасности, который вы можете иметь в настоящее время для одностраничных приложений при использовании OIDC для аутентификации.
Примечание . Чтобы узнать больше о PKCE (произносится как «pixy»), см. « Реализация кода авторизации OAuth 2.0 с помощью PKCE Flow» .
Скопируйте свой идентификатор клиента и URI издателя (из API > Серверы авторизации ) в следующую команду.
Оболочка
xxxxxxxxxx
1
ng add @oktadev/schematics --issuer=$issuer --clientId=$clientId
Эта команда добавляет Angta SDK от Okta и настраивает аутентификацию OIDC для вашего приложения.
Он создает home.component.ts
логику аутентификации, а также шаблон, отображающий кнопки входа и выхода.
ЦСИ / приложение / дома / home.component.ts
Машинопись
xxxxxxxxxx
1
import { Component, OnInit } from '@angular/core';
2
import { OktaAuthService } from '@okta/okta-angular';
3
@Component({
5
selector: 'app-home',
6
templateUrl: './home.component.html',
7
styleUrls: ['./home.component.css']
8
})
9
export class HomeComponent implements OnInit {
10
isAuthenticated: boolean;
11
constructor(public oktaAuth: OktaAuthService) {
13
}
14
async ngOnInit() {
16
this.isAuthenticated = await this.oktaAuth.isAuthenticated();
17
// Subscribe to authentication state changes
18
this.oktaAuth.$authenticationState.subscribe(
19
(isAuthenticated: boolean) => this.isAuthenticated = isAuthenticated
20
);
21
}
22
}
ЦСИ / приложение / дома / home.component.html
HTML
xxxxxxxxxx
1
<div>
2
<button *ngIf="!isAuthenticated" (click)="oktaAuth.loginRedirect()">Login</button>
3
<button *ngIf="isAuthenticated" (click)="oktaAuth.logout()">Logout</button>
4
</div>
Также есть HttpInterceptor
созданный для добавления токена доступа к исходящим HTTP-запросам.
SRC / приложение / общий / окт / auth.interceptor.ts
Машинопись
xxxxxxxxxx
1
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
2
import { Observable, from } from 'rxjs';
3
import { OktaAuthService } from '@okta/okta-angular';
4
import { Injectable } from '@angular/core';
5
@Injectable()
7
export class AuthInterceptor implements HttpInterceptor {
8
constructor(private oktaAuth: OktaAuthService) {
10
}
11
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
13
return from(this.handleAccess(request, next));
14
}
15
private async handleAccess(request: HttpRequest<any>, next: HttpHandler): Promise<HttpEvent<any>> {
17
// Only add an access token to whitelisted origins
18
const allowedOrigins = ['http://localhost'];
19
if (allowedOrigins.some(url => request.urlWithParams.includes(url))) {
20
const accessToken = await this.oktaAuth.getAccessToken();
21
request = request.clone({
22
setHeaders: {
23
Authorization: 'Bearer ' + accessToken
24
}
25
});
26
}
27
return next.handle(request).toPromise();
28
}
29
}
Примечание . Вы можете заметить, что токены добавляются только для http: // localhost . Вам нужно будет изменить allowedOrigins
массив, чтобы в конечном итоге включить ваш рабочий URL.
Снова запустите приложение ng serve
, откройте приватное окно / окно инкогнито http://localhost:4200
, и вы увидите кнопку « Вход» в левом нижнем углу.
Нажмите на нее, и вы будете перенаправлены в Okta для входа.
Введите действительные учетные данные, и вы будете перенаправлены обратно в свое приложение. Теперь будет кнопка « Выход» , показывающая, что вы успешно прошли аутентификацию.
Теперь, когда вы создали безопасное приложение Angular 9, давайте создадим приложение Spring Boot для обработки данных с помощью REST API.
Создать приложение Spring Boot 2.2
Хорошие ребята из Pivotal создали start.spring.io, чтобы помочь вам быстро создавать приложения Spring Boot с минимальными усилиями. Этот сайт представляет собой приложение Spring Boot, в котором есть REST API, с которым вы можете общаться с помощью HTTPie .
Kotlin является интригующим языком для разработчиков Spring, потому что он сокращает шаблонный код и позволяет сжатый, эффективный код. Kotlin на 100% совместим с Java, поэтому вы можете продолжать использовать библиотеки Java и фреймворки, которые вы знаете и любите. Мало того, но Spring имеет первоклассную поддержку Kotlin.
Создайте новое приложение Spring Boot, которое использует Java 11, Kotlin, Gradle и имеет необходимые зависимости для создания безопасного CRUD API.
Джава
xxxxxxxxxx
1
http https://start.spring.io/starter.zip javaVersion==11 language==kotlin \
2
artifactId==notes-api groupId==com.okta.developer packageName==com.okta.developer.notes \
3
type==gradle-project dependencies==h2,data-jpa,data-rest,okta,web -d
Запустите эту команду в терминале, и notes-api.zip
файл будет загружен. Разверните его в angular-spring-boot/notes-api
каталог.
Оболочка
xxxxxxxxxx
1
unzip notes-api.zip -d angular-spring-boot/notes-api
Вы также можете использовать start.spring.io в своем браузере для создания этого же приложения.
Безопасная загрузка Spring с Spring Security, OAuth 2.0 и OIDC
Поскольку вы выбрали Okta в качестве зависимости, вам необходимо создать приложение OIDC для его аутентификации в Okta. Вы можете использовать идентификатор клиента из вашего приложения Angular, но если вы когда-нибудь захотите разрешить пользователям входить в ваше приложение Spring Boot, ему потребуется собственное приложение OIDC.
Войдите в свою панель Okta, затем:
- Перейдите в Приложения > Добавить приложение .
-
Выберите Web и нажмите Next .
-
Дайте ему имя как
Spring Boot 2.2
и измените URI перенаправления входа наhttp://localhost:8080/login/oauth2/code/okta
-
Нажмите Готово .
Настройки вашего приложения должны выглядеть следующим образом:
Вы можете скопировать настройки OIDC вашего приложения в src/main/resources/application.properties
:
Файлы свойств
xxxxxxxxxx
1
okta.oauth2.issuer=https://{yourOktaDomain}/oauth2/default
2
okta.oauth2.client-id={yourClientId}
3
okta.oauth2.client-secret={yourClientSecret}
Тем не менее, вы никогда не должны хранить секреты в системе контроля версий ! Чтобы помешать вам проверять секреты, вы можете использовать git-секреты .
Для этого примера, скопируйте настройки в новый okta.env
файл и игнорировать *.env
в вашем notes-api/.gitignore
файле.
Файлы свойств
xxxxxxxxxx
1
export OKTA_OAUTH2_ISSUER=https://{yourOktaDomain}/oauth2/default
2
export OKTA_OAUTH2_CLIENT_ID={yourClientId}
3
export OKTA_OAUTH2_CLIENT_SECRET={yourClientSecret}
После замены {…}
заполнителей вашими значениями запустите, source okta.env
чтобы установить эти переменные среды.
Затем запустите ваше приложение, используя ./gradlew bootRun
. Откройте http://localhost:8080
в браузере, и вы будете перенаправлены в Okta для входа.
Примечание . Если вы не получили подсказку, это потому, что вы уже вошли в систему. Попробуйте в окне инкогнито, чтобы увидеть полный поток входа в систему.
Spring Boot как сервер ресурсов OAuth 2.0
Ваш Spring Boot API теперь защищен, но он не настроен на поиск Authorization
заголовка с токеном доступа. Вам нужно написать код, чтобы сделать ваш Spring Boot API сервером ресурсов OAuth 2.0.
Создайте SecurityConfiguration.kt
класс в том же каталоге, что и DemoApplication.kt
:
Котлин
xxxxxxxxxx
1
package com.okta.developer.notes
2
import org.springframework.security.config.annotation.web.builders.HttpSecurity
4
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
5
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
6
@EnableWebSecurity
8
class SecurityConfiguration : WebSecurityConfigurerAdapter() {
9
override fun configure(http: HttpSecurity) {
10
//@formatter:off
11
http
12
.authorizeRequests().anyRequest().authenticated()
13
.and()
14
.oauth2Login()
15
.and()
16
.oauth2ResourceServer().jwt()
17
//@formatter:on
18
}
19
}
oauth2Login() Конфигурация не является необходимой для этого примера на работу. Это необходимо, только если вы хотите требовать аутентификацию из браузера. |
Добавление Notes REST API с помощью Spring Data REST
Начните с создания нового Note
объекта в src/main/kotlin/…/notes/DemoApplication.kt
.
Котлин
xxxxxxxxxx
1
package com.okta.developer.notes
2
import com.fasterxml.jackson.annotation.JsonIgnore
4
import org.springframework.boot.autoconfigure.SpringBootApplication
5
import org.springframework.boot.runApplication
6
import javax.persistence.Entity
7
import javax.persistence.GeneratedValue
8
import javax.persistence.Id
9
@SpringBootApplication
11
class DemoApplication
12
fun main(args: Array<String>) {
14
runApplication<DemoApplication>(*args)
15
}
16
@Entity
18
data class Note(@Id @GeneratedValue var id: Long? = null,
19
var title: String? = null,
20
var text: String? = null,
21
@JsonIgnore var user: String? = null)
Классы данных Kotlin созданы для хранения данных. При добавлении data
ключевого слова, ваш класс получит equals()
, hashCode()
, toString()
, и copy()
функцию. Type? = null
Синтаксис означает , что аргументы обнуляемые при создании нового экземпляра класса.
Создайте NotesRepository
для сохранения данных в ваших заметках. Добавьте следующие строки кода чуть ниже вашей Note
сущности.
Котлин
xxxxxxxxxx
1
@RepositoryRestResource
2
interface NotesRepository : JpaRepository<Note, Long>
В extends
синтаксисе отличается от Java и является гораздо более кратким (двоеточие вместо extends
). Если ваша IDE не добавляет автоматически импорт, вам нужно добавить следующее в верхней части файла.
Котлин
xxxxxxxxxx
1
import org.springframework.data.jpa.repository.JpaRepository
2
import org.springframework.data.rest.core.annotation.RepositoryRestResource
Чтобы автоматически добавить имя пользователя в заметку при ее создании, добавьте имя RepositoryEventHandler
, которое вызывается перед созданием записи.
Котлин
xxxxxxxxxx
1
@Component
2
@RepositoryEventHandler(Note::class)
3
class AddUserToNote {
4
@HandleBeforeCreate
6
fun handleCreate(note: Note) {
7
val username: String = SecurityContextHolder.getContext().getAuthentication().name
8
println("Creating note: $note with user: $username")
9
note.user = username
10
}
11
}
Импорт для этого класса:
Котлин
xxxxxxxxxx
1
import org.springframework.data.rest.core.annotation.HandleBeforeCreate
2
import org.springframework.data.rest.core.annotation.RepositoryEventHandler
3
import org.springframework.security.core.context.SecurityContextHolder
4
import org.springframework.stereotype.Component
Создайте DataInitializer.kt
класс, который заполняет базу данных некоторыми данными по умолчанию при запуске.
Котлин
xxxxxxxxxx
1
package com.okta.developer.notes
2
import org.springframework.boot.ApplicationArguments
4
import org.springframework.boot.ApplicationRunner
5
import org.springframework.stereotype.Component
6
@Component
8
class DataInitializer(val repository: NotesRepository) : ApplicationRunner {
9
@Throws(Exception::class)
11
override fun run(args: ApplicationArguments) {
12
listOf("Note 1", "Note 2", "Note 3").forEach {
13
repository.save(Note(title = it, user = "user"))
14
}
15
repository.findAll().forEach { println(it) }
16
}
17
}
Перезапустите приложение Spring Boot, и вы должны увидеть следующее, напечатанное на вашей консоли при запуске.
Простой текст
xxxxxxxxxx
1
Note(id=1, title=Note 1, text=null, user=user)
2
Note(id=2, title=Note 2, text=null, user=user)
3
Note(id=3, title=Note 3, text=null, user=user)
Создайте UserController.kt
класс (в том же каталоге, что и DemoApplication.kt
) и используйте его для фильтрации заметок текущего пользователя, вошедшего в систему. Пока вы это делаете, добавьте /user
конечную точку, которая возвращает информацию о пользователе.
Котлин
xxxxxxxxxx
1
package com.okta.developer.notes
2
import org.springframework.security.core.annotation.AuthenticationPrincipal
4
import org.springframework.security.oauth2.core.oidc.user.OidcUser
5
import org.springframework.web.bind.annotation.GetMapping
6
import org.springframework.web.bind.annotation.RestController
7
import java.security.Principal
8
@RestController
10
class UserController(val repository: NotesRepository) {
11
@GetMapping("/user/notes")
13
fun notes(principal: Principal): List<Note> {
14
println("Fetching notes for user: ${principal.name}")
15
val notes = repository.findAllByUser(principal.name)
16
if (notes.isEmpty()) {
17
return listOf()
18
} else {
19
return notes
20
}
21
}
22
@GetMapping("/user")
24
fun user(@AuthenticationPrincipal user: OidcUser): OidcUser {
25
return user;
26
}
27
}
findAllByUser()
Метод не существует на NotesRepository
, так что вам нужно , чтобы добавить его. Благодаря Spring Data JPA все, что вам нужно сделать, это добавить определение метода в интерфейс, и он будет обрабатывать создание метода поиска в реализации.
Котлин
xxxxxxxxxx
1
interface NotesRepository : JpaRepository<Note, Long> {
2
fun findAllByUser(name: String): List<Note>
3
}
Чтобы предотвратить конфликт путей с конечными точками REST, созданными с помощью @RepositoryRestResource
, установите базовый путь в /api
in application.properties
.
Файлы свойств
xxxxxxxxxx
1
spring.data.rest.base-path=/api
Перезапустите приложение Spring Boot, перейдите к нему http://localhost:8080/user
, и вы увидите всю информацию о вашей учетной записи. Открытие http://localhost:8080/api/notes
покажет заметки по умолчанию, введенные DataInitializer
компонентом.
Добавить CORS-фильтр для Angular
Чтобы ваше приложение Angular (на порту 4200) могло обмениваться данными с приложением Spring Boot (на порту 8080), необходимо включить CORS (совместное использование ресурсов из разных источников). Вы можете сделать это, предоставив свое DemoApplication
тело и определив в нем corsFilter
боб.
Котлин
xxxxxxxxxx
1
import org.springframework.boot.web.servlet.FilterRegistrationBean
2
import org.springframework.context.annotation.Bean
3
import org.springframework.core.Ordered
4
import org.springframework.web.cors.CorsConfiguration
5
import org.springframework.web.cors.UrlBasedCorsConfigurationSource
6
import org.springframework.web.filter.CorsFilter
7
@SpringBootApplication
9
class DemoApplication {
10
@Bean
12
fun simpleCorsFilter(): FilterRegistrationBean<CorsFilter> {
13
val source = UrlBasedCorsConfigurationSource()
14
val config = CorsConfiguration()
15
config.allowCredentials = true
16
config.allowedOrigins = listOf("http://localhost:4200")
17
config.allowedMethods = listOf("*");
18
config.allowedHeaders = listOf("*")
19
source.registerCorsConfiguration("/**", config)
20
val bean = FilterRegistrationBean(CorsFilter(source))
21
bean.order = Ordered.HIGHEST_PRECEDENCE
22
return bean
23
}
24
}
Перезапустите приложение Spring Boot после добавления этого компонента.
Чтобы увидеть, как DemoApplication
должен выглядеть ваш окончательный файл, вы можете просмотреть законченную версию в GitHub .
Теперь, когда ваш API работает, пришло время разработать для него пользовательский интерфейс с Angular 9!
Добавление функции заметки CRUD в Angular
Angular Schematics - инструмент рабочего процесса, который позволяет вам управлять любым проектом, который имеет package.json
. Угловой CLI основан на схемах. OktaDev Schematics использует Schematics для обновления и добавления новых файлов в проекты. Есть даже угловая схема CRUD !
Angular CRUD позволяет генерировать экраны CRUD (создавать, читать, обновлять и удалять) и связанные файлы из JSON.
В вашем notes
приложении Angular установите angular-crud
с помощью npm:
Оболочка
xxxxxxxxxx
1
npm i -D angular-crud@1.0.0
Затем создайте src/app/note
каталог.
Оболочка
xxxxxxxxxx
1
mkdir -p src/app/note
Затем создайте в нем model.json
файл, который определяет метаданные, которые используются при создании файлов.
JSON
xxxxxxxxxx
1
{
2
"title": "Notes",
3
"entity": "note",
4
"api": {
5
"url": "http://localhost:8080/api/notes"
6
},
7
"filter": [
8
"title"
9
],
10
"fields": [
11
{
12
"name": "id",
13
"label": "Id",
14
"isId": true,
15
"readonly": true,
16
"type": "number"
17
},
18
{
19
"name": "title",
20
"type": "string",
21
"label": "Title"
22
},
23
{
24
"name": "text",
25
"type": "string",
26
"label": "Text"
27
}
28
]
29
}
Запустите команду ниже для создания экранов CRUD.
Оболочка
xxxxxxxxxx
1
ng g angular-crud:crud-module note
Вы увидите следующий вывод.
Простой текст
xxxxxxxxxx
1
CREATE src/app/note/note-filter.ts (44 bytes)
2
CREATE src/app/note/note.module.ts (659 bytes)
3
CREATE src/app/note/note.routes.ts (346 bytes)
4
CREATE src/app/note/note.service.spec.ts (607 bytes)
5
CREATE src/app/note/note.service.ts (1744 bytes)
6
CREATE src/app/note/note.ts (69 bytes)
7
CREATE src/app/note/note-edit/note-edit.component.html (1097 bytes)
8
CREATE src/app/note/note-edit/note-edit.component.spec.ts (978 bytes)
9
CREATE src/app/note/note-edit/note-edit.component.ts (1493 bytes)
10
CREATE src/app/note/note-list/note-list.component.html (1716 bytes)
11
CREATE src/app/note/note-list/note-list.component.spec.ts (978 bytes)
12
CREATE src/app/note/note-list/note-list.component.ts (1091 bytes)
13
UPDATE src/app/app.module.ts (540 bytes)
Эта схема создает NotesModule
маршруты, сервисы для связи с API и списки / экраны для просмотра и редактирования заметок. Если вы посмотрите на src/app/note/note.routes.ts
файл, вы увидите маршруты, которые он создает.
Машинопись
xxxxxxxxxx
1
import { Routes } from '@angular/router';
2
import { NoteListComponent } from './note-list/note-list.component';
3
import { NoteEditComponent } from './note-edit/note-edit.component';
4
export const NOTE_ROUTES: Routes = [
6
{
7
path: 'notes',
8
component: NoteListComponent
9
},
10
{
11
path: 'notes/:id',
12
component: NoteEditComponent
13
}
14
];
Добавьте ссылку NoteListComponent
в src/app/home/home.component.html
.
HTML
xxxxxxxxxx
1
<div>
2
<button *ngIf="!isAuthenticated" (click)="oktaAuth.loginRedirect()">Login</button>
3
<p><a routerLink="/notes" *ngIf="isAuthenticated">View Notes</a></p>
4
<button *ngIf="isAuthenticated" (click)="oktaAuth.logout()">Logout</button>
5
</div>
Изменить, src/app/app.component.html
чтобы быть настолько простым, насколько это возможно.
HTML
xxxxxxxxxx
1
<h1>{{ title }} app is running!</h1>
2
<router-outlet></router-outlet>
Если вы хотите npm test передать после изменения этого шаблона, вам нужно изменить, app.component.spec.ts чтобы искать querySelector('h1') вместо querySelector('.content span') . |
Запустите ng serve
(и убедитесь, что приложение Spring Boot тоже работает).
Войдите в систему, и вы должны увидеть ссылку View Notes .
Нажмите на ссылку, и вы увидите экран со списком, подобный приведенному ниже. Заметки не отображаются, потому что вы не создали заметок, связанных с вашим пользователем.
Нажмите на ссылку New, чтобы добавить новую заметку.
Добавьте новую заметку, и вы увидите подобное сообщение в своей бэкэнд-консоли.
Простой текст
xxxxxxxxxx
1
Creating note: Note(id=null, title=1st note, text=Wahoo!, user=null) with user: matt.raible .com
Вы все еще не увидите заметки в списке. Вам нужно изменить, NoteService
чтобы позвонить на /user/notes
конечную точку, чтобы получить ваши заметки.
Машинопись
xxxxxxxxxx
1
find(filter: NoteFilter): Observable<Note[]> {
2
const params = {
3
title: filter.title,
4
};
5
const userNotes = 'http://localhost:8080/user/notes';
6
return this.http.get<Note[]>(userNotes, {params, headers});
7
}
Теперь вы увидите свои заметки в списке. Хорошая работа!
Вы можете быть удивлены, как NoteListComponent
работает. Он загружает заметки пользователя с NoteService
момента инициализации компонента, а также содержит select()
и delete()
методы. Причина, по которой он может общаться с вашим безопасным Spring Boot API, заключается в том, что вышеупомянутое AuthInterceptor
добавляет токен доступа к запросу.
ЦСИ / приложение / примечание / примечание-лист / записной list.component.ts
Машинопись
xxxxxxxxxx
1
import { Component, OnInit } from '@angular/core';
2
import { NoteFilter } from '../note-filter';
3
import { NoteService } from '../note.service';
4
import { Note } from '../note';
5
@Component({
7
selector: 'app-note',
8
templateUrl: 'note-list.component.html'
9
})
10
export class NoteListComponent implements OnInit {
11
filter = new NoteFilter();
13
selectedNote: Note;
14
feedback: any = {};
15
get noteList(): Note[] {
17
return this.noteService.noteList;
18
}
19
constructor(private noteService: NoteService) {
21
}
22
ngOnInit() {
24
this.search();
25
}
26
search(): void {
28
this.noteService.load(this.filter);
29
}
30
select(selected: Note): void {
32
this.selectedNote = selected;
33
}
34
delete(note: Note): void {
36
if (confirm('Are you sure?')) {
37
this.noteService.delete(note).subscribe(() => {
38
this.feedback = {type: 'success', message: 'Delete was successful!'};
39
setTimeout(() => {
40
this.search();
41
}, 1000);
42
},
43
err => {
44
this.feedback = {type: 'warning', message: 'Error deleting.'};
45
}
46
);
47
}
48
}
49
}
Изменить ссылку в шаблон ссылок этого компонента к NoteEditComponent
.
HTML
xxxxxxxxxx
1
<a [routerLink]="['../notes', item.id ]" class="btn btn-secondary">Edit</a>
NoteEditComponent
Имеет методы для загрузки заметки, сохранения заметки и отмены.
Машинопись
xxxxxxxxxx
1
import { Component, OnInit } from '@angular/core';
2
import { ActivatedRoute, Router } from '@angular/router';
3
import { NoteService } from '../note.service';
4
import { Note } from '../note';
5
import { map, switchMap } from 'rxjs/operators';
6
import { of } from 'rxjs';
7
@Component({
9
selector: 'app-note-edit',
10
templateUrl: './note-edit.component.html'
11
})
12
export class NoteEditComponent implements OnInit {
13
id: string;
15
note: Note;
16
feedback: any = {};
17
constructor(
19
private route: ActivatedRoute,
20
private router: Router,
21
private noteService: NoteService) {
22
}
23
ngOnInit() {
25
this
26
.route
27
.params
28
.pipe(
29
map(p => p.id),
30
switchMap(id => {
31
if (id === 'new') { return of(new Note()); }
32
return this.noteService.findById(id);
33
})
34
)
35
.subscribe(note => {
36
this.note = note;
37
this.feedback = {};
38
},
39
err => {
40
this.feedback = {type: 'warning', message: 'Error loading'};
41
}
42
);
43
}
44
save() {
46
this.noteService.save(this.note).subscribe(
47
note => {
48
this.note = note;
49
this.feedback = {type: 'success', message: 'Save was successful!'};
50
setTimeout(() => {
51
this.router.navigate(['/notes']);
52
}, 1000);
53
},
54
err => {
55
this.feedback = {type: 'warning', message: 'Error saving'};
56
}
57
);
58
}
59
cancel() {
61
this.router.navigate(['/notes']);
62
}
63
}
Исправить функцию редактирования заметки
Одна из проблем с ним NoteEditComponent
заключается в том, что API-интерфейс возвращает идентификатор. Поскольку Spring Data REST по умолчанию использует HATEOS, он возвращает ссылки вместо идентификаторов. Вы можете изменить это значение по умолчанию, чтобы оно возвращало идентификаторы, создав RestConfiguration
класс в приложении Spring Boot. Вы можете заметить, что вы также можете настроить базовый путь в этом классе, а не в application.properties
.
Котлин
xxxxxxxxxx
1
package com.okta.developer.notes
2
import org.springframework.context.annotation.Configuration
4
import org.springframework.data.rest.core.config.RepositoryRestConfiguration
5
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer
6
@Configuration
8
class RestConfiguration : RepositoryRestConfigurer {
9
override fun configureRepositoryRestConfiguration(config: RepositoryRestConfiguration?) {
10
config?.exposeIdsFor(Note::class.java)
11
config?.setBasePath("/api")
12
}
13
}
Другой вариант - изменить угловую сторону вещей. Поскольку идентификатор передается в NoteEditComponent
, вы можете установить его как локальную переменную, а затем установить его в заметке после его возвращения. Вот разница в том, какие изменения должны быть сделаны notes/src/app/note/note-edit/note-edit.component.ts
.
разница
xxxxxxxxxx
1
--- a/notes/src/app/note/note-edit/note-edit.component.ts
2
+++ b/notes/src/app/note/note-edit/note-edit.component.ts
3
4
map(p => p.id),
5
switchMap(id => {
6
if (id === 'new') { return of(new Note()); }
7
+ this.id = id;
8
return this.noteService.findById(id);
9
})
10
)
11
.subscribe(note => {
12
this.note = note;
13
+ this.note.id = +note.id;
14
this.feedback = {};
15
},
16
err => {
17
18
this.noteService.save(this.note).subscribe(
19
note => {
20
this.note = note;
21
+ this.note.id = +this.id;
22
this.feedback = {type: 'success', message: 'Save was successful!'};
23
setTimeout(() => {
24
this.router.navigate(['/notes']);
В последнем примере для этого поста я решил вернуть идентификаторы из моего Spring Boot API.
Заблокируйте загрузочный пружинный блок с рекомендациями по безопасности
В 10 превосходных способов защитить приложение Spring Boot , я рекомендовал несколько элементов, специфичных для Spring Boot
-
Используйте HTTPS в производстве.
-
Включить защиту от подделки межсайтовых запросов (CSRF).
-
Используйте Политику безопасности контента (CSP) для предотвращения атак XSS.
-
Используйте OpenID Connect для аутентификации.
Вы уже внедрили # 4 с Okta, но как насчет других?
Вы можете использовать mkcert для генерации локальных действительных сертификатов TLS. Чтобы активировать HTTPS, вам просто нужно настроить Spring Security. Мне нравится просто делать это в производстве, поэтому мне не нужно устанавливать сертификаты в разработке.
Защита CSRF и CSP могут быть настроены с помощью Spring Security.
Измените свой SecurityConfiguration
класс с этими улучшениями безопасности.
Котлин
xxxxxxxxxx
1
class SecurityConfiguration : WebSecurityConfigurerAdapter() {
2
override fun configure(http: HttpSecurity) {
3
//@formatter:off
4
http
5
.authorizeRequests().anyRequest().authenticated()
6
.and()
7
.oauth2Login()
8
.and()
9
.oauth2ResourceServer().jwt()
10
http.requiresChannel()
12
.requestMatchers(RequestMatcher {
13
r -> r.getHeader("X-Forwarded-Proto") != null
14
}).requiresSecure()
15
http.csrf()
17
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
18
http.headers()
20
.contentSecurityPolicy("script-src 'self'; report-to /csp-report-endpoint/");
21
//@formatter:on
22
}
23
}
1. | Принудительно HTTPS в производстве |
2. | Настройте файл cookie CSRF таким образом, чтобы его можно было читать с помощью JavaScript |
3 | Настройте CSP, который разрешает только локальные сценарии |
Angular's HttpClient
имеет встроенную поддержку клиентской части защиты CSRF. Он прочитает куки, отправленные Spring Boot, и вернет их в X-XSRF-TOKEN
заголовке. Вы можете прочитать больше об этом в документации по безопасности Angular .
В этом конкретном примере CSP не будет использоваться, поскольку Angular - это отдельное приложение. Однако, если вы включите приложение Angular в свой артефакт Spring Boot, это пригодится.
Развернув приложение Spring Boot по общедоступному URL-адресу, вы можете проверить работу заголовков CSP с securityheaders.com .
Примечание. После развертывания приложения Spring Boot по общедоступному URL-адресу вы можете проверить работу заголовков CSP с securityheaders.com . |
Узнайте больше об Angular, Spring Boot и Kotlin
В этом руководстве я показал вам, как создать приложение Angular 9, приложение Spring Boot 2.2 и как защитить связь между ними с помощью OAuth 2.0 и OIDC. Вы использовали Kotlin на бэкэнде; язык любимый многими. Вы использовали Angular Schematics для генерации кода для аутентификации и CRUD, повышая вашу эффективность как разработчика.
В этом руководстве не было показано, как сделать ваше приложение Angular хорошо, добавить проверку или как развернуть его на общедоступном сервере. Я рассмотрю эти темы в следующем посте. Тем временем вы можете увидеть предыдущий учебник, который я написал, чтобы увидеть, как это приложение может выглядеть с Angular Material.
Вы можете найти исходный код для завершенного приложения в oktadeveloper / okta-spring-boot-2-angular-9-example .
В нашем блоге есть учебники по Angular, Spring Boot и Kotlin. Вот некоторые, которые я рекомендую:
- Руководство по Java OAuth 2.0: защитите свое приложение за 5 минут
- Безопасные реактивные микросервисы с Spring Cloud Gateway
- Создайте приложение с помощью Spring Boot и Kotlin
Changelog:
-
19 февраля 2020 г. Обновлен для использования Angular CLI 9.0.2 (с Angular 9.0.1). Смотрите изменения кода в примере приложения на GitHub . Изменения в этой статье можно посмотреть в oktadeveloper / okta-blog # 197 .
-
30 января 2020: обновлен для использования Spring Boot 2.2.4 и Angular CLI 9.0 RC11 (с Angular 9.0 RC11). Смотрите изменения кода в примере приложения на GitHub . Изменения в этой статье можно посмотреть в oktadeveloper / okta-blog # 175 .