kotlin 构建对象

I love my job as a developer advocate at Okta. I get to learn a lot, write interesting blog posts and create example apps with cool technologies like Kotlin, TypeScript, Spring Boot, and Angular, which I’m about to demo. When it comes to writing Hello World apps with authentication, I can whip one out in a few minutes. That isn't because I'm a particularly good programmer, it's because the languages, frameworks, tools, and platforms available to developers are impressive.

我喜欢我在Okta担任开发人员拥护者的工作。 我将学到很多东西,撰写有趣的博客文章,并使用即将演示的Kotlin,TypeScript,Spring Boot和Angular等酷技术创建示例应用程序。 在编写带有身份验证的Hello World应用程序时,我可以在几分钟内完成。 那不是因为我是一个特别优秀的程序员,而是因为开发人员可以使用的语言,框架,工具和平台令人印象深刻。

In this tutorial, I'll show you how to write a note-taking application in Kotlin and TypeScript - two of the fastest growing languages of 2017. You'll use two popular frameworks, Spring Boot and Angular, to make development super fast. Along the way, I’ll show you a few tips and tricks from my favorite development IDE, IntelliJ IDEA. Finally, we’ll leverage Angular CLI and start.spring.io to generate application skeletons.

在本教程中,我将向您展示如何用Kotlin和TypeScript (2017年增长最快的两种语言)编写笔记应用程序。 您将使用两个流行的框架Spring Boot和Angular使开发变得超快。 在此过程中,我将向您展示我最喜欢的开发IDE IntelliJ IDEA的一些技巧。 最后,我们将利用Angular CLI和start.spring.io生成应用程序框架。

As with any good example app, you’ll want to deploy it securely, so I'll show you how to do that using Okta's Identity APIs and our new Spring Boot starter. The Okta Spring Boot starter allows you to make your API into a resource server that can read and validate access tokens sent to it. The diagram below shows how a resource server fits into an OAuth architecture.

与任何优秀的示例应用程序一样,您将需要安全地对其进行部署,因此,我将向您展示如何使用Okta的Identity API和我们新的Spring Boot启动程序来进行此操作。 Okta Spring Boot启动程序使您可以将API放入资源服务器,该服务器可以读取和验证发送给它的访问令牌。 下图显示了资源服务器如何适应OAuth架构。

Phew! That's a lot of buzzwords for one article. Don't worry, I've confirmed it's possible to develop this app in even less time than it takes to deploy and secure it. And developing is fun, so let's get started!

! 一篇文章有​​很多流行语。 不用担心,我已经确认可以在比部署和保护它所需的时间短的时间内开发此应用程序。 开发很有趣,所以让我们开始吧!

使用Kotlin和Spring Boot构建Notes API ( Build a Notes API with Kotlin and Spring Boot )

Start building the API for your application by navigating your favorite browser to start.spring.io. Select Kotlin as your language, and choose Web, H2, JPA, Rest Repositories, and DevTools. You’ll notice in the screenshot below that I changed the group and artifact names too. Please use these same names, so your package and class names match this tutorial.

通过将喜欢的浏览器导航到start.spring.io,开始为您的应用程序构建API。 选择Kotlin作为您的语言,然后选择Web,H2,JPA,Rest Repositories和DevTools。 您会在下面的屏幕截图中注意到,我也更改了组和工件名称。 请使用相同的名称,因此您的程序包和类名称与本教程匹配。

Click Generate Project and expand the zip file after downloading. If you don’t have IntelliJ IDEA installed, now’s a good time to try it out. It’s a great IDE for Java, Kotlin, Groovy, TypeScript, JavaScript, HTML, and Sass/CSS. One of its killer features is the ability to copy/paste Java code into a Kotlin class and have it auto-converted to Kotlin on-the-fly!

单击生成项目并在下载后展开zip文件。 如果您尚未安装IntelliJ IDEA,那么现在是尝试一下的好时机。 这是Java,Kotlin,Groovy,TypeScript,JavaScript,HTML和Sass / CSS的出色IDE。 它的杀手级功能之一是能够将Java代码复制/粘贴到Kotlin类中,并能够将其自动即时转换为Kotlin!

You can also turn on automatic-compilation-on-save and reap the benefits of Spring Boot’s DevTools that restart your app when files change.

您还可以打开保存时自动编译,并利用Spring Boot的DevTools的优点,当文件更改时,该工具可以重新启动您的应用程序。

@media (max-width: 1280px) { .go-go-gadget-react img:first-child { display: none; } }@media (max-width: 780px) {.go-go-gadget-react { flex-direction: column; }.go-go-gadget-react img { margin-left: 0 !important; margin-bottom: 12px !important; }.header-thingy { margin-top: 20px; }.button-thingy { margin-left: 0 !important; margin-top: 12px !important; }} @media (max-width: 1280px) { .go-go-gadget-react img:first-child { display: none; } }@media (max-width: 780px) {.go-go-gadget-react { flex-direction: column; }.go-go-gadget-react img { margin-left: 0 !important; margin-bottom: 12px !important; }.header-thingy { margin-top: 20px; }.button-thingy { margin-left: 0 !important; margin-top: 12px !important; }}

  • Go to Preferences > Build, Execution, Deployment > Compiler and enable "Build project automatically"转到首选项>构建,执行,部署>编译器,然后启用“自动构建项目”
  • Open the Action window:
    • Linux: CTRL+SHIFT+A
    • Mac: SHIFT+COMMAND+A
    • Windows: CTRL+ALT+SHIFT+/

    打开“操作”窗口:

    • Linux:CTRL + SHIFT + A
    • Mac:SHIFT + COMMAND + A
    • Windows:CTRL + ALT + SHIFT + /
  • Enter Registry… and enable compiler.automake.allow.when.app.running输入注册表...并启用compiler.automake.allow.when.app.running

