注册

improve this page | report issue

概述

该样本演示了定制注册流程和递增授权。在此一次性注册流程中,用户需要输入其用户名和密码,还需要定义 PIN 码。

先决条件:确保阅读 ExternalizableSecurityCheck递增教程。

跳转至:

应用程序流

  • 在应用程序初次启动时(注册前),会显示具有以下两个按钮的 UI:获取公共数据注册
  • 在用户点击注册按钮开始注册时,会使用登录表单进行提示,然后请求该用户设置 PIN 码。
  • 在用户成功注册后,UI 包含四个按钮:获取公共数据获取余额获取交易注销。用户无需输入 PIN 码,便可访问全部四个按钮。
  • 再次启动应用程序时(注册后),UI 仍包含所有四个按钮。然而,当用户单击获取交易* 按钮时,会要求该用户输入 PIN 码。

尝试输入 PIN 码三次失败后,会提示该用户使用用户名和密码重新认证和重置 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();
    }
    

安全性检查

注册样本包含三项安全性检查:

EnrollmentUserLogin

EnrollmentUserLogin 安全性检查保护 setPinCode 资源,以便只有已认证的用户才能够设置 PIN 码。此安全性检查意味着快速到期,并且仅在“初次体验”期间进行。它与实现 UserAuthenticationSecurityCheck 教程中所解释的 UserLogin 安全性检查相同,但额外的 isLoggedIngetRegisteredUser 方法除外。
如果安全性检查状态等于 SUCCESS,isLoggedIn 方法会返回 true,否则将返回 false
getRegisteredUser 方法会返回已认证的用户。

public boolean isLoggedIn(){
    return getState().equals(STATE_SUCCESS);
}
public AuthenticatedUser getRegisteredUser() {
    return registrationContext.getRegisteredUser();
}

EnrollmentPinCode

EnrollmentPinCode 安全性检查可保护获取交易资源,与实现 CredentialsValidationSecurityCheck 教程中所解释的 PinCodeAttempts 安全性检查相似(一些更改除外)。

在本教程的示例中,EnrollmentPinCode 取决于 EnrollmentUserLogin。成功登录到 EnrollmentUserLogin 后,只会要求用户输入 PIN 码。

@SecurityCheckReference
private transient EnrollmentUserLogin userLogin;

在应用程序初次启动且用户成功注册时,用户必须能够在无需输入其刚刚设置的 PIN 码的情况下访问获取交易资源。为此,authorize 方法使用 EnrollmentUserLogin.isLoggedIn 方法来检查用户是否登录。这意味着只要 EnrollmentUserLogin 不到期,用户便能够访问获取交易

@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());
    }
}

本教程针对用户尝试三次后输入 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 适配器,并添加名为 IsEnrolled 且可扩展 ExternalizableSecurityCheck 的 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 配置类

创建可扩展 ExternalizableSecurityCheckConfigIsEnrolledConfig 配置类:

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 方法使用用户对象来呈现显示名称。

注册样本应用程序

样本应用程序

安全性检查

注册 Maven 项目下的 SecurityChecks 项目中提供了 EnrollmentUserLoginEnrollmentPinCodeIsEnrolled 安全性检查。 单击以下载安全性检查 Maven 项目。

应用程序

样本应用程序可用于 iOS (Swift)、Android、Cordova 和 Web。

样本用法

请遵循样本的 README.md 文件获取指示信息。

Last modified on February 23, 2017