频道栏目
首页 > 程序开发 > 综合编程 > 安全编程 > 正文
在 Worklight 中开发基于认证器和登陆模块的安全验证
2014-01-19 21:30:08           
收藏   我要投稿
IBM Worklight 提供了多种形式的安全验证方式。本文将以 Worklight 6.0 版本为基础,首先介绍 Worklight 中安全验证的基本概念,然后,通过围绕一个示例,来介绍如何开发基于认证器和登陆模块的安全验证。

IBM Worklight 作为一个领先的移动应用开发和管理平台,提供了完善的安全验证框架,保证了内部应用、适配器及静态资源的安全,并且提供了很好的扩展功能,可以自定义认证器和登陆模块来进行复杂的验证。

Worklight 安全验证框架

安全框架认证流程如图 1 所示,主要包含四部分:

  1. 特定保护资源:包括内部应用、适配器(访问企业信息系统)及静态资源。
  2. security test:由一个或多个 ream 组成,用于资源保护。
  3. realm:定义了处理用户验证的业务逻辑,主要包含两部分:
    • 认证器:服务器端组件,用于收集客户端发来的用户身份信息,分为三类:基于表单的认证器、基于适配器的认证器和基于 HTTP 头的认证器。它可以收集 HTTP request 对象的所有信息,如 cookies,body,headers 及其他属性的值。
    • 登陆模块:服务器端组件,用于接收认证器收集到的用户身份信息,验证该身份并创建用户身份对象。当 session 结束(注销或是超时)后,登陆模块会自动销毁用户身份对象。验证逻辑可以调用企业已有的验证框架,有多种验证方式,常见的有如下几种:
      • Webservice 方式调用验证接口
      • 数据库表中查找用户信息
      • 使用 Websphere LTPA Token
  4. challenge handler:客户端组件,每个实例对应一个 realm,用于监测服务器端是不是在发送一个需要安全验证的请求,如果是,则收集用户身份信息,并发送至服务器端。否则验证已经完成,可以直接访问资源。
图 1. 安全框架认证示意图
图 1. 安全框架认证示意图

在多数情况下,我们仅需直接使用 Worklight 提供的认证器和登陆模块,如有复杂的情况,例如认证信息需要从客户端 request 的 cookie、header 和 user-agent 中获取,我们可以用 Java 来实现自己所需的认证器和登陆模块。在本例中,我们将使用认证器和登陆模块的安全验证。

基于认证器和登陆模块的安全验证简介

同其他方式的安全验证流程类似,认证器在拦截到一个访问受保护资源的请求后,会首先检查该请求是否含有合法的用户身份,如果是,则给予访问权限,并返回请求的数据;如果没有合法的用户身份,则启动登陆模块的验证流程,只有在验证流程成功后,才能授予访问权限,返回请求的数据。如图 2 所示。

图 2. 基于认证器和登陆模块的安全验证处理流程图
vcSjv+m1xLCyyKvR6daktKbA7cH3s8zNvA==" src="/uploadfile/2014/0119/20140119093330286.jpg" width="581" />

 

示例介绍

本文给出了一个基于自定义认证器和登陆模块的安全验证的示例,并使用该安全策略来保护适配器中的过程,读者也可以根据自己的需要使用安全策略来保护应用和静态资源。

本文的示例分为四个部分:认证器、登陆模块、适配器和客户端应用,图 3 给出了示例的请求处理流程,该处理流程同图 2 所示的标准请求处理流程类似:

图 3. 示例请求处理流程
图 3. 示例请求处理流程
  1. 请求访问客户端应用中的页面 LoginModuleAuth.html。
  2. 页面中的 JavaScript 调用受保护的过程 protectResource。
  3. 安全验证框架被触发,调用认证器。
  4. 客户端页面中的 JavaScript 检查 authStatus 属性,判断是否需要验证
  5. 如已验证,显示请求页面,显示相应的 DIV 内容,并隐藏特定的 DIV 内容,如登录输入框。至此,请求处理流程结束。
  6. 如需要验证,则显示请求页面,显示相应的 DIV 内容,如登录输入框,并隐藏特定的 DIV 内容。
  7. 用户输入相应身份信息,并触发身份验证请求,相应的登陆模块中的 login 将被触发,用于验证身份信息。
  8. 如果验证成功,则转至第 5 步。
  9. 如果验证失败,则转至第 6 步,并显示相应的错误消息。