Start by creating a new Note entity in src/main/kotlin/com/okta/developer/notes/NotesApplication.kt.

首先在src/main/kotlin/com/okta/developer/notes/NotesApplication.kt创建一个新的Note实体。

@SpringBootApplication
class NotesApplicationfun main(args: Array<String>) {SpringApplication.run(NotesApplication::class.java, *args)
}@Entity
data class Note(@Id @GeneratedValue var id: Long? = null,var text: String? = null, var user: String? = null)

Kotlin’s data classes are built to hold data. By adding the data keyword, your class will get equals(), hashCode(), toString(), and a copy() function. The Type? = null syntax means the arguments are nullable when creating a new instance of the class.

Kotlin的数据类用于保存数据。 通过添加data关键字,您的类将获得equals()hashCode()toString()copy()函数。 Type? = null Type? = null语法意味着在创建类的新实例时参数可以为空。

Create a NotesRepository for persisting the data in your notes. Add the following lines of code just below your Note entity.

创建一个NotesRepository以将数据持久存储在便笺中。 在您的Note实体下方添加以下代码行。

@RepositoryRestResource
interface NotesRepository : JpaRepository<Note, Long>

The extends syntax differs from Java and is a lot more concise (a colon instead of extends).

extends语法与Java有所不同,并且更加简洁(用冒号代替extends )。

Create a DataInitializer bean that populates the database with some default data on startup.

创建一个DataInitializer bean,该bean在启动时用一些默认数据填充数据库。

@Component
class DataInitializer(val repository: NotesRepository) : ApplicationRunner {@Throws(Exception::class)override fun run(args: ApplicationArguments) {listOf("Note 1", "Note 2", "Note 3").forEach {repository.save(Note(text = it, user = "user"))}repository.findAll().forEach { println(it) }}
}

This example shows constructor injection, but Kotlin also supports field injection with @Autowired.

此示例显示了构造函数注入,但Kotlin还支持@Autowired字段注入 。

Start the app in your IDE using its Spring Boot tooling, or from the command line using mvnw spring-boot:run. If you’re on a Mac or Linux, you might need to use ./mvnw spring-boot:run.

使用其Spring Boot工具在IDE中启动应用程序,或者使用mvnw spring-boot:run从命令行启动应用程序。 如果您使用的是Mac或Linux,则可能需要使用./mvnw spring-boot:run

You should see the following printed to your console on startup.

启动时,您应该在控制台上看到以下内容。

Note(id=1, text=Note 1, user=user)
Note(id=2, text=Note 2, user=user)
Note(id=3, text=Note 3, user=user)

I recommend installing HTTPie, a command-line HTTP client that is much easier to use than curl. Use HTTPie to query the /notes endpoint provided by Spring Data REST’s @RepositoryRestResource.

我建议安装HTTPie ,这是一个比curl更易于使用的命令行HTTP客户端。 使用HTTPie查询/notes终结点由Spring数据REST的提供@RepositoryRestResource

http localhost:8080/notes

The result will look like the following screenshot.

结果将类似于以下屏幕截图。

Create a HomeController (in the same NotesApplication.kt file) and use it to filter notes by the currently logged-in user.

创建一个HomeController (在同一NotesApplication.kt文件中),并使用它来过滤当前登录用户的注释。

import java.security.Principal@RestController
class HomeController(val repository: NotesRepository) {@GetMapping("/")fun home(principal: Principal): List<Note> {println("Fetching notes for user: ${principal.name}")val notes = repository.findAllByUser(principal.name)if (notes.isEmpty()) {return listOf()} else {return notes}}
}

The findAllByUser() method doesn’t exist on NotesRepository, so you’ll need to add it. Thanks to Spring Data JPA, all you need to do is add the method definition to the interface, and it will handle generating the finder method in the implementation.

findAllByUser()方法不上不存在NotesRepository ,所以你需要添加它。 多亏了Spring Data JPA,您所要做的就是将方法定义添加到接口中,它将在实现中处理生成finder方法。

interface NotesRepository : JpaRepository<Note, Long> {fun findAllByUser(name: String): List<Note>
}

If you try to access this new endpoint, you’ll get an error that the Principal parameter is not defined.

如果尝试访问此新端点,则会收到一个错误,提示您未定义Principal参数。

$ http localhost:8080
HTTP/1.1 500
Connection: close
Content-Type: application/json;charset=UTF-8
Date: Fri, 11 Aug 2017 07:36:46 GMT
Transfer-Encoding: chunked
{"error": "Internal Server Error","exception": "java.lang.IllegalArgumentException","message": "Parameter specified as non-null is null: method com.okta.developer.notes.HomeController.home, parameter principal","path": "/","status": 500,"timestamp": 1502437006005
}

Spring MVC throws a 500 error because it has no knowledge of a logged-in user. Add the Spring Security starter to your pom.xml to enable security in your application.

Spring MVC抛出500错误,因为它不知道已登录的用户。 将Spring Security启动器添加到pom.xml以在应用程序中启用安全性。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>

Restart the Maven process to download this new dependency and add it to the classpath.

重新启动Maven进程以下载此新依赖项并将其添加到类路径中。

If you navigate to http://localhost:8080 in your browser, you will see a basic authentication dialog. The command line will yield similar results.

如果在浏览器中导航到http:// localhost:8080 ,将看到一个基本的身份验证对话框。 命令行将产生类似的结果。

{"error": "Unauthorized","message": "Full authentication is required to access this resource","path": "/","status": 401,"timestamp": 1502437281185
}

The Spring Security starter creates a default user with username “user” and a password that changes every time you start the application. You can find this password in your terminal, similar to the one below.

Spring Security启动程序会创建一个默认用户,其用户名为“ user”,并且密码每次启动应用程序时都会更改。 您可以在终端中找到该密码,类似于以下密码。

Usingdefault security password: 103c55b4-2760-4830-9bca-a06a87d384f9

Change the user’s password so it’s the same every time by adding the following to src/main/resources/application.properties.

通过将以下内容添加到src/main/resources/application.properties更改用户密码,使其每次都相同。

security.user.password=kotlin is fun!

After the change, verify that this HTTPie command works.

更改后,请验证此HTTPie命令是否有效。

$ http --auth user:'kotlin is fun!' localhost:8080
HTTP/1.1 200
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: application/json;charset=UTF-8
Date: Fri, 11 Aug 2017 07:47:10 GMT
Expires: 0
Pragma: no-cache
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
Transfer-Encoding: chunked
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
[{"id": 1,"text": "Note 1"},{"id": 2,"text": "Note 2"},{"id": 3,"text": "Note 3"}
]

The reason you don’t see the user property in the JSON above is because I added a @JsonIgnore annotation to the Note class.

您在上面的JSON中看不到user属性的原因是因为我在Note类中添加了@JsonIgnore批注。

import com.fasterxml.jackson.annotation.JsonIgnore@Entity
data class Note(@Id @GeneratedValue var id: Long? = null,var text: String? = null,@JsonIgnore var user: String? = null)

To automatically add the username to a note when it’s created, add a RepositoryEventHandler that is invoked before creating the record.

要在创建用户名时将其自动添加到便笺中,请添加一个RepositoryEventHandler ,在创建记录之前将其调用。

@Component
@RepositoryEventHandler(Note::class)
class AddUserToNote {@HandleBeforeCreatefun handleCreate(note: Note) {val username: String =  SecurityContextHolder.getContext().getAuthentication().nameprintln("Creating note: $note with user: $username")note.user = username}
}

After adding the handler, saving your files, and waiting for your API to restart, you’ll be able to run the following commands with wild success.

添加处理程序,保存文件并等待API重新启动后,您将能够成功运行以下命令。

http --auth user:'kotlin is fun!' POST localhost:8080/notes text='Note 4'
http --auth user:'kotlin is fun!' PUT localhost:8080/notes/4 text='Remember the Milk!'
http --auth user:'kotlin is fun!' DELETE localhost:8080/notes/4

Your API works and is locked down, but you still only have one user. Rather than spending time setting up database tables and encrypting passwords, you can use Okta’s APIs to manage, authenticate, and authorize your users securely. To get started with Okta, sign up for a free-forever developer account.

您的API可以使用并且已被锁定,但是您仍然只有一个用户。 您无需花时间设置数据库表和加密密码,而是可以使用Okta的API安全地管理,认证和授权用户。 要开始使用Okta,请注册一个永久性的开发者帐户 。

Okta Spring启动启动器 ( The Okta Spring Boot Starter )

Okta provides a Spring Boot starter that integrates with Spring Security and its OAuth 2.0 support. Replace the Spring Security starter with the Okta Spring Security starter.

Okta提供了一个Spring Boot启动器,该启动器与Spring Security及其OAuth 2.0支持集成在一起。 用Okta Spring Security启动器替换Spring Security启动器。

<dependency><groupId>com.okta.spring</groupId><artifactId>okta-spring-security-starter</artifactId><version>0.1.0</version>
</dependency>

You’ll also need to upgrade the OAuth library used by Spring Security to the latest version.

您还需要将Spring Security使用的OAuth库升级到最新版本。

<dependencyManagement><dependencies><dependency><groupId>org.springframework.security.oauth</groupId><artifactId>spring-security-oauth2</artifactId><version>2.2.0.RELEASE</version></dependency></dependencies>
</dependencyManagement>

After modifying your pom.xml, configure it with your Okta settings.

修改pom.xml ,使用Okta设置对其进行配置。

添加授权服务器 (Add an Authorization Server)

Log in to your Okta account and click the Admin button in the top right. Navigate to Security > API in the top menu and click on the Add Authorization Server button. Use a name and audience that works for you. For example, I chose:

登录到您的Okta帐户,然后单击右上角的“ 管理”按钮。 导航到顶部菜单中的“ 安全性” >“ API” ,然后单击“ 添加授权服务器”按钮。 使用适合您的名称和受众群体。 例如,我选择了:

  • Name: My AS名称:我的AS
  • Audience: people-of-earth受众群体:地球人

Copy and paste the Issuer URL and audience values into application.properties:

将Issuer URL和受众群体值复制并粘贴到application.properties

okta.oauth.issuer={issuer}
okta.oauth.audience={audience}

The Okta Spring Security starter expects you to have a custom claim called groups. Define a custom claim with these values:

Okta Spring Security入门者希望您有一个名为groups的自定义声明。 使用以下值定义自定义声明:

  • Name: groups名称: groups
  • Value Type: Groups值类型: Groups
  • Filter: Regex - .*过滤器:正则Regex - .*

You’ll also need to add a new Access Policy and Rule that gives everyone access. Of course, you can lock these down if you want to, but it’s best to use the defaults for this tutorial.

您还需要添加一个新的访问策略和规则,该规则和规则可以授予所有人访问权限。 当然,您可以根据需要将其锁定,但是最好在本教程中使用默认值。

Finally, navigate to Security > API > Trusted Origins and add http://localhost:4200 as an Origin URL with CORS support.

最后,导航到“ 安全性” >“ API” >“ 受信任的起源” ,并将http://localhost:4200为具有CORS支持的起源URL。

添加一个OpenID Connect应用程序 (Add an OpenID Connect Application)

Navigate to Applications and click on Add Applications > Create New App. Select Single Page App (SPA) using OpenID Connect and click Create. Give the application a name (e.g. “My OIDC App”) and specify http://localhost:4200 as a Login redirect URI. Your upcoming Angular client will use this value. Click Save and admire your handiwork!

导航到应用程序 ,然后单击添加应用程序 > 创建新应用程序 。 使用OpenID Connect选择单页应用(SPA),然后点击创建 。 为应用程序命名(例如“ My OIDC App”),并指定http://localhost:4200作为登录重定向URI。 您即将到来的Angular客户端将使用此值。 单击保存并欣赏您的手工!

Click on the Assignments tab and click Assign. Assign the “Everyone” group and click Done.

单击“ 分配”选项卡,然后单击“ 分配” 。 分配“所有人”组,然后单击“完成”

Copy the Client ID value into application.properties.

将客户端ID值复制到application.properties

okta.oauth.clientId={client-id}

Before you start building the Angular client, add a CORS filter to NotesApplication so cross-origin requests can succeed.

在开始构建Angular客户端之前,请向NotesApplication添加一个CORS过滤器,以便跨域请求可以成功进行。

class NotesApplication {@Beanopen fun simpleCorsFilter(): FilterRegistrationBean {val source = UrlBasedCorsConfigurationSource()val config = CorsConfiguration()config.allowCredentials = trueconfig.allowedOrigins = listOf("http://localhost:4200")config.allowedMethods = listOf("_");config.allowedHeaders = listOf("_")source.registerCorsConfiguration("/_*", config)val bean = FilterRegistrationBean(CorsFilter(source))bean.order = Ordered.HIGHEST_PRECEDENCEreturn bean}
}fun main(args: Array<String>) {SpringApplication.run(NotesApplication::class.java, _args)
}
…

You can see the final version of this file on GitHub.

您可以在GitHub上查看此文件的最终版本。

I hope you’ve enjoyed this quick tour of Kotlin and saw how its concise syntax can be a lot of fun. In May 2017, Kotlin was announced as an officially supported language on Android, giving the language quite a bit of attention. You can learn more about Kotlin on kotlinlang.org.

我希望您喜欢Kotlin的快速浏览,并看到其简洁的语法会带来很多乐趣。 2017年5月,Kotlin被宣布为Android上官方支持的语言 ,使该语言备受关注。 您可以在kotlinlang.org上了解有关Kotlin的更多信息。

使用TypeScript和Angular CLI构建Angular UI ( Build an Angular UI with TypeScript and Angular CLI )

Angular CLI is a convenient way to create Angular applications. It generates a project skeleton, installs all the dependencies, and configures Webpack to compile TypeScript and optimize for production.

Angular CLI是创建Angular应用程序的便捷方法。 它生成一个项目框架,安装所有依赖项,并将Webpack配置为编译TypeScript并针对生产进行优化。

Install Angular CLI using Facebook’s Yarn.

使用Facebook的Yarn安装Angular CLI。

yarn add global @angular/cli

Or using npm (npm install -g @angular/cli).

或使用npm( npm install -g @angular/cli )。

Then create a new project using its ng command.

然后使用其ng命令创建一个新项目。

ng new client

It takes a minute or two to install all the dependencies. After it finishes, you can run ng serve to view the app, or ng test to run unit tests. If you want to verify that the end-to-end tests pass, run ng e2e.

安装所有依赖项需要一两分钟。 完成后,您可以运行ng serve查看应用程序,或执行ng test运行单元测试。 如果要验证端到端测试通过,请运行ng e2e

Create a service and component using the generate (alias: g) command. You can use s as an alias for service and c as an alias for component.

使用generate (alias: g )命令创建服务和组件。 您可以将s用作service的别名,将c用作component的别名。

ng gservice note
ng g component note-list
ng g c note-detail
ng g c login

The service files are generated in client/src/app by default, but I like to move them into a shared/{service} directory.

默认情况下,服务文件是在client/src/app中生成的,但是我想将它们移动到shared/{service}目录中。

mkdir -p src/app/shared/note
mv src/app/note.service.* src/app/shared/note

At this point, I’d recommend opening your Angular client in IntelliJ IDEA. It has excellent TypeScript support and will auto-import classes for you, just like it does for Java and Kotlin.

此时,我建议在IntelliJ IDEA中打开Angular客户端。 它具有出色的TypeScript支持,并且会像Java和Kotlin一样自动为您导入类。

Add the NoteService to the providers list in client/src/app/app.module.ts. Notice that Angular CLI has already added the generated components to the declarations list.

NoteService添加到client/src/app/app.module.tsproviders列表中。 注意,Angular CLI已经将生成的组件添加到了declarations列表中。

@NgModule({declarations: [AppComponent,NoteListComponent,NoteDetailComponent,LoginComponent],imports: [BrowserModule],providers: [NoteService],bootstrap: [AppComponent]
})
export class AppModule {}

Modify client/src/app/shared/note/note.service.ts to have a getAll() method that talks to the API.

修改client/src/app/shared/note/note.service.ts以使其具有与API通讯的getAll()方法。

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { HttpClient } from '@angular/common/http';@Injectable()
export class NoteService {public API = 'http://localhost:8080';public NOTE_API = this.API + '/notes';constructor(private http: HttpClient) {}getAll(): Observable<any> {return this.http.get(this.API);}
}

TIP: If you’re using IntelliJ IDEA, I recommend you install the Angular 2 TypeScript Live Templates. They drastically reduce the amount of code you have to write with several code-generation shortcuts.

提示:如果您使用的是IntelliJ IDEA,建议您安装Angular 2 TypeScript动态模板 。 它们通过几个代码生成快捷方式极大地减少了您必须编写的代码量 。

