序言:本文主要介紹了使用 Ionic 和 Cordova 開發混合應用時如何添加用戶身份認證。教程簡易,對於 Ionic 入門學習有必定幫助。由於文章是去年發表,因此教程內關於 Okta 的一些使用步驟不太準確,可是經過 Okta 的官網也能夠找到對應的內容。另外,使用 npm 安裝 Ionic starter 模板可能會有安裝失敗的狀況,建議不要在這方面浪費太多時間,能夠直接在 Ionic 的 GitHub 倉庫 中下載 starter 模板。javascript
原文:How to Sprinkle ReactJS into an Existing Web Applicationcss
譯者:nzbinhtml
使用 Okta 和 OpenID Connect (OIDC),能夠很輕鬆的在 Ionic 應用中添加身份認證,徹底不須要本身實現。 OIDC 容許你直接使用 Okta Platform API 進行認證,本文的目的就是告訴你如何在一個 Ionic 應用中使用這些 API。我將演示如何使用 OIDC 重定向、Okta 的 Auth SDK 以及基於 Cordova 內嵌瀏覽器的 OAuth 進行登陸; 因爲功能還在開發中,因此省略了用戶註冊。java
Ionic 是一個用於開發原生及先進 web 應用的開源的移動端 SDK。它使用 Angular 和 Apache Cordova ,能夠用 HTML、CSS、和 JavaScript 來開發移動應用。Apache Cordova 將 HTML 代碼嵌入到一個設備上的原生 WebView 中, 經過外部功能接口來訪問原生資源。你可能據說過 PhoneGap —— 這是 Adobe Cordova 的商業版本。node
Cordova 和 PhoneGap 容許你使用一套代碼開發多個平臺的應用 (好比 Android 和 iOS) 。除此以外,應用程序和原生程序相差無盡而且和原生體驗同樣好。若是你須要開發原生功能,使用 web 技術是沒法實現的,可是有些原生插件能夠實現。 Ionic Native 是這些插件的精選集。android
我第一次使用 Ionic 是在 2013 年末。當時我作的項目是開發一款原生應用,可是打算使用 HTML 來開發適配多個屏幕的應用,這樣 web 開發者也能夠參與開發。我在 2014 年的三月寫了個人經歷。我喜歡使用 Ionic,我發現使用 Ionic 移植現有的應用程序更多的就是修改 HTML 和調整 CSS。ios
Ionic 2 在 一月份發佈, 可使用 Angular 開發 Ionic 應用。 Ionic 3 在 四月份發佈,容許使用 Angular 4 進行開發。git
注意: "Angular" 是 Angular 2+ 的通用名稱。AngularJS 是 1.x 版本的名稱。之因此用 Angular 命名是由於在 2017 年的三月發佈了 Angular 4 。能夠查看 Branding Guidelines for Angular and AngularJS 瞭解更多信息。angularjs
本文會演示如何建立一個簡單的 Ionic 應用以及如何添加用戶身份認證。大多數的應用都須要身份認證,這樣才能知道用戶是誰。一旦 app 知道你的身份,它就能夠保存你的信息及個性化的功能。github
爲了設置 Ionic 的開發環境,須要完成如下幾步:
npm install -g cordova ionic
在 terminal 窗口中,使用如下命令建立一個新的應用程序:
ionic start ionic-auth
命令行會提示選擇一個 starter 項目而且能夠選擇是否將應用鏈接到 Ionic Dashboard。對於本教程,選擇 tabs starter 項目,不須要將項目鏈接到 Ionic Dashboard。
項目建立須要花費一到兩分鐘,這取決於你的網絡鏈接速度。運行如下命令來打開你的 Ionic 應用。
cd ionic-auth ionic serve
這個命令默認打開瀏覽器的 http://localhost:8100。你可使用 Chrome 的設備模式查看應用程序在 iPhone 6 中的效果。
使用 Ionic serve
命令的特色是它會在瀏覽器中顯示編譯錯誤,而不是(有時會隱藏)在開發控制檯。好比,給 app.component.ts
組件中的 rootPage
變量設置一個非法類型,你將看到如下錯誤。
Ionic Cloud 提供了免費的 Auth 服務。它容許使用郵箱及密碼驗證身份,也可使用社交提供商好比 Facebook、Google 和 Twitter 登陸。你可使用 @ionic/cloud-angular
依賴中提供的類建立身份認證。它也支持 自定義身份認證,可是 "須要你本身的服務器處理身份認證"。
目前尚未太多關於這方面的教程,不過從去年開始有了一些。
你可能注意到全部的教程都須要不少的代碼。另外,關於如何在後端的 Auth 服務中驗證用戶身份的文檔也很少。
OpenID Connect (OIDC) 基於 OAuth 2.0 協議。它容許客戶端驗證用戶的身份並得到他們的基本配置文件信息。爲了將 Okta 的身份認證平臺整合到用戶身份認證中,須要如下步驟:
http://localhost:8100
做爲重定向的 URI 並點擊 Finish。你會看到如下設置信息:爲了建立身份認證的登陸頁,先建立 src/pages/login.ts
和 src/pages/login.html
。在 login.html
中,添加一個具備 username 和 password 的表單。
<ion-header> <ion-navbar> <ion-title> Login </ion-title> </ion-navbar> </ion-header> <ion-content padding> <form #loginForm="ngForm" (ngSubmit)="login()" autocomplete="off"> <ion-row> <ion-col> <ion-list inset> <ion-item> <ion-input placeholder="Email" name="username" id="loginField" type="text" required [(ngModel)]="username" #email></ion-input> </ion-item> <ion-item> <ion-input placeholder="Password" name="password" id="passwordField" type="password" required [(ngModel)]="password"></ion-input> </ion-item> </ion-list> </ion-col> </ion-row> <ion-row> <ion-col> <div *ngIf="error" class="alert alert-danger">{{error}}</div> <button ion-button class="submit-btn" full type="submit" [disabled]="!loginForm.form.valid">Login </button> </ion-col> </ion-row> </form> </ion-content>
你能夠利用幾個開源庫來完成實際的身份驗證。第一個是 Manfred Steyer's angular-oauth2-oidc. 這個庫能夠很容易的與 identity tokens 和 access tokens 交互。第二個是 Okta Auth SDK。因爲 OIDC 和 OAuth 不是身份認證協議,因此這是使用 JavaScript 完成身份驗證所必需的,沒必要重定向到 Okta 。
使用 npm 安裝 angular-oauth2-oidc
npm install angular-oauth2-oidc --save
Okta Auth SDK 目前不支持 TypeScript,能夠將如下代碼添加到 src/index.html
底部。
<script src="https://ok1static.oktacdn.com/assets/js/sdk/okta-auth-js/1.5.0/OktaAuth.min.js"></script>
在 src/pages/login/login.ts
中, 添加 LoginPage
類的基本結構,在構造器函數中使用 OAuthService
(來自於 angular-oauth2-oidc) 配置了 OIDC 的設置。 你須要使用 Okta OIDC 設置中的 Client ID 替換 "[client-id]" 以及你帳戶的當前 URI 替換 "[dev-id]"。
import { Component, ViewChild } from '@angular/core'; import { NavController } from 'ionic-angular'; import { OAuthService } from 'angular-oauth2-oidc'; declare const OktaAuth: any; @Component({ selector: 'page-login', templateUrl: 'login.html' }) export class LoginPage { @ViewChild('email') email: any; private username: string; private password: string; private error: string; constructor(private navCtrl: NavController, private oauthService: OAuthService) { oauthService.redirectUri = window.location.origin; oauthService.clientId = '[client-id]'; oauthService.scope = 'openid profile email'; oauthService.oidc = true; oauthService.issuer = 'https://dev-[dev-id].oktapreview.com'; } ionViewDidLoad(): void { setTimeout(() => { this.email.setFocus(); }, 500); } }
修改 src/app/app.component.ts
驗證用戶是否登陸。若是沒有,將 LoginPage
設置爲 rootPage。
import { OAuthService } from 'angular-oauth2-oidc'; import { LoginPage } from '../pages/login/login'; @Component({ templateUrl: 'app.html' }) export class MyApp { rootPage: any = TabsPage; constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen, oauthService: OAuthService) { if (oauthService.hasValidIdToken()) { this.rootPage = TabsPage; } else { this.rootPage = LoginPage; } platform.ready().then(() => { statusBar.styleDefault(); splashScreen.hide(); }); } }
更新 src/app/app.module.ts
,在 declarations
和 entryComponents
中添加 LoginPage
。你也要將 OAuthService
添加到 providers
中。
@NgModule({ declarations: [ ... LoginPage ], ... entryComponents: [ ... LoginPage ], providers: [ OAuthService, ... ] })
運行 ionic serve
,確認 LoginPage
在 app 首次加載後能夠展現出來。app 加載時會有如下報錯:
No provider for Http!
出現這個錯誤是由於 OAuthService
須要依賴 Angular 的 Http
模塊,可是尚未將該模塊導入到項目中。在 src/app/app.module.ts
中導入 HttpModule
。
import { HttpModule } from '@angular/http'; @NgModule({ ... imports: [ BrowserModule, HttpModule, IonicModule.forRoot(MyApp) ], ... })
如今登陸頁已經展現出來了。你可使用 Chrome 的設備模式查看在 iPhone 6 上的效果。
爲了解決缺乏 TypeScript 支持的問題,你須要在 src/app/pages/login/login.ts
的頂部添加如下代碼。
declare const OktaAuth: any;
TIP: 要了解更多關於在 TypeScript 項目引用外部 JavaScript 庫的知識,能夠閱讀 Nic Raboy 寫的關於這方面的文章。
在 src/app/pages/login/login.ts
中添加一個 login()
方法,它使用 Okta Auth SDK 進行: 1) 登陸; 2) 將 session token 轉換成 identity 和 access token。 一個 ID token 相似於身份證,它是標準的 JWT 格式,由 OpenID 提供者簽名。Access tokens 是 OAuth 規範的一部分。一個 access token 能夠是一個 JWT。它們用於訪問被保護的資源,一般是在發送請求時將它們添加到 Authentication
請求頭中。
login(): void { this.oauthService.createAndSaveNonce().then(nonce => { const authClient = new OktaAuth({ clientId: this.oauthService.clientId, redirectUri: this.oauthService.redirectUri, url: this.oauthService.issuer }); authClient.signIn({ username: this.username, password: this.password }).then((response) => { if (response.status === 'SUCCESS') { authClient.token.getWithoutPrompt({ nonce: nonce, responseType: ['id_token', 'token'], sessionToken: response.sessionToken, scopes: this.oauthService.scope.split(' ') }) .then((tokens) => { // oauthService.processIdToken doesn't set an access token // set it manually so oauthService.authorizationHeader() works localStorage.setItem('access_token', tokens[1].accessToken); this.oauthService.processIdToken(tokens[0].idToken, tokens[1].accessToken); this.navCtrl.push(TabsPage); }) .catch(error => console.error(error)); } else { throw new Error('We cannot handle the ' + response.status + ' status'); } }).fail((error) => { console.error(error); this.error = error.message; }); }); }
經過 identity token 你能夠了解用戶的更多信息。經過 access token 你能夠訪問須要 Bearer token 的受保護的 API。好比, 在 在 Angular PWA 中添加身份認證中,有一個 BeerService
,它用於在發送 API 請求時攜帶 access token 。
import { Injectable } from '@angular/core'; import { Http, Response, Headers, RequestOptions } from '@angular/http'; import 'rxjs/add/operator/map'; import { Observable } from 'rxjs'; import { OAuthService } from 'angular-oauth2-oidc'; @Injectable() export class BeerService { constructor(private http: Http, private oauthService: OAuthService) { } getAll(): Observable<any> { const headers: Headers = new Headers(); headers.append('Authorization', this.oauthService.authorizationHeader()); let options = new RequestOptions({ headers: headers }); return this.http.get('http://localhost:8080/good-beers', options) .map((response: Response) => response.json()); } }
您能夠(可選)在表單上方添加圖標來美化登陸頁。下載 這張圖片,將它拷貝到 src/assets/image/okta.png
,在 login.html
的 <form>
標籤中添加如下代碼。
<ion-row> <ion-col text-center> <img src="assets/image/okta.png" width="300"> </ion-col> </ion-row>
當你嘗試使用 Okta 的用戶證書登陸應用程序,你將在瀏覽器的控制檯看到跨域報錯。
XMLHttpRequest cannot load https://dev-158606.oktapreview.com/api/v1/authn. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8100' is therefore not allowed access.
爲了修復這一問題,在 Okta 修改 Trusted Origins (在 Security > API 下面), 將你的 client's URL 添加進去 (好比 http://localhost:8100
)。檢查 CORS 和重定向的 origin 類型。
如今登陸能夠正常工做了,可是 UI 界面並無提示。在首頁的右上角添加一個 "Logout" 按鈕。用如下 HTML 替換 src/pages/home/home.html
中的 <ion-header>
。
<ion-header> <ion-navbar> <ion-title>Home</ion-title> <ion-buttons end> <button ion-button (click)="logout()"> Logout </button> </ion-buttons> </ion-navbar> </ion-header>
在 src/pages/home/home.ts
中,添加一個 logout()
方法, 用於在 identity token 中獲取姓名及 claims 。ID token 中的 claims 是關於頒發者、用戶、目標受衆、過時時間及頒發時間的信息。你能夠閱讀 OIDC 規範中的標準 claims。
import { Component } from '@angular/core'; import { NavController } from 'ionic-angular'; import { LoginPage } from '../login/login'; import { OAuthService } from 'angular-oauth2-oidc'; @Component({ selector: 'page-home', templateUrl: 'home.html' }) export class HomePage { constructor(public navCtrl: NavController, public oauthService: OAuthService) { } logout() { this.oauthService.logOut(); this.navCtrl.setRoot(LoginPage); this.navCtrl.popToRoot(); } get givenName() { const claims = this.oauthService.getIdentityClaims(); if (!claims) { return null; } return claims.name; } get claims() { return this.oauthService.getIdentityClaims(); } }
爲了在 home 標籤頁上展現信息,將如下 HTML 添加到 src/app/home/home.html
文件的第二段以後。
<div *ngIf="givenName"> <hr> <p>You are logged in as: <b>{{ givenName }}</b></p> <div class="claims"> <strong>Claims from Identity Token JWT:</strong> <pre>{{claims | json}}</pre> </div> </div>
更新 src/app/home/home.scss
,添加一些 CSS 讓原始的 JSON 看起來舒服一點。
page-home { .claims { pre { color: green; } } pre { border: 1px solid silver; background: #eee; padding: 10px; } }
如今登陸以後你會看到你的姓名及聲明信息。
你能夠退出以後看一下帶標識的登陸頁。
注意: 你可能注意到退出以後標籤頁並無消失。我正在查找 沒有正常工做 的緣由。
使用 Ionic 在瀏覽器中開發移動應用是很是酷的事情。很高興你能看到本身的勞動成果以及優秀的手機應用。可是它的外觀和表現還不是原生應用。
爲了查看應用程序在不一樣設備上的效果,你能夠運行 ionic serve --lab
。--lab
標識會在瀏覽器中打開一個頁面讓你查看在不一樣設備中的效果。
LoginPage
在加載時會自動聚焦到 email
輸入框。爲了自動激活鍵盤,你須要告訴 Cordova 沒有用戶交互的狀況下顯示鍵盤是能夠的。你能夠在根路徑的 config.xml
中添加如下代碼。
<preference name="KeyboardDisplayRequiresUserAction" value="false" />
爲了模擬或者部署到 iOS 設備上,你須要一個 Mac 以及一個新安裝的 Xcode。若是你喜歡在 Windows 中建立 iOS 應用,Ionic 提供了一個 Ionic Package 服務。
確保打開 Xcode 完成安裝 ,而後運行 ionic cordova emulate ios
在模擬器中打開應用。
可能會提示你安裝 @ionic/cli-plugin-cordova
插件。當出現提示時輸入 "y",按回車。
TIP: 我發如今模擬器中運行應用程序時的最大問題是鍵盤很難彈出。爲了解決這一問題,當我須要在輸入框輸入文本時,我使用 Hardware > Keyboard > Toggle Software Keyboard 。
若是你在登陸頁輸入憑證,可能什麼也不會發生。打開 Safari 轉到 Develop > Simulator > MyApp / Login,你會看到控制檯有一條錯誤信息。若是你看不到開發菜單,從新執行 這篇文章 中的方法使其生效。
若是打開 Network 標籤,你會看到只發送了一條請求 (to /authn
),它和在瀏覽器中發送的兩條請求 (to /authn
and /authorize
) 有所不一樣。
我相信使用 Cordova 打包 app 以後並不會正常工做,由於經過內嵌的 iframe 向服務端發送請求,而後使用 postMessage 將結果返回當前窗口。Ionic/Cordova 彷佛並不支持這種方式。爲了解決這個問題,你可使用 Cordova 提供的 in-app 瀏覽器直接與 Okta 的 OAuth 服務通訊。Nic Raboy 演示了在 Facebook 中的操做方法,他在 Ionic 2 移動 App 中使用了 OAuth 2.0 服務。
使用如下命令安裝 Cordova In-App Browser plugin :
ionic cordova plugin add cordova-plugin-inappbrowser
打開 src/app/pages/login/login.html
,用一個 <div>
包裹 <form>
,爲了只在瀏覽器中運行時顯示登陸表單。添加一個新的 <div>
,它會在模擬器或設備上運行時顯示。
<ion-content padding> <ion-row> <!-- optional logo --> </ion-row> <div showWhen="core"> <form> ... </form> </div> <div hideWhen="core"> <button ion-button full (click)="redirectLogin()">Login with Okta</button> </div> </ion-content>
打開 src/pages/login/login.ts
,在 imports 下面添加一個 window
的引用。
declare const window: any;
爲了更容易的使用 OAuth 登陸,能夠添加如下方法。
redirectLogin() { this.oktaLogin().then(success => { localStorage.setItem('access_token', success.access_token); this.oauthService.processIdToken(success.id_token, success.access_token); this.navCtrl.push(TabsPage); }, (error) => { this.error = error; }); } oktaLogin(): Promise<any> { return this.oauthService.createAndSaveNonce().then(nonce => { let state: string = Math.floor(Math.random() * 1000000000).toString(); if (window.crypto) { const array = new Uint32Array(1); window.crypto.getRandomValues(array); state = array.join().toString(); } return new Promise((resolve, reject) => { const oauthUrl = this.buildOAuthUrl(state, nonce); const browser = window.cordova.InAppBrowser.open(oauthUrl, '_blank', 'location=no,clearsessioncache=yes,clearcache=yes'); browser.addEventListener('loadstart', (event) => { if ((event.url).indexOf('http://localhost:8100') === 0) { browser.removeEventListener('exit', () => {}); browser.close(); const responseParameters = ((event.url).split('#')[1]).split('&'); const parsedResponse = {}; for (let i = 0; i < responseParameters.length; i++) { parsedResponse[responseParameters[i].split('=')[0]] = responseParameters[i].split('=')[1]; } const defaultError = 'Problem authenticating with Okta'; if (parsedResponse['state'] !== state) { reject(defaultError); } else if (parsedResponse['access_token'] !== undefined && parsedResponse['access_token'] !== null) { resolve(parsedResponse); } else { reject(defaultError); } } }); browser.addEventListener('exit', function (event) { reject('The Okta sign in flow was canceled'); }); }); }); } buildOAuthUrl(state, nonce): string { return this.oauthService.issuer + '/oauth2/v1/authorize?' + 'client_id=' + this.oauthService.clientId + '&' + 'redirect_uri=' + this.oauthService.redirectUri + '&' + 'response_type=id_token%20token&' + 'scope=' + encodeURI(this.oauthService.scope) + '&' + 'state=' + state + '&nonce=' + nonce; }
把在構造器中設置的 redirectUri
替換成硬編碼 http://localhost:8100
。若是省略這一步,當 app 在設備上運行時, window.location.origin
會跳轉到 file://
。爲了將它設置成已知的 URL,咱們能夠經過 in-app browser 的 "loadstart" 事件查找它。
constructor(private navCtrl: NavController, private oauthService: OAuthService) { oauthService.redirectUri = 'http://localhost:8100'; ... }
更改以後,須要將 app 從新部署到手機上。
ionic cordova emulate ios
如今能夠點擊 "Login with Okta" 按鈕,而後輸入合法的憑證進行登陸。
使用這項技術的好處就是 Okta 的登陸頁具備「記住我」和「忘記密碼」的功能,因此不須要本身編寫代碼。
爲了將 app 部署到 iPhone,首先將手機插到電腦上。而後運行如下命令安裝 ios-deploy、構建 app 並在你的設備上運行。
npm install -g ios-deploy ionic cordova run ios
若是你以前沒有爲應用程序設置代碼簽名,則此命令可能會失敗。
Signing for "MyApp" requires a development team. Select a development team in the project editor. Code signing is required for product type 'Application' in SDK 'iOS 10.3'
在 Xcode 中打開你的項目,運行如下命令。
open platforms/ios/MyApp.xcodeproj
Ionic's 開發文檔 有解決這一問題的說明。
選擇你的手機做爲 Xcode 的目標,而後點擊 play 按鈕運行 app。若是你是第一次作,Xcode 可能會加載一段時間,上方會顯示一條 "Processing symbol files" 的信息。
只要你已經設置了你的手機、電腦以及 Apple ID,你就能夠打開應用並登陸。如下是在個人手機上的展現效果。
爲了模擬或者部署到 Android 設備上,你首先要安裝 Android Studio。在安裝過程當中,它會提示你將 Android SDK 安裝到哪裏。將這個路徑設置爲 ANDROID_HOME 的環境變量。在 Mac 上,it should be ~/Library/Android/sdk/
。
若是你已經安裝了Android Studio,請確保打開它以完成安裝。
爲了部署到 Android 模擬器,運行 ionic cordova emulate android
。這個命令將安裝 Android 支持並打印關於如何建立模擬圖像的說明。
Error: No emulator images (avds) found. 1. Download desired System Image by running: /Users/mraible/Library/Android/sdk/tools/android sdk 2. Create an AVD by running: /Users/mraible/Library/Android/sdk/tools/android avd HINT: For a faster emulator, use an Intel System Image and install the HAXM device driver
運行第一條建議並下載您想要的系統映像。而後運行第二個命令並用如下設置建立一個 AVD(Android 虛擬設備):
AVD Name: TestPhone Device: Nexus 5 Target: Android 7.1.1 CPU/ABI: Google APIs Intel Axom (x86_64) Skin: Skin with dynamic hardware controls
警告: 這些設置不適用於 Mac 上的 Android Studio 2.3.2 版本。當你嘗試運行第一條命令時,它會顯示如下內容:
************************************************************************* The "android" command is deprecated. For manual SDK, AVD, and project management, please use Android Studio. For command-line tools, use tools/bin/sdkmanager and tools/bin/avdmanager *************************************************************************
爲了解決這個問題,打開 Android Studio,選擇 "Open an existing Android Studio project",而後選擇 ionic-auth/platforms/android
的路徑。若是提示升級,選擇 "OK",而後繼續建立一個新的 AVD ,和 Android Studio 文檔描述的那樣.
執行完這些步驟以後,你能夠運行 ionic cordova emulate android
查看運行在 AVD 中的 app。
注意: 若是應用程序顯示錯誤 "鏈接服務器失敗 (file:///android/www/index.html
)",在 config.xml
中添加如下代碼。這行代碼將默認超時時間設置爲 60 秒 (默認 20)。感謝 Stack Overflow 社區 對此問題的解答。
<preference name="loadUrlTimeoutValue" value="60000"/>
Ionic 支持建立 progressive web apps (PWAs)。這意味着你能夠將 Ionic app 部署成 web app (不是移動端 app) ,它能夠在離線的 支持 service workers 的瀏覽器 中運行。
想要了解如何使用 service workers 並把 app 轉換成 PWA ,能夠閱讀 如何使用 Ionic 和 Spring Boot 開發移動應用 的 PWAs 部分 。PWA 是能夠安裝在系統中的 web 應用程序。它能夠在離線狀況下工做,使用的是你最後一次與 app 交互的數據緩存。添加 PWA 功能可讓 app 加載更快,提供更好的用戶體驗。想要了解更多關於 PWA 的知識,能夠閱讀 The Ultimate Guide to Progressive Web Applications.
我但願你喜歡這篇關於 Ionic、Angular 及 Okta 的教程。我喜歡 Ionic 是由於它能夠將你的 web 開發技能提高一個檔次,而且它能夠快速建立仿原生的移動應用。
你能夠在 GitHub 上查看本教程的完整代碼。若是你有問題,能夠經過 Twitter @mraible 或者在 Okta's Developer Forums 上聯繫我。
想要了解更多關於 Ionic、Angular 或者 Okta 的知識,能夠查看如下資源: