登録
improve this page | report issue概説
このサンプルは、カスタムの登録プロセスとステップアップ許可を例示します。 この一回限りの登録プロセスの間に、ユーザーはユーザー名とパスワードを入力し、PIN コードを定義する必要があります。
前提条件: ExternalizableSecurityCheck およびステップアップのチュートリアルをお読みください。
ジャンプ先:
アプリケーション・フロー
- アプリケーションが初めて開始されると (登録の前)、アプリケーションは、「公開データの照会 (Get public data)」と「登録 (Enroll)」の 2 つのボタンがある UI を表示します。
- ユーザーが「登録 (Enroll)」ボタンをタップして登録を開始すると、ログイン・フォームのプロンプトが出され、その後、PIN コードを設定するように要求されます。
- ユーザーが正常に登録されると、UI には、「公開データの照会 (Get public data)」、「残高照会 (Get balance)」、「取引照会 (Get transactions)」、および「ログアウト」の 4 つのボタンが表示されます。 ユーザーは、PIN コードを入力しなくとも 4 つすべてのボタンを利用できます。
- アプリケーションが 2 回目に起動されたとき (登録の後)、UI には引き続き 4 つすべてのボタンが含まれています。 ただし、ユーザーが「取引照会 (Get transactions)」ボタンをクリックすると、ユーザーは PIN コードの入力を要求されます。
PIN コードの入力に 3 回失敗すると、ユーザーは再度、ユーザー名とパスワードを使用して認証を受け、PIN コードを再設定するように要求されます。
永続属性へのデータの保管
保護データを PersistentAttributes
オブジェクトに保存するように選択できます。このオブジェクトは、登録済みクライアントのカスタム属性用のコンテナーです。 オブジェクトにはセキュリティー検査クラスまたはアダプター・リソース・クラスのいずれかからアクセスできます。
提供されるサンプル・アプリケーションでは、PersistentAttributes
オブジェクトをアダプター・リソース・クラスで使用して、PIN コードを保管しています。
-
setPinCode リソースは、pinCode 属性を追加し、
AdapterSecurityContext.storeClientRegistrationData()
メソッドを呼び出して変更を保管します。@POST @OAuthSecurity(scope = "setPinCode") @Path("/setPinCode/{pinCode}") public Response setPinCode(@PathParam("pinCode") String pinCode){ ClientData clientData = adapterSecurityContext.getClientRegistrationData(); clientData.getProtectedAttributes().put("pinCode", pinCode); adapterSecurityContext.storeClientRegistrationData(clientData); return Response.ok().build(); }
ここで、
users
にはEnrollmentUserLogin
というキーが存在し、このキー自体にAuthenticatedUser
オブジェクトが含まれています。 -
unenroll リソースは、pinCode 属性を削除し、
AdapterSecurityContext.storeClientRegistrationData()
メソッドを呼び出して変更を保管します。@DELETE @OAuthSecurity(scope = "unenroll") @Path("/unenroll") public Response unenroll(){ ClientData clientData = adapterSecurityContext.getClientRegistrationData(); if (clientData.getProtectedAttributes().get("pinCode") != null){ clientData.getProtectedAttributes().delete("pinCode"); adapterSecurityContext.storeClientRegistrationData(clientData); } return Response.ok().build(); }
セキュリティー検査
登録のサンプルには 3 つのセキュリティー検査が含まれています。
EnrollmentUserLogin
EnrollmentUserLogin
セキュリティー検査は、setPinCode リソースを保護して、認証済みユーザーのみが PIN コードを設定できるようにします。 このセキュリティー検査は、すぐに期限切れになるように作られており、「初回に起動」された期間中のみ有効であるように意図されています。 このセキュリティー検査は、追加の isLoggedIn
メソッドと getRegisteredUser
メソッドがある点を除けば、UserAuthenticationSecurityCheck の実装のチュートリアルで説明している UserLogin
セキュリティー検査と同じものです。
isLoggedIn
メソッドは、セキュリティー検査の状態が SUCCESS と等しい場合には true
を返し、それ以外の場合には false
を返します。
getRegisteredUser
メソッドは、認証済みユーザーを返します。
public boolean isLoggedIn(){
return getState().equals(STATE_SUCCESS);
}
public AuthenticatedUser getRegisteredUser() {
return registrationContext.getRegisteredUser();
}
EnrollmentPinCode
EnrollmentPinCode
セキュリティー検査は、「取引照会 (Get transactions)」リソースを保護します。いくつかの違いを除いて、CredentialsValidationSecurityCheck の実装のチュートリアルで説明している PinCodeAttempts
セキュリティー検査に似ています。
このチュートリアルの例の場合、EnrollmentPinCode
は、EnrollmentUserLogin
に依存しています。 EnrollmentUserLogin
へのログインが成功した後でのみ、ユーザーは PIN コードの入力を求められます。
@SecurityCheckReference
private transient EnrollmentUserLogin userLogin;
アプリケーションが始めて開始され、ユーザーが正常に登録されると、ユーザーは、設定した PIN コードを入力しなくとも「取引照会 (Get transactions)」リソースにアクセスできなければなりません。 その目的のために、authorize
メソッドは EnrollmentUserLogin.isLoggedIn
メソッドを使用して、ユーザーがログイン状態であるかどうかをチェックします。 これは、EnrollmentUserLogin
の有効期限が切れていない限り、ユーザーは「取引照会 (Get transactions)」にアクセスできることを意味します。
@Override
public void authorize(Set<String> scope, Map<String, Object> credentials, HttpServletRequest request, AuthorizationResponse response) {
if (userLogin.isLoggedIn()){
setState(STATE_SUCCESS);
response.addSuccess(scope, userLogin.getExpiresAt(), getName());
}
}
ユーザーが 3 回試行しても PIN コードの入力に失敗すると、チュートリアルの設計では、ユーザー名とパスワードで認証を受け、PIN コードを再設定するようにユーザーにプロンプトが出される前に pinCode 属性が削除されます。
@Override
public void authorize(Set<String> scope, Map<String, Object> credentials, HttpServletRequest request, AuthorizationResponse response) {
PersistentAttributes attributes = registrationContext.getRegisteredProtectedAttributes();
if (userLogin.isLoggedIn()){
setState(STATE_SUCCESS);
response.addSuccess(scope, userLogin.getExpiresAt(), getName());
} else {
super.authorize(scope, credentials, request, response);
if (getState().equals(STATE_BLOCKED)){
attributes.delete("pinCode");
}
}
}
validateCredentials
メソッドは、PinCodeAttempts
セキュリティー検査内のものと同様ですが、ここでは、保管されている pinCode 属性に対して資格情報が比較される点が異なります。
@Override
protected boolean validateCredentials(Map<String, Object> credentials) {
PersistentAttributes attributes = registrationContext.getRegisteredProtectedAttributes();
if(credentials!=null && credentials.containsKey("pin")){
String pinCode = credentials.get("pin").toString();
if(pinCode.equals(attributes.get("pinCode"))){
errorMsg = null;
return true;
}
else {
errorMsg = "The pin code is not valid. Hint: " + attributes.get("pinCode");
}
}
else{
errorMsg = "The pin code was not provided.";
}
//In any other case, credentials are not valid
return false;
}
IsEnrolled
IsEnrolled
セキュリティー検査は以下を保護します。
- getBalance リソース。登録済みユーザーのみが残高を確認できるようにします。
- transactions リソース。登録済みユーザーのみが取り引きを照会できるようにします。
- unenroll リソース。pinCode があらかじめ設定されている場合にのみ、これを削除できるようにします。
セキュリティー検査の作成
Java アダプターを作成し、ExternalizableSecurityCheck
を継承する IsEnrolled
という名前の Java クラスを追加します。
public class IsEnrolled extends ExternalizableSecurityCheck{
protected void initStateDurations(Map<String, Integer> durations) {}
public void authorize(Set<String> scope, Map<String, Object> credentials, HttpServletRequest request, AuthorizationResponse response) {}
public void introspect(Set<String> scope, IntrospectionResponse response) {}
}
IsEnrolledConfig 構成クラス
ExternalizableSecurityCheckConfig
を継承する IsEnrolledConfig
構成クラスを作成します。
public class IsEnrolledConfig extends ExternalizableSecurityCheckConfig {
public int successStateExpirationSec;
public IsEnrolledConfig(Properties properties) {
super(properties);
successStateExpirationSec = getIntProperty("expirationInSec", properties, 8000);
}
}
createConfiguration
メソッドを IsEnrolled
クラスに追加します。
public class IsEnrolled extends ExternalizableSecurityCheck{
@Override
public SecurityCheckConfiguration createConfiguration(Properties properties) {
return new IsEnrolledConfig(properties);
}
}
initStateDurations メソッド
SUCCESS 状態を維持する期間を successStateExpirationSec
に設定します。
@Override
protected void initStateDurations(Map<String, Integer> durations) {
durations.put (SUCCESS_STATE, ((IsEnrolledConfig) config).successStateExpirationSec);
}
authorize メソッド
コード・サンプルは、ユーザーが登録されているかどうかを単純にチェックし、その結果に応じて成功または失敗を返します。
public void authorize(Set<String> scope, Map<String, Object> credentials, HttpServletRequest request, AuthorizationResponse response) {
PersistentAttributes attributes = registrationContext.getRegisteredProtectedAttributes();
if (attributes.get("pinCode") != null){
setState(SUCCESS_STATE);
response.addSuccess(scope, getExpiresAt(), this.getName());
} else {
setState(STATE_EXPIRED);
Map <String, Object> failure = new HashMap<String, Object>();
failure.put("failure", "User is not enrolled");
response.addFailure(getName(), failure);
}
}
-
pinCode
属性が存在する場合は、以下が実行されます。 setState
メソッドを使用して状態を SUCCESS に設定します。-
addSuccess
メソッドを使用して応答オブジェクトに成功を追加します。 -
pinCode
属性が存在しない場合は、以下が実行されます。 setState
メソッドを使用して状態を EXPIRED に設定します。addFailure
メソッドを使用して応答オブジェクトに失敗を追加します。
IsEnrolled
セキュリティー検査は、EnrollmentUserLogin
に依存しています。
@SecurityCheckReference
private transient EnrollmentUserLogin userLogin;
以下のコードを追加することで、アクティブ・ユーザーを設定します。
public void authorize(Set<String> scope, Map<String, Object> credentials, HttpServletRequest request, AuthorizationResponse response) {
PersistentAttributes attributes = registrationContext.getRegisteredProtectedAttributes();
if (attributes.get("pinCode") != null){
// Is there a user currently active?
if (!userLogin.isLoggedIn()){
// If not, set one here.
authorizationContext.setActiveUser(userLogin.getRegisteredUser());
}
setState(SUCCESS_STATE);
response.addSuccess(scope, getExpiresAt(), this.getName());
} else {
setState(STATE_EXPIRED);
Map <String, Object> failure = new HashMap<String, Object>();
failure.put("failure", "User is not enrolled");
response.addFailure(getName(), failure);
}
}
その後、transactions
リソースによって、表示名を提供するために現在の AuthenticatedUser
オブジェクトが取得されます。
@GET
@Produces(MediaType.TEXT_PLAIN)
@OAuthSecurity(scope = "transactions")
@Path("/transactions")
public String getTransactions(){
AuthenticatedUser currentUser = securityContext.getAuthenticatedUser();
return "Transactions for " + currentUser.getDisplayName() + ":\n{'date':'12/01/2016', 'amount':'19938.80'}";
}
securityContext
について詳しくは、Java アダプターのチュートリアルにあるセキュリティー API セクションを参照してください。
以下を追加することで、登録済みユーザーを応答オブジェクトに追加します。
public void authorize(Set<String> scope, Map<String, Object> credentials, HttpServletRequest request, AuthorizationResponse response) {
PersistentAttributes attributes = registrationContext.getRegisteredProtectedAttributes();
if (attributes.get("pinCode") != null){
// Is there a user currently active?
if (!userLogin.isLoggedIn()){
// If not, set one here.
authorizationContext.setActiveUser(userLogin.getRegisteredUser());
}
setState(SUCCESS_STATE);
response.addSuccess(scope, getExpiresAt(), getName(), "user", userLogin.getRegisteredUser());
} else {
setState(STATE_EXPIRED);
Map <String, Object> failure = new HashMap<String, Object>();
failure.put("failure", "User is not enrolled");
response.addFailure(getName(), failure);
}
}
サンプル・コードの場合、IsEnrolled
チャレンジ・ハンドラーの handleSuccess
メソッドは、表示名を提供するために user オブジェクトを使用します。
サンプル・アプリケーション
セキュリティー検査
EnrollmentUserLogin
、EnrollmentPinCode
、および IsEnrolled
の各セキュリティー検査は、Enrollment Maven プロジェクトの下にある SecurityChecks プロジェクトで入手できます。
ここをクリック してセキュリティー検査 Maven プロジェクトをダウンロードします。
アプリケーション
iOS (Swift)、Android、Cordova、および Web 用のサンプル・アプリケーションを使用できます。
- ここをクリック して Cordova プロジェクトをダウンロードします。
- ここをクリック して iOS Swift プロジェクトをダウンロードします。
- ここをクリック して Android プロジェクトをダウンロードします。
- ここをクリック して Web アプリケーション・プロジェクトをダウンロードします。
サンプルの使用法
サンプルの README.md ファイルの指示に従ってください。
▲Inclusive terminology note: The Mobile First Platform team is making changes to support the IBM® initiative to replace racially biased and other discriminatory language in our code and content with more inclusive language. While IBM values the use of inclusive language, terms that are outside of IBM's direct influence are sometimes required for the sake of maintaining user understanding. As other industry leaders join IBM in embracing the use of inclusive language, IBM will continue to update the documentation to reflect those changes.