In client/src/app/notes-list/note-list.component.ts, add a dependency on NoteService and get all the user’s notes when the component loads.

client/src/app/notes-list/note-list.component.ts ,添加对NoteService的依赖关系,并在加载组件时获取所有用户的注释。

import { NoteService } from '../shared/note/note.service';export class NoteListComponent implements OnInit {notes: Array<any>;constructor(private noteService: NoteService) {}ngOnInit() {this.noteService.getAll().subscribe(data => {this.notes = data;}, error => console.error(error));}
}

Replace the HTML in client/src/app/note-list/note-list.component.html with a few lines to render the notes list.

用几行替换client/src/app/note-list/note-list.component.htmlHTML以呈现注释列表。

<h2>Notes List</h2>
<div *ngFor="let note of notes">{{note.text}}
</div>

If you try to make things work at this point, you won’t be able to access your API because it expects you to send an access token in an Authorization header.

如果此时尝试使事情正常进行,则将无法访问您的API,因为它希望您在Authorization标头中发送访问令牌。

Install the Okta Sign-In Widget to authenticate using the “My OIDC” app you already created and get an access token.

安装Okta登录小部件以使用您已经创建的“我的OIDC”应用进行身份验证,并获取访问令牌。

yarn add @okta/okta-signin-widget

Create an OktaAuthService that can be used to render the Sign-In Widget and handle authentication. The following TypeScript code should be in client/src/app/shared/okta/okta.service.ts. Be sure to replace {dev-id}, {client-id}, and {as-server-url} with values appropriate for your Okta application and authorization server.

创建一个OktaAuthService ,可用于呈现登录小部件并处理身份验证。 以下TypeScript代码应位于client/src/app/shared/okta/okta.service.ts 。 确保用适合您Okta应用程序和授权服务器的值替换{dev-id}{client-id}{as-server-url}