从开发角度看,本例的开发主要分为认证器、登陆模块、适配器和客户端应用的开发,由于只是用于演示用途,本例并未设计复杂的业务逻辑。在登陆模块中,只有简单的认证逻辑;在客户端应用中,页面中主要分成两块,一块是在验证后才会显示的,另一块则在没有验证的时候显示。下面我们将介绍如何开发本例及相应的 Worklight 安全验证框架中的要点。

 

示例开发

配置服务器端的安全验证策略

当安全验证策略确定后,我们就可以着手配置安全策略,但在配置之前,我们首先需要创建一个 Worklight 项目,本示例选择创建一个混合应用(Hybrid Application)类型的项目,在向导窗口,输入应用名为 LoginModuleAuth,在下面的小节“开发客户端应用”中,我们将基于这里生成的应用做修改。

图 4. 创建 Worklight 项目
图 4. 创建 Worklight 项目

项目创建成功后,我们可以在项目视图中看到已经生成好的文件,打开 server->conf,我们即可找到用于安全配置的文件 authenticationConfig.xml,如图 5 所示。

图 5. 安全配置文件 authenticationConfig.xml
图 5. 安全配置文件 authenticationConfig.xml

Fig5_AuthConfigFile.jpg

正如本文在第一小节提到的,服务器端的安全配置需要配置 security test 和 realm,在服务器端,realm 由登陆模块和认证器组成,下面我们首先配置登陆模块,打开文件 authenticationConfig.xml,在节点 loginModules 加入清单 1 所示的代码片段。

清单 1. 登陆模块配置代码片段

 <loginModules> 
 <loginModule name="CustomLoginModule"> 
 <className>com.auth.MyCustomLoginModule</className> 
 </loginModule> 
 </loginModules>

由于示例使用登陆模块来执行安全验证逻辑,所以这里使用了自定义的登陆模块 com.auth.MyCustomLoginModule。它将在后面小节中介绍。

配置好了登陆模块,便可在同一个文件中加入 realm 的配置,找到节点 realms,并加入清单 2 所示的代码片段。

清单 2. realm 配置代码片段

 <realm loginModule="CustomLoginModule" 
 name="CustomAuthenticatorRealm"> 
 <className>com.auth.MyCustomAuthenticator</className> 
 </realm>

com.auth.MyCustomAuthenticator 是我们自定义的认证器,该认证器接收客户端的 request 请求信息,当监测到一个针对受保护资源的访问请求,会调用登陆模块 CustomLoginModule 中的 login 方法进行验证。它将在下一个小节中介绍。

现在,我们就可以在同一个配置文件中加入 security test 的配置,如清单 3 所示。

清单 3. security test 配置代码片段

 <customSecurityTest name="LoginModule-securityTest"> 
        <test isInternalUserID="true" realm="CustomAuthenticatorRealm"/> 
 </customSecurityTest>

在文件中,我们可以看到已经有些被注释掉的 security test 的配置,而其中的 security test 又可以分为下面三种:

  • webSecurityTest:用于包含 Web 安全相关的 realm。
  • mobileSecurityTest:用于包含移动安全相关的 realm。
  • customSecurityTest:用于包含自定义开发的 realm。

由于示例使用了自定义的认证器和登陆模块来保护对受保护资源的访问,我们这里使用 customSecurityTest,realm 则是上面所声明的 CustomAuthenticatorRealm。

到这里,我们就已经配置好了安全验证策略,在下面的适配器开发中,我们将会使用到本节声明的 security test 和 realm。

开发认证器

在项目浏览器视图中,找到 server/java 文件夹,选中文件夹 java,创建类 com.auth.MyCustomAuthenticator,并实现 worklight 的认证器接口 WorkLightAuthenticator。认证结果以 json 方式写到 response 中,客户端根据 response 中的结果来进行页面上的显示。

在认证器方法中主要返回了以下几个状态,如图 6 所示(供后面方法参考)。Worklight 框架会根据这几个返回的状态做相应的处理。

图 6. 认证器验证状态
图 6. 认证器验证状态

认证器主要包含以下几个方法。

init 方法在初始化时调用,可以接收在 realm 配置中的定义的 options 参数,代码片段如清单 4。

清单 4. 认证器 init 方法

 public void init(Map<String, String> options)
  throws MissingConfigurationOptionException { 
 logger.info("MyCustomAuthenticator initialized"); 
 }

clone 方法实现类对象的深层复制,代码片段如清单 5。

清单 5. 认证器 clone 方法

 public WorkLightAuthenticator clone() throws CloneNotSupportedException { 
        MyCustomAuthenticator otherAuthenticator = (
        MyCustomAuthenticator) super.clone(); 
        otherAuthenticator.authenticationData = 
        new HashMap<String, Object>(authenticationData); 
        return otherAuthenticator; 
 }

processRequest 方法从 request 接收用户信息,进行简单校验并把结果写到 response 中,返回 AuthenticationResult,代码片段如清单 6。

清单 6. 认证器 processRequest 方法

 public AuthenticationResult processRequest(HttpServletRequest request,
  HttpServletResponse response, boolean isAccessToProtectedResource) 
  throws IOException,ServletException { 
 if (request.getRequestURI().contains("my_custom_auth_request_url")){ 
 String username = request.getParameter("username"); 
 String password = request.getParameter("password"); 

 if (null != username && null != password &&
  username.length() > 0 && password.length() > 0){ 
 authenticationData = new HashMap<String, Object>(); 
 authenticationData.put("username", username); 
 authenticationData.put("password", password); 
 return AuthenticationResult.createFrom(AuthenticationStatus.SUCCESS); 
 } else { 
 response.setContentType("application/json; charset=UTF-8"); 
 response.setHeader("Cache-Control", "no-cache, must-revalidate"); 
 response.getWriter().print("{\"authStatus\":\"required\", 
 \"errorMessage\":\"Please enter username and password\"}"); 
 return AuthenticationResult.createFrom(AuthenticationStatus.CLIENT_INTERACTION_REQUIRED); 
 } 
 } 

 if (!isAccessToProtectedResource) 
 return AuthenticationResult.createFrom(AuthenticationStatus.REQUEST_NOT_RECOGNIZED); 

 response.setContentType("application/json; charset=UTF-8"); 
 response.setHeader("Cache-Control", "no-cache, must-revalidate"); 
 response.getWriter().print("{\"authStatus\":\"required\"}"); 
 return AuthenticationResult.createFrom(AuthenticationStatus.CLIENT_INTERACTION_REQUIRED); 
 }

方法首先根据 request url 进行判断,如果是客户端的定义的请求,则判断是否用户名和密码为空,如果为空则在 response 中保存需要登录的 required 信息和错误信息,并且返回 AuthenticationStatus.CLIENT_INTERACTION_REQUIRED,如果不为空则把数据保存到 authenticationData 中,并返回 AuthenticationStatus.SUCCESS,此时 worklight 框架会自动调用登陆模块中的 login 方法进行验证;如果不是客户端定义的请求,则判断是否是保护的资源,如果不是,则返回 AuthenticationStatus.REQUEST_NOT_RECOGNIZED,否则在 repsonse 中保存需要登录的 required 信息,并返回 AuthenticationStatus.CLIENT_INTERACTION_REQUIRED。

changeResponseOnSuccess 方法是在登陆模块验证成功后被调用,代码片段如清单 7。

清单 7. 认证器 changeResponseOnSuccess 方法

 public boolean changeResponseOnSuccess(HttpServletRequest request,
  HttpServletResponse response) throws IOException { 
 if (request.getRequestURI().contains("my_custom_auth_request_url")){ 
 response.setContentType("application/json; charset=UTF-8"); 
 response.setHeader("Cache-Control", "no-cache, must-revalidate"); 
 response.getWriter().print("{\"authStatus\":\"complete\"}"); 
 return true; 
 } 
 return false; 
 }

在方法中如果自定义 response 的值,则需要返回 true,否则是 false。在本例中登陆验证成功后 response 中保存 complete 状态。

processRequestAlreadyAuthenticated 方法是在已经验证过之后请求访问资源时被调用,直接返回 AuthenticationStatus.REQUEST_NOT_RECOGNIZED,代码片段如清单 8。

清单 8. 认证器 processRequestAlreadyAuthenticated 方法

 public AuthenticationResult 
 processRequestAlreadyAuthenticated(HttpServletRequest 
 request, HttpServletResponse response) throws IOException, ServletException { 
 return AuthenticationResult.createFrom(AuthenticationStatus.REQUEST_NOT_RECOGNIZED); 
 }

processAuthenticationFailure 方法是在登陆模块认证失败后被调用,在 response 中保存需要登录的 required 信息和错误信息,并返回需要提供认证信息的 AuthenticationStatus.CLIENT_INTERACTION_REQUIRED 状态,代码片段如清单 9。

清单 9. 认证器 processAuthenticationFailure 方法

 public AuthenticationResult processAuthenticationFailure(HttpServletRequest 
  request, HttpServletResponse response, 
 String errorMessage) throws IOException, ServletException { 
 response.setContentType("application/json; charset=UTF-8"); 
 response.setHeader("Cache-Control", "no-cache, must-revalidate"); 
 response.getWriter().print("{\"authStatus\":\"required\", 
  \"errorMessage\":\"" + errorMessage + "\"}"); 
 return AuthenticationResult.createFrom(AuthenticationStatus.CLIENT_INTERACTION_REQUIRED); 
 }

开发登录模块

在项目浏览器视图中,找到 server/java 文件夹,选中文件夹 java,创建类 com.auth.MyCustomLoginModule,并实现 worklight 的登录模块接口 WorkLightAuthLoginModule。它主要包含以下几个方法。

init 方法在初始化时调用,可以接收在 realm 配置中的定义的 options 参数,代码片段如清单 10。

清单 10. 登陆模块 init 方法

 public void init(Map<String, String> options) 
  throws MissingConfigurationOptionException { 
 logger.info("MyCustomLoginModule initialized"); 
 }

clone 方法实现类对象的深层复制,代码片段如清单 11。

清单 11. 登陆模块 clone 方法

 public MyCustomLoginModule clone() throws CloneNotSupportedException { 
 return (MyCustomLoginModule) super.clone(); 
 }

login 方法在认证器返回 SUCCESS 状态 ( 如图 6) 后被调用,代码片段如清单 12。

清单 12. 登陆模块 login 方法

 public boolean login(Map<String, Object> authenticationData) { 
 logger.info("MyCustomLoginModule :: login"); 

 USERNAME = (String) authenticationData.get("username"); 
 PASSWORD = (String) authenticationData.get("password"); 

 //Dummy verify logic, just for demo purpose 
 if (USERNAME.equals(PASSWORD)) 
 return true; 
 else 
 throw new RuntimeException("Invalid credentials"); 
 }

示例逻辑比较简单,从认证器的 authenticationData 中得到用户名和密码,当用户名和密码相同时,我们认为用户信息正确,认证通过,返回 true,否则抛出异常,然后 worklight 框架会调用认证器的 processAuthenticationFailure 方法把登陆模块的错误信息返回给客户端。在这个方法中可以根据需求加入特定的验证逻辑,比如调用 webservice, 查询数据库之类的操作。

createIdentity 方法在 login 方法返回 true 后被调用,代码片段如清单 13。

清单 13. 登陆模块 createIdentity 方法

 public UserIdentity createIdentity(String loginModule) { 
 logger.info("MyCustomLoginModule :: createIdentity"); 

 HashMap<String, Object> customAttributes = new HashMap<String, Object>(); 
 customAttributes.put("AuthenticationDate", new Date()); 

 UserIdentity identity = new UserIdentity(loginModule, USERNAME, 
 USERNAME, null, customAttributes, PASSWORD); 
 return identity; 
 }

生成 UserIdentity 用户对象,并且可以自定义一些属性放到 customAttributes 中。UserIdentity 的构造方法如清单 14 所示。在返回对象后,worklight 框架会自动把它赋值给前面 realm 定义的 CustomAuthenticatorRealm 的 activeUser,这代表着用户已经通过认证,在现有的 session 内可以直接访问资源,并且该 activeUser 的信息可以在服务器端被 java 或是 adapter 使用,我们会在后面的 adapter 示例中介绍调用方法。

清单 14. 登陆模块 UserIdentity 构造方法

 public userIdentity (String loginModule, String name, 
 String displayName, Set<String> roles, Map<String, 
 Object> attributes, Object credentials)

logout 和 abort 方法在用户登出或是认证流程终止后被调用,用于清除对象中的值,代码片段如清单 15。

清单 15. 登陆模块 logout 和 abort 方法

 public void logout() { 
 logger.info("MyCustomLoginModule :: logout"); 
 USERNAME = null; 
 PASSWORD = null; 
 } 
 public void abort() { 
 logger.info("MyCustomLoginModule :: abort"); 
 USERNAME = null; 
 PASSWORD = null; 
 }

开发适配器

在项目浏览器视图中,可以找到 adapter 文件夹,我们即可选中文件夹 adapters,右键选中 NewWorklight Adapter,如图 7 所示。

图 7. 创建适配器
图 7. 创建适配器

在弹出的窗口中,选中 Adapter type 为 HTTP Adapter,并输入 Adapter name 为 DummyAdapter。

图 8. 输入适配器信息
图 8. 输入适配器信息

选择“Finish”,即可看到项目视图中新生成的文件夹 DummyAdapter,该文件夹包含下面三个文件,如图 9 所示:

  • DummyAdapter.xml,适配器的配置和声明文件,用于配置和第三方系统连接的相关属性及声明该适配器中对其他应用或适配器所公开的过程。
  • DummyAdapter-impl.js,用于实现上面 XML 文件中所声明的过程。
  • filtered.xsl,用于定义所处理的数据的 schema,由于本例较为简单,无需对该文件做任何修改。
图 9. 适配器文件列表
图 9. 适配器文件列表

如前文所述,本例实际上并未使用适配器连接第三方系统,所以在适配器的配置和声明文件中,我们并无需配置 connectivity 节点,使用生成的默认值即可,目前,Worklight 的 HTTP 适配器支持 REST 和 SOAP 调用。除了 connectivity 之外,我们还在配置和声明文件中,声明了两个公开方法 doLogin 和 protectResource,清单 16 给出了配置和声明文件片段。

清单 16. 配置和声明文件片段

 <connectivity> 
 <connectionPolicy xsi:type="http:HTTPConnectionPolicyType"> 
 <protocol>http</protocol> 
 <domain>rss.cnn.com</domain> 
 <port>80</port> 
 </connectionPolicy> 
 <loadConstraints maxConcurrentConnectionsPerNode="2" /> 
 </connectivity> 
 <procedure name="getSecretData" securityTest="LoginModule-securityTest"/> 
 <procedure name="protectResource" securityTest="LoginModule-securityTest"/>

getSecretDate 和 protectResource 为受保护的过程,当有请求调用该过程时,相应的 security test 将会被调用 .

配置好了 XML 文件之后,我们便可在 JavaScript 文件中实现所配置的公开方法,同时可以添加其他内部方法,清单 17 给出了 getSecretData 方法的实现。

清单 17. getSecretData 方法的实现

 function getSecretData(){ 
 var activeUser =WL.Server.getActiveUser(); 
 return { 
 userId:activeUser.userId, displayName:activeUser.displayName, 
 authenticationDate:activeUser.attributes.AuthenticationDate, 
  secretData: '123456' 
 }; 
 }

得到登陆模块中返回的用户对象,把用户的部分信息和 secretData 返回给客户端。

清单 18. protectResource 方法

 function protectResource() { 

 //No function code required, this method is just used to 
 //make sure the user is authenticated 
 //Otherwise, login page will be showed. 

 }

protectResource 是个空方法,在清单 18 中我们将方法 protectResource 定义为受保护的方法,所以尽管该方法中没有逻辑,但在调用方法前,依然会进行安全检查。这个方法在页面初始化时调用,如果没有验证,显示登陆页面。

至此,我们的适配器就开发完毕,下面我们就可以开发客户端应用来验证我们所做的安全配置。

开发客户端应用

示例的客户端应用主要为一个 HTML 页面,应用为登录前或登录后的用户显示同一个页面,但页面的内容并不一样。由于只是演示用途,我们直接基于创建项目时自动生成的应用上做定制修改,虽然创建项目时自动创建了数个文件,而我们只需要修改其中的一个 JS(LoginModuleAuth.js)文件和 HTML(LoginModuleAuth.html)文件,如图 10 所示。

图 10. 客户端应用文件结构图
图 10. 客户端应用文件结构图

在整个客户端应用的开发过程中,我们主要需要做下面的工作:

  1. 修改 HTML 页面,对已验证的用户和未验证的用户添加不同的显示内容。
  2. 修改 JavaScript wlCommonInit 方法,用于调用受保护的过程。
  3. 在 JavaScript 文件中,增加客户端的用户身份信息收集和验证请求提交代码,其核心为 challenge handler。
  4. 在 JavaScript 文件中,增加一个新的方法 getSecretData,用于调用适配器中的 getSecretData 方法检查用户是否已经通过验证。

HTML 页面比较简单,主要目的是能够对已验证的和未验证的用户提供不同的显示内容,并能够给未验证用户提供登录框供用户输入用户名和密码。清单 19 为页面 HTML 代码片段。

清单 19. 页面 HTML 代码片段

 <body id="content" style="display: none"> 
 <p id="AppBody"> 
 <p class="header"> 
 <h1>Custom Login Module</h1> 
 </p> 
 <p class="wrapper"> 
 <input type="button" value="Call protected adapter proc" onclick="getSecretData()" /> 
 <input type="button" value="Logout" 
   onclick="WL.Client.logout('CustomAuthenticatorRealm', 
   {onSuccess: WL.Client.reloadApp})" /> 
 </p> 
 </p> 
 <p id="AuthBody" style="display: none;"> 
 <p id="loginForm"> 
 Username:<br/> 
 <input type="text" id="usernameInputField" /><br /> 
 Password:<br/> 
 <input type="password" id="passwordInputField" /><br/> 
 <input type="button" id="loginButton" value="Login" /> 
 <input type="button" id="cancelButton" value="Cancel" /> 
 </p> 
 </p>    
 <script src="js/initOptions.js"></script> 
        <script src="js/LoginModuleAuth.js"></script> 
        <script src="js/messages.js"></script> 
    </body>

在加载页面的过程中,initOptions.js 中的 WL.Client.init({}) 会首先被调用,它将调用方法 wlCommonInit。AppBody 区域用于提供已通过验证的用户所能看到的内容,AuthBody 区域用户显示为登录用户所能看到的内容,challenge handler 的方法 handleChallenge 会设定这两个 DIV 的 display 属性。

wlCommonInit 方法位于文件 LoginModuleAuth.js 中,我们需要在该方法中调用前面所创建的适配器 DummyAdapter 中的受保护过程 protectResource,这样,在初始化客户端应用页面时,Worklight 的安全机制将会随着受保护过程的调用而被触发,清单 20 列出了方法 wlCommonInit。

清单 20. 方法 wlCommonInit

 function wlCommonInit(){ 
 // Common initialization code goes here 
 var invocationData = { 
 adapter : "DummyAdapter", 
 procedure : "protectResource", 
 parameters : [] 
 }; 
 WL.Client.invokeProcedure(invocationData,{}); 
  
 }

在 LoginModuleAuth.js 文件中,还需要添加核心的客户端安全验证代码,其核心主要是获取和使用 challenge handler,如清单 21 所示。

清单 21. 获取和使用 challenge handler

 var customAuthenticatorRealmChallengeHandler 
 = WL.Client.createChallengeHandler("CustomAuthenticatorRealm"); 

 customAuthenticatorRealmChallengeHandler.isCustomResponse = function(response) { 
    if (!response || !response.responseJSON) { 
        return false; 
    } 
    
    if (response.responseJSON.authStatus) 
    return true; 
    else 
    return false; 
 }; 

 customAuthenticatorRealmChallengeHandler.handleChallenge = function(response){ 
 var authStatus = response.responseJSON.authStatus; 

 if (authStatus == "required"){ 
 $('#AppBody').hide(); 
 $('#AuthBody').show(); 
 $('#passwordInputField').val(''); 
        if (response.responseJSON.errorMessage){ 
        alert(response.responseJSON.errorMessage); 
        } 
 } else if (authStatus == "complete"){ 

 $('#AppBody').show(); 
 $('#AuthBody').hide(); 
 customAuthenticatorRealmChallengeHandler.submitSuccess(); 
 } 
 }; 
 customAuthenticatorRealmChallengeHandler.submitLoginFormCallback 
 = function(response) { 
    var isLoginFormResponse 
    = customAuthenticatorRealmChallengeHandler.isCustomResponse(response); 
    if (isLoginFormResponse){ 
    customAuthenticatorRealmChallengeHandler.handleChallenge(response); 
    } 
 };

WL.Client.createChallengeHandler("CustomAuthenticatorRealm ") 用于获取 challenge handler,参数为我们所要使用的 realm。Challenge handler 的方法 isCustomResponse 会在每次收到服务器端响应的时候被调用,用于检查是不是需要对该响应做安全验证,当该方法返回为 true 时,意味着 Worklight 框架需要对该响应做安全验证。handleChallenge 方法用于在需要安全验证的情况下,当返回为 required 时,显示用户身份信息输入选项,并显示相应的错误消息;当返回为 complete 时,则意味着用户已通过安全验证,只需调用 submitSuccess 方法来通知 Worklight 框架,表示用户已通过安全验证。submitLoginFormCallback 在提交用户信息后被调用,根据 isCustomResponse 返回的值来判断是否调用 handleChallenge 方法。

本文的目的是介绍调用受保护的适配器过程,然后触发适配器和登陆模块的安全验证,调用适配器代码也需要添加在 LoginModuleAuth.js 中,如清单 22 所示。

清单 22. 调用适配器触发验证逻辑

 $('#loginButton').bind('click', function () { 
    var reqURL = '/my_custom_auth_request_url'; 
    var options = {}; 
    options.parameters = { 
        username : $('#usernameInputField').val(), 
        password : $('#passwordInputField').val() 
    }; 
    options.headers = {}; 
    customAuthenticatorRealmChallengeHandler.submitLoginForm(reqURL, 
    options, customAuthenticatorRealmChallengeHandler.submitLoginFormCallback); 
 });

上面的代码主要做了三件事:

  1. 为按键的 click 事件绑定 JavaScript 方法,当用户单击页面登录按键时,绑定的 JavaScript 方法将被触发。
  2. 获取页面中的用户名和密码信息
  3. 通过执行 submitLoginForm 来调用认证器和登陆模块的验证逻辑,并通过 submitLoginFormCallback 根据验证结果来显示页面内容。

至此,我们已经完成了本例的开发工作,下面我们将部署和运行本例。

 

部署和运行示例

由于我们使用了适配器,所以我们首先需要部署适配器,在项目视图中,右键单击适配器的文件夹,选中“Run As->Deploy Worklight Adapter”,如图 11 所示。

图 11. 部署适配器
图 11. 部署适配器

在控制台视图中,如果看到“Adapter buildand deploy finished”,则表示适配器已经部署成功,如图 12 所示。

图 12. 控制台视图
图 12. 控制台视图

同部署适配器类似,我们还需要部署客户端应用,找到应用的文件夹,右键单击,选中“Run As->Build All and Deploy”,如图 13 所示。

图 13. 部署客户端应用
图 13. 部署客户端应用

同样,如果控制台视图提示“Application 'LoginModuleAuth' deployed successfully with all environments”,则表明应用已经部署成功,如图 14 所示。

图 14. 部署客户端应用控制台
图 14. 部署客户端应用控制台

最后,我们便可以通过控制台(localhost:8080/LoginModuleAuth/console)来预览我们的示例,图 15 为初次访问的示例页面。

图 15. 示例初次访问页面
图 15. 示例初次访问页面

因为用户还没登录,而我们在页面初始化过程中已经调用了受保护的适配器过程,所以在页面上显示用户登录信息,当用户输入正确的用户和密码(这里只需要用户名等于密码),即可显示已验证用户所能看到的内容,如图 16 所示。

图 16. 授权用户显示页面
图 16. 授权用户显示页面

用户即使再次刷新页面,页面内容也不会变化,因为我们在认证器和登陆模块验证过程中已经将 authStatus 设为 complete,这样 Worklight 框架会认为后续请求已经经过安全验证,无需再次进行安全验证,当点击“退出”按键后,则会回到未验证状态,并显示未验证的页面。用户点击获取数据按钮,将返回适配器中 getSecretData 方法中的数据,如图 17 所示。

图 17. 数据返回
图 17. 数据返回

 

总结

本文通过一个简单的示例,介绍了基于认证器和登陆模块的安全验证的基本概念,同时介绍了基本的开发步骤以及如何在 Eclipse 中做相关开发,我们在掌握了开发本文示例的基础上,可以根据项目的需要,做进一步的定制开发


点击复制链接 与好友分享!回本站首页
相关TAG标签 模块
上一篇:Delphi 执行DOS命令并返回结果 CMD远控等等常用
下一篇:Delphi执行DOS命令并返回结果CMD远控等等常用命令
相关文章
图文推荐
点击排行

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站