import { Injectable } from '@angular/core';
import * as OktaSignIn from '@okta/okta-signin-widget/dist/js/okta-sign-in.min.js'
import { ReplaySubject } from 'rxjs/ReplaySubject';
import { Observable } from 'rxjs/Observable';
import { Router } from '@angular/router';@Injectable()
export class OktaAuthService {signIn = new OktaSignIn({baseUrl: 'https://dev-{dev-id}.oktapreview.com',clientId: '{client-id}',redirectUri: 'http://localhost:4200',authParams: {issuer: '{as-server-url}',responseType: ['id_token', 'token'],scopes: ['openid', 'email', 'profile']}});private userSource: ReplaySubject<any>;public user$: Observable<any>;constructor(private router: Router) {this.userSource = new ReplaySubject<any>(1);this.user$ = this.userSource.asObservable();}isAuthenticated() {// Checks if there is a current accessToken in the TokenManger.return !!this.signIn.tokenManager.get('accessToken');}login(next?: string) {if (next) {this.router.navigate(['login', {next: next}]);} else {this.router.navigate(['login']);}}showLogin() {// Launches the widget and stores the tokenstry {this.signIn.renderEl({el: '#okta-signin-container'}, response => {if (response.status === 'SUCCESS') {response.forEach(token => {if (token.idToken) {this.signIn.tokenManager.add('idToken', token);}if (token.accessToken) {this.signIn.tokenManager.add('accessToken', token);}});this.userSource.next(this.idTokenAsUser);this.signIn.hide();} else {console.error(response);}});} catch (exception)  {// An instance of the widget has already been rendered. Call remove() first.}}get idTokenAsUser() {const token = this.signIn.tokenManager.get('idToken');return {name: token.claims.name,email: token.claims.email,username: token.claims.preferred_username};}async logout() {// Terminates the session with Okta and removes current tokens.this.signIn.tokenManager.clear();await this.signIn.signOut();this.signIn.remove();this.userSource.next(undefined);this.login();}
}

NOTE: I realize this is quite a bit of code to render a sign-in form. The good news is we’ll be wrapping much of it into an okta-angular library soon.

注意:我意识到这是渲染登录表单的大量代码。 好消息是,我们很快会将其包装到okta-angular库中。

Create an OktaAuthGuard in client/src/app/shared/okta/okta.guard.ts. You’ll use this to guard routes so they can’t be activated if the user isn’t authenticated.

创建OktaAuthGuardclient/src/app/shared/okta/okta.guard.ts 。 您将使用它来保护路由,以便在用户未通过身份验证时无法激活它们。

import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
import { OktaAuthService } from './okta.service';@Injectable()
export class OktaAuthGuard implements CanActivate {signIn;authenticated;constructor(private oktaService: OktaAuthService) {this.signIn = oktaService;}canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {this.authenticated = this.oktaService.isAuthenticated();if (this.authenticated) {return true;}this.signIn.login();return false;}
}

Create an OktaAuthInterceptor in client/src/app/shared/okta.interceptor.ts to automatically add an Authorization header to HTTP requests.

client/src/app/shared/okta.interceptor.ts创建OktaAuthInterceptor ,以自动向HTTP请求添加Authorization标头。

import { Injectable } from '@angular/core';
import {HttpRequest,HttpHandler,HttpEvent,HttpInterceptor, HttpErrorResponse, HttpResponse
} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { OktaAuthService } from './okta.service';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';@Injectable()
export class OktaAuthInterceptor implements HttpInterceptor {constructor(private oktaService: OktaAuthService) {}intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {if (this.oktaService.isAuthenticated()) {const accessToken = this.oktaService.signIn.tokenManager.get('accessToken');request = request.clone({setHeaders: {Authorization: `${accessToken.tokenType} ${accessToken.accessToken}`}});}return next.handle(request).map((event: HttpEvent<any>) => {if (event instanceof HttpResponse) {return event;}}).catch(error => {if (error instanceof HttpErrorResponse) {if (error.status === 401) {this.oktaService.login();return Observable.create(error);}}});}
}

In client/src/app/app.module.ts, define the routes for the application, with canActivate guards for the note-related routes.

client/src/app/app.module.ts ,定义应用程序的路由,并为与笔记相关的路由使用canActivate保护。

const appRoutes: Routes = [{path: 'login', component: LoginComponent},{path: 'notes', component: NoteListComponent, canActivate: [OktaAuthGuard]},{path: 'notes/:id', component: NoteDetailComponent, canActivate: [OktaAuthGuard]},{path: '', redirectTo: '/notes', pathMatch: 'full'}
];

Import HttpClientModule and RouterModule, configure OktaAuthService and OktaAuthGard as providers, and define OktaAuthInterceptor as an HTTP interceptor.

导入HttpClientModuleRouterModule ,将OktaAuthServiceOktaAuthGard配置为提供程序,并将OktaAuthInterceptor定义为HTTP拦截器。

@NgModule({declarations: [AppComponent,NoteListComponent,NoteDetailComponent,LoginComponent],imports: [BrowserModule,HttpClientModule,RouterModule.forRoot(appRoutes)],providers: [NoteService, OktaAuthService, OktaAuthGuard, {provide: HTTP_INTERCEPTORS,useClass: OktaAuthInterceptor,multi: true}],bootstrap: [AppComponent]
})
export class AppModule {}

Modify LoginComponent (in client/src/app/login/login.component.ts) to show the homepage if the user is logged-in, or the sign-in widget if not.

修改LoginComponent (在client/src/app/login/login.component.ts ),如果用户已登录,则显示主页,否则,则显示登录小部件。

export class LoginComponent implements OnInit {constructor(private oktaService: OktaAuthService,private router: Router) {}ngOnInit() {if (this.oktaService.isAuthenticated()) {this.router.navigate(['/']);} else {this.oktaService.showLogin();}// user authentication listenerthis.oktaService.user$.subscribe(user => {this.router.navigate(['/']);});}
}

In the same directory, update login.component.html to have a div for the sign-in widget to render in.

在同一目录中,更新login.component.html以使div呈现在登录小部件中。

<div id="okta-signin-container"></div>

Modify client/src/app/app.component.html to show the user’s name and add a <router-outlet> for rendering all the routes.

修改client/src/app/app.component.html以显示用户名,并添加<router-outlet>来呈现所有路由。

<h1>{{title}}</h1><div *ngIf="user">Welcome {{user?.name}}!<button (click)="oktaService.logout()">Logout</button>
</div><router-outlet [hidden]="!user"></router-outlet>

Then update client/src/app/app.component.ts so it has a reference to user and oktaService. Notice that this class populates the user variable if the user is already authenticated (for example, they refreshed their browser) or if they sign in with the widget. The this.oktaService.user$ is an Observable that can be subscribed to for changes in the user.

然后更新client/src/app/app.component.ts ,使其具有对useroktaService的引用。 请注意,如果用户已经通过身份验证(例如,他们刷新了浏览器)或使用小部件登录,则该类将填充用户变量。 this.oktaService.user$是一个Observable ,可以预订以进行用户更改。

export class AppComponent implements OnInit {title = 'My Notes';user;constructor(public oktaService: OktaAuthService) {}ngOnInit() {if (this.oktaService.isAuthenticated()) {this.user = this.oktaService.idTokenAsUser;}this.oktaService.user$.subscribe(user => {this.user = user;});}
}

To make the Okta Sign-In Widget look good, add its default CSS files to client/src/styles.

为了使Okta登录小部件看起来不错,请将其默认CSS文件添加到client/src/styles

@import '~https://ok1static.oktacdn.com/assets/js/sdk/okta-signin-widget/2.1.0/css/okta-sign-in.min.css';
@import '~https://ok1static.oktacdn.com/assets/js/sdk/okta-signin-widget/2.1.0/css/okta-theme.css';

After making all these changes, you should be able to fire up http://localhost:4200 (using ng serve) and see a sign in form.

进行所有这些更改之后,您应该能够启动http:// localhost:4200 (使用ng serve )并查看登录表单。

After signing in, you should see the notes list, but no records in it.

登录后,您应该会看到注释列表,但其中没有记录。

To make sure I could add, edit, and delete notes, I wrote a bunch of TypeScript and HTML. I also added Angular Material using yarn add @angular/material @angular/cdk.

为了确保可以添加,编辑和删除注释,我编写了一堆TypeScript和HTML。 我还使用yarn add @angular/material @angular/cdk添加了Angular Material 。

You can see the results in the GitHub repository for this article. In particular, the code in the following files:

您可以在GitHub存储库中查看本文的结果 。 特别是以下文件中的代码:

  • client/src/app/note-list/note-list.component.htmlclient / src / app / note-list / note-list.component.html
  • client/src/app/note-detail/note-detail.component.ts客户端/src/app/note-detail/note-detail.component.ts
  • client/src/app/note-detail/note-detail.component.htmlclient / src / app / note-detail / note-detail.component.html

The final client/src/app/app.module.ts shows all the imports needed for Angular Material. Its stylesheets are referenced in client/src/styles.css. If you copy the code from these files into your project, you’ll have a working notes app with authentication!

最终的client / src / app / app.module.ts显示了Angular Material所需的所有导入。 其样式表在client / src / styles.css中引用。 如果您将这些文件中的代码复制到项目中,那么您将拥有一个带有身份验证功能的工作笔记应用!

The screenshots below show the fruits of my labor.

下面的屏幕截图显示了我的劳动成果。

NOTE: There’s one issue with Okta’s Sign-In Widget I still haven’t fully figured out. Not every time, but everyone once it in a while, it requires me to move my mouse or click on the screen to make the notes list load after logging in. I opened an issue for this and tried the suggested solution, but it doesn’t work 100% of the time.

注意: Okta的登录小部件有一个问题,我仍然没有完全弄清楚。 并非每次都这样,但是每个人都有一段时间了,这需要我移动鼠标或单击屏幕以使登录后加载注释列表。我为此打开了一个问题并尝试了建议的解决方案,但它没有不能100%地工作。

You now know how to build an Angular client with TypeScript, using Okta’s Sign-In Widget for authentication.

现在,您知道如何使用Okta的登录小部件进行身份验证,使用TypeScript构建Angular客户端。

If you’re ambitious, you could even turn the client into a progressive web app (PWA), enabling offline access and faster load times. There are a couple of posts about developing PWAs on the Okta Developer Blog if you’re interested in learning more.

如果您有雄心壮志,甚至可以将客户端变成渐进式Web应用程序(PWA),从而实现脱机访问和更快的加载时间。 如果您想了解更多信息,可以在Okta Developer Blog上找到一些有关开发PWA的帖子。

  • Build Your First Progressive Web Application with Angular and Spring Boot使用Angular和Spring Boot构建第一个渐进式Web应用程序
  • Add Authentication to Your Angular PWA向您的Angular PWA添加身份验证

I also published The Ultimate Guide to Progressive Web Applications on scotch.io earlier this summer.

我还于今年夏天初在scotch.io上发布了《渐进式Web应用程序最终指南》 。

My good buddy Josh Long and I recently hosted a live-coding session where we developed a Spring Boot microservices architecture on the backend and an Angular PWA on the front-end. The code we wrote is very similar to the code in this article. You can check the video out for reference on YouTube.

我的好伙伴Josh Long和我最近主持了一个现场编码会议,在会议上我们在后端开发了Spring Boot微服务架构,在前端开发了Angular PWA。 我们编写的代码与本文中的代码非常相似。 您可以在YouTube上查看视频以供参考。

部署到生产 ( Deploy to Production )

It's cool to see an application running locally, but it's even better to see it up and running in production.

看到应用程序在本地运行很酷,但最好在生产环境中启动并运行它。

My platform of choice for deployment is Cloud Foundry. To get started, you'll need to create an account and install the command line tools.

我选择的部署平台是Cloud Foundry。 首先,您需要创建一个帐户并安装命令行工具。

brew tap cloudfoundry/tap&& brew install cf-cli
cf login -a api.run.pivotal.io

Before deploying, you’ll need to create a couple of files to build the application artifacts and tell Cloud Foundry where everything lives. Create a manifest.yml file in the root directory and specify where the files to upload are. Note that this file expects your apps to be in the same directory, with Spring Boot in a server subdirectory and the Angular app in a client subdirectory.

在部署之前,您需要创建几个文件来构建应用程序工件,并告诉Cloud Foundry所有内容都存放在哪里。 在根目录中创建manifest.yml文件,并指定要上传的文件的位置。 请注意,此文件期望您的应用程序位于同一目录中,其中Spring Boot在server子目录中,而Angular应用程序在client机子目录中。

---
applications:- name: notes-server host: notes-by-kotlinpath: ./server/target/notes-0.0.1-SNAPSHOT.jarenv :FORCE_HTTPS: true- name: notes-clienthost: notes-with-typescriptpath: ./client/dist/env :FORCE_HTTPS: true
`

Then, create a build.sh script that packages the server and client, and replaces the development URLs with the production URLs.

然后,创建一个打包服务器和客户端的build.sh脚本,并用生产URL替换开发URL。

#!/bin/bashstart=`pwd`# set origin for client on server
sed -i -e "s|http://localhost:4200|https://notes-with-typescript.cfapps.io|g" $start/server/src/main/kotlin/com/okta/developer/notes/NotesApplication.ktmvn clean package -f $start/server/pom.xmlcd $start/client
rm -rf dist
# set API URL
sed -i -e "s|http://localhost:8080|https://notes-by-kotlin.cfapps.io|g" $start/client/src/app/shared/note/note.service.ts
# set redirectURI to client URI
sed -i -e "s|http://localhost:4200|https://notes-with-typescript.cfapps.io|g" $start/client/src/app/shared/okta/okta.service.ts
yarn && ng build -prod --aot
touch dist/Staticfilecd $start
cf push# reset and remove changed files
git checkout $start
rm -rf $start/server/src/main/kotlin/com/okta/developer/notes/NotesApplication.kt-e
rm -rf $start/client/src/app/shared/note/note.service.ts-e

After logging into Cloud Foundry, you can run the build script (using sh build.sh) and deploy everything. If you receive an error about the host name being in use, try a different host name in manifest.yml.

登录Cloud Foundry后,您可以运行构建脚本(使用sh build.sh )并部署所有内容。 如果收到有关正在使用的主机名的错误,请在manifest.yml尝试其他主机名。

Run cf apps to see the URLs of the applications you deployed.

运行cf apps以查看您部署的应用程序的URL。

name           requested state   instances   memory   disk   urls
notes-client   started           1/1         1G       1G     notes-with-typescript.cfapps.io
notes-server   started           1/1         1G       1G     notes-by-kotlin.cfapps.io

When you try to log in, you’ll get a CORS error.

尝试登录时,会出现CORS错误。

To fix this, log in to your Okta dashboard once more and navigate to Security > API > Trusted Origins. Add http://notes-with-typescript.cfapps.io as an Origin URL with CORs support. You’ll also need to add https://notes-with-typescript.cfapps.io as a Login Redirect URI to your “My OIDC App”.

要解决此问题,请再次登录Okta仪表板,然后导航至“ 安全性” >“ API” >“ 可信来源” 。 将http://notes-with-typescript.cfapps.io添加为具有CORs支持的原始URL。 您还需要将https://notes-with-typescript.cfapps.io作为登录重定向URI添加到“我的OIDC应用”。

You can now log in and add a note.

您现在可以登录并添加注释。

学到更多 ( Learn More )

Congrats! You're well on your way to becoming a Kotlin and TypeScript developer who understands Spring Boot and Angular. All of the code used in this article is available on GitHub.

恭喜! 您正在成为一名了解Spring Boot和Angular的Kotlin和TypeScript开发人员。 GitHub上提供了本文中使用的所有代码。

If you liked what you learned here, check out the Okta Developer Blog for tons more like it.

如果您喜欢在这里学到的知识,请查看Okta开发人员博客 ,了解更多类似内容。

If you have questions about this code or technologies you want to see in my next post, let me know on Twitter @mraible!

如果您有关于此代码或技术的疑问,希望在我的下一篇文章中看到,请在Twitter @mraible上告诉我!

翻译自: https://scotch.io/tutorials/build-a-secure-notes-application-with-kotlin-typescript-and-okta

kotlin 构建对象

kotlin 构建对象_使用Kotlin,TypeScript和Okta构建安全的Notes应用程序相关推荐

  1. kotlin编写后台_在Kotlin编写图书馆的提示

    kotlin编写后台 by Adam Arold 亚当·阿罗德(Adam Arold) 在Kotlin编写图书馆的提示 (Tips for Writing a Library in Kotlin) W ...

  2. kotlin协程_使Kotlin协程无缝采用的5个技巧

    kotlin协程 After successfully adopting coroutines in my prod project I think it is time to share 5 tip ...

  3. kotlin mysql数据库_在kotlin中使用mysql行级锁

    mysql中的锁 首先需要介绍一下mysql的锁.一般我们使用InnoDB数据库引擎+行级锁,SQL为:SELECT * FROM table where id = 1 for update;.for ...

  4. 使用vue+el构建表格_如何使用Vue和VuePress构建文档系统

    使用vue+el构建表格 介绍 (Introduction) Good documentation is a critical part of a successful project, but a ...

  5. 软件构建设计图_游戏设计如何帮助您构建更好的软件

    软件构建设计图 游戏是一种有趣的媒介. 与电影,文学和戏剧等几乎所有其他流行娱乐方式不同,游戏取决于玩家的选择. 作为游戏设计师,您大部分时间都花在精心设计要呈现给玩家的选择上. 对我们来说,最有趣的 ...

  6. python 构建类_使用Python中的类构建餐厅菜单

    python 构建类 Here, we try to use class in python to build a Menu for the restaurant. The Menu will con ...

  7. mac自带邮箱导出邮件_如何将电子邮件从Mac Mail导出到Notes应用程序

    mac自带邮箱导出邮件 Khamosh Pathak Khamosh Pathak If you use the Mail app regularly, you're used to archivin ...

  8. kotlin半生对象_如何在Kotlin中使用Actor实现对象池

    kotlin半生对象 by osha1 由osha1 如何在Kotlin中使用Actor实现对象池 (How to implement an Object-Pool with an Actor in ...

  9. kotlin 复制对象属性_Kotlin 怎么学 ?遇到过哪些坑?

    本文作者 作者:kotlon 链接: https://www.jianshu.com/p/dcf6cd7c59a3 本文由作者授权发布. 1kotlin 优势 kotlin 在 17 年 google ...

最新文章

  1. 32岁封神!苏炳添博士重磅论文:我怎么跑这么快?
  2. hdu 4460 friend chains spfa 最短路里面的最长路
  3. 教你用Python制作一款自己的杀毒程序
  4. 计算机图形学E1——OpenGL 方中有圆,圆中有方,无穷尽焉
  5. js 时间加减_【JS】550 简单几步让你的 JS 写得更漂亮
  6. 映月城与电子姬服务器维护,映月城与电子姬11月16日更新公告 加强玩家作弊检测增加举报功能...
  7. 2018-07-20
  8. Linux 安装 tomcat
  9. 46.Linux/Unix 系统编程手册(下) -- System V 消息队列
  10. 11.云计算平台(数据科学概论)
  11. 【笔记】《离散数学》第十章 递推方程与生成函数
  12. 【2020年高被引学者】 车万翔 哈尔滨工业大学
  13. win10桌面管理文件收纳_win10系统关闭桌面文件收纳盒的详细方法介绍
  14. 切图常说的@1X@2X@3X是什么意思?
  15. 金融行业市场策划案例(共12份)
  16. 我只会SQL,到底能不能找到工作?
  17. ABP微服务系列学习-搭建自己的微服务结构(三)
  18. DB-Engines发布了2017年9月数据库排名。
  19. ReviewBoard安装配置
  20. 【mysql】获取指定日期是当年第几周,指定日期所在周的开始和结束日期

热门文章

  1. java oshi获取服务器各种信息
  2. 图论——深度优先搜索
  3. Linux系统安装MySQL(rmp方式)
  4. 中国石油大学《 管理心理学(行政管理专业禁选)》在线考试
  5. 我的世界神级种子Java_《我的世界》老玩家珍藏的10个“神级”种子,你一定没去过系列!...
  6. 6:基于Excel的数据展示(生成报表)
  7. 南京观海微电子---残影原理和调试方法简介
  8. VB+Access设计图书管理系统
  9. 人物专访 | 《复仇者联盟》特效工作室的小姐姐告诉你如何建立自己的工作室
  10. 消费积分与顾客忠诚计划