了解 Java 应用程序与 Oracle Identity Cloud Service 之间的验证

您已准备好了解 Java Web 应用程序与 Oracle Identity Cloud Service 之间的验证。这包括了解以下内容:

  • Oracle Identity Cloud Service 为 Java SDK 支持的三路验证流

  • 将 SDK 与 Java 应用结合使用以通过 Oracle Identity Cloud Service 进行验证的用例

  • Java SDK 的方法和函数

了解三路验证流

Oracle Identity Cloud Service 支持 Java SDK 的三路验证流。在此流中,用户直接与 Oracle Identity Cloud Service 交互。用户登录后,Oracle Identity Cloud Service 会发出 SDK 为用户访问令牌交换的授权代码。Java Web 应用程序使用此访问令牌向用户授予对应用程序中受保护资源的访问权限。三路流使用授权代码授权类型。

为了提高安全性,Oracle 建议您使用三路流程将 Java Web 应用程序与 Oracle Identity Cloud Service 集成以进行验证。通过使用授权代码授权类型,您还可以访问受 Oracle Identity Cloud Service 保护的其他应用程序,而无需重新验证。

了解将 SDK 与 Java 应用程序结合使用的主要用例

Java Web 应用程序实现两个用例:一个用于验证用户,另一个用于访问有关登录用户的详细信息。

以下数据流图说明了 Web 浏览器、Web 应用程序和 Oracle Identity Cloud Service 之间每个用例的事件、调用和响应的流。

用例 1:验证用户

数据流以以下方式进行:

  1. 用户请求受保护的资源。

  2. 验证模块使用 SDK 为 Oracle Identity Cloud Service 生成请求授权代码 URL,并将此 URL 作为重定向响应发送到 Web 浏览器。

  3. Web 浏览器调用 URL。

  4. 此时将显示 Oracle Identity Cloud Service 登录页。

  5. 用户提交其 Oracle Identity Cloud Service 登录身份证明。

  6. 用户登录后,Oracle Identity Cloud Service 将为用户创建会话并发出授权代码。

  7. Web 应用程序进行后端(或服务器到服务器)调用以交换访问令牌的授权代码。

  8. Oracle Identity Cloud Service 发出访问令牌和 ID 令牌。

  9. 将建立会话,并将用户重定向到主页

  10. 此时将显示 Web 应用程序的主页

用例 2:获取有关用户的详细信息

数据流以以下方式进行:

  1. 用户请求 /myProfile 资源。

  2. Web 应用程序使用 Oracle Identity Cloud Service 的 SDK 验证 ID 令牌。

  3. 从 ID 标记验证返回的数据包含 JSON 对象格式的用户详细信息。

  4. 我的概要信息页将 JSON 对象呈现为 HTML 内容。

了解方法和函数

Java SDK 是作为 Web 应用程序库加载的 JAR 文件。此 JAR 文件需要这些第三方库,您还必须将其加载到库中:

Java SDK zip 文件包含以下 SDK 所需的第三方库。您可以将它们加载到应用程序中。

  • ASM 帮助程序 Minidev 1.0.2

  • Apache 公用集合 4.1

  • Apache 公用语言 3.7

  • JSON 小型和快速语法分析器 2.3

  • Nimbus LangTag 1.4.3

  • Nimbus JOSE+JWT 5.14

  • OAuth 2.0 SDK 与 OpenID Connect 扩展 5.30

Oracle 提供了一个示例 Java Web 应用程序来演示如何使用 Java SDK。此应用程序是使用 Servlet 技术构建的,用于简化学习体验,以了解 Java SDK 的工作原理。应用程序使用 Maven 获取所有库并生成 Web 应用程序资源 (Web Application Resource,WAR) 文件。

Java SDK 需要加载有 Oracle Identity Cloud Service 连接信息的 HashMap 对象实例。Java Web 应用程序将此 HashMap 实例实现为 ConnectionOptions.java 类中的类属性。

//Instance of a HashMap.
    private Map<String,Object> options = new HashMap<>();
  
public ConnectionOptions(){
        this.options = new HashMap<>();
}
  
public Map<String,Object> getOptions(){
        //Adding Oracle Identity Cloud Service connection parameters to the HashMap instance.
        this.options.put(IDCSTokenAssertionConfiguration.IDCS_HOST, "identity.oraclecloud.com");
        this.options.put(IDCSTokenAssertionConfiguration.IDCS_PORT, "443");
        this.options.put(IDCSTokenAssertionConfiguration.IDCS_CLIENT_ID, "123456789abcdefghij");
        this.options.put(IDCSTokenAssertionConfiguration.IDCS_CLIENT_SECRET, "abcde-12345-zyxvu-98765-qwerty");
        this.options.put(IDCSTokenAssertionConfiguration.IDCS_CLIENT_TENANT, "idcs-abcd1234");
        this.options.put(Constants.AUDIENCE_SERVICE_URL, "https://idcs-abcd1234.identity.oraclecloud.com");
        this.options.put(Constants.TOKEN_ISSUER, "https://identity.oraclecloud.com/");
        this.options.put(Constants.TOKEN_CLAIM_SCOPE, "urn:opc:idm:t.user.me openid");
        this.options.put("SSLEnabled", "true");
        this.options.put("redirectURL", "http://localhost:8080/callback");
        this.options.put("logoutSufix", "/oauth2/v1/userlogout");
        this.options.put(Constants.CONSOLE_LOG, "True");
        this.options.put(Constants.LOG_LEVEL, "DEBUG");
        return this.options;
    }

以下是此 SDK 的每个必需属性的简要说明:

名称 说明
IDCSTokenAssertionConfiguration.IDCS_HOST Oracle Identity Cloud Service 实例的域后缀。
IDCSTokenAssertionConfiguration.IDCS_PORT Oracle Identity Cloud Service 实例保留的 HTTPS 端口号(通常为 443)。
IDCSTokenAssertionConfiguration.IDCS_CLIENT_ID 在 Identity Cloud Service 控制台中注册 Java Web 应用程序后生成的客户端 ID 的值。
IDCSTokenAssertionConfiguration.IDCS_CLIENT_SECRET 在 Identity Cloud Service 控制台中注册 Java Web 应用程序后生成的客户端密钥值。
IDCSTokenAssertionConfiguration.IDCS_CLIENT_TENANT Oracle Identity Cloud Service 实例的域前缀。此前缀通常是一个类似于 idcs-abcd1234 的值。
Constants.AUDIENCE_SERVICE_URL Oracle Identity Cloud Service 实例的全限定域名 URL。
Constants.TOKEN_ISSUER 对于此属性,Oracle 建议您保留值 https://identity.oraclecloud.com
Constants.TOKEN_CLAIM_SCOPE

该范围控制应用程序可以代表 Oracle Identity Cloud Service 用户访问或处理的数据。

如果应用程序使用 SDK 对用户进行验证,则范围为 openid。如果应用程序还使用 SDK 获取有关用户的详细信息,则范围为 urn:opc:idm:t.user.me openid

SSLEnabled 指示 Oracle Identity Cloud Service 是否响应 HTTPS 或 HTTP 请求。对于此属性,Oracle 建议您保留值 true
Constants.CONSOLE_LOG 启用 SDK 日志。
Constants.LOG_LEVEL 指示 SDK 的日志级别。

应用程序同时使用 logoutSufixredirectURL 属性来避免对这些值进行硬编码。SDK 不需要它们。

Java Web 应用程序实现 AuthServlet 类,该类映射 /auth URL。当用户决定使用 Oracle Identity Cloud Service 进行验证时,Web 浏览器会向此 URL 发出请求。AuthServlet 类初始化 Authentication Manager 对象,使用 Java SDK 生成 Oracle Identity Cloud Service 的授权代码 URL,然后将 Web 浏览器重定向到此 URL。

public class AuthServlet extends HttpServlet {
    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //Loading the configurations
        Map<String, Object> options = new ConnectionOptions().getOptions();
        //Configuration object instance with the parameters loaded.
        IDCSTokenAssertionConfiguration config = new IDCSTokenAssertionConfiguration(options);
	String redirectUrl = (String)options.get("redirectURL");
        String scope = (String)options.get(Constants.TOKEN_CLAIM_SCOPE);
        //Authentication Manager loaded with the configurations.
        AuthenticationManager am = AuthenticationManagerFactory.getInstance(config);
        //Using Authentication Manager to generate the Authorization Code URL, passing the
        //application's callback URL as parameter, along with code value and code parameter.
        String authzURL = am.getAuthorizationCodeUrl(redirectUrl, scope, "1234", "code");
        //Redirecting the browser to the Oracle Identity Cloud Service Authorization URL.
        response.sendRedirect(authzURL);
    }
}

以下参数用于生成授权代码 URL:

名称 说明
redirectUrl 用户登录后,Oracle Identity Cloud Service 会将用户的 Web 浏览器重定向到此 URL。此 URL 必须与您在 Identity Cloud Service 控制台中为可信应用程序配置的 URL 匹配。有关为 Java Web 应用程序指定重定向 URL 的详细信息,请参阅注册 Java 应用程序。
scope 验证的 OAuth 或 OpenID Connect 范围。此应用程序仅需要 openid 验证。
state OAuth 协议定义此参数。示例 Java Web 应用程序使用此代码检查是否可以建立与 Oracle Identity Cloud Service 的通信。对于此示例,此参数的值为 1234
response_type 授权代码授权类型所需的参数。对于此示例,此参数的值为 code

用户登录后,Oracle Identity Cloud Service 会将用户的 Web 浏览器重定向到回调 URL,开发人员必须实施该 URL。Java Web 应用程序使用 CallbackServlet 来处理此请求。

public class CallbackServlet extends HttpServlet {
    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //Loading the configurations
        Map<String, Object> options = new ConnectionOptions().getOptions();
        //After Oracle Identity Cloud Service authenticates the user, the browser is redirected to the
        //callback URL, implemented as a Servlet.
        IDCSTokenAssertionConfiguration config = new IDCSTokenAssertionConfiguration(options);
        //Authentication Manager loaded with the configurations.
        AuthenticationManager am = AuthenticationManagerFactory.getInstance(config);
        //Getting the authorization code from the "code" parameter
        String authzCode = request.getParameter("code");
        //Using the Authentication Manager to exchange the Authorization Code to an Access Token.
        AuthenticationResult ar = am.authorizationCode(authzCode);
        //Getting the Access Token object and its String value.
        AccessToken access_token = ar.getToken(OAuthToken.TokenType.ACCESS_TOKEN);
        String access_token_string = access_token.getToken();
        //Getting the ID Token object and its String value.
        IdToken id_token = ar.getToken(OAuthToken.TokenType.ID_TOKEN);
        String id_token_string = id_token.getToken();
        //Validating both Tokens to acquire information for User such as UserID, 
        //DisplayName, list of groups and AppRoles assigned to the user.
        IdToken id_token_validated = am.validateIdToken(id_token_string);
        //Storing information into the HTTP Session.
        HttpSession session=request.getSession();
        session.setAttribute("access_token", access_token_string);
        session.setAttribute("id_token", id_token_string);
        session.setAttribute("userId", id_token_validated.getUserId());
        session.setAttribute("displayName", id_token_validated.getDisplayName());
	//Forwarding the request to the Home page.
        request.getRequestDispatcher("private/home.jsp").forward(request, response);
    }
}	

应用程序请求授权代码参数,并使用该参数调用 Java SDK 的 AuthenticationManager.authorizationCode() 方法来请求访问令牌和 ID 令牌。然后,servlet 使用 AuthenticationManager 实例验证 ID 标记。AuthenticationManager.validateIdToken() 方法返回 IdToken 对象的实例,其中包含用户信息,例如显示名称、用户 ID 以及分配给用户的组和应用程序角色的列表。访问令牌和 ID 令牌值以及某些用户信息都存储在 HTTP 会话对象中,以便可以将请求转发到名为 /private/home.jsp 的应用程序中的受保护 URL。除了 /private/home.jsp,Java Web 应用程序还有其他受保护的 URL:/private/myProfile.jsp

myProfile.jsp 页面使用以下代码示例来获取有关登录到 Oracle Identity Cloud Service 的用户的更多信息:


<%if(session.getAttribute("access_token")==null) response.sendRedirect("/login.html");%>
<!DOCTYPE html>
<%
/**
 * The /private/myProfile.jsp page accesses the user's access token previously set in the session, 
 * calls the getAuthenticatedUser method to retrieve the user's information, and then formats it as HTML.
 * @author felippe.oliveira@oracle.com
 * @Copyright Oracle
*/
  java.util.Map<String, Object> options = new sampleapp.util.ConnectionOptions().getOptions();
  //Configuration object instance with the parameters loaded.
  oracle.security.jps.idcsbinding.shared.IDCSTokenAssertionConfiguration configuration = new oracle.security.jps.idcsbinding.shared.IDCSTokenAssertionConfiguration(options);
  oracle.security.jps.idcsbinding.shared.AuthenticationManager am = oracle.security.jps.idcsbinding.shared.AuthenticationManagerFactory.getInstance(configuration);
  
  //Getting the Access Token and the Id Token from the session object
  String access_token_string = (String)session.getAttribute("access_token");
  String id_token_string = (String)session.getAttribute("id_token");

  //Validating the ID Token to get user information, groups and app roles associated with the user.
  oracle.security.jps.idcsbinding.api.AccessToken access_token_validated = am.validateAccessToken(access_token_string);
  oracle.security.jps.idcsbinding.api.IdToken id_token_validated = am.validateIdToken(id_token_string);
%>

/private/myProfile.jsp 页面访问会话中设置的 ID 标记值,调用 AuthenticationManager.validateIdToken() 方法以检索用户的信息,然后将其显示为 HTML 内容。


<p><b>Information from the Identity Token:</b></p><p><%
out.println("DisplayName = "+ id_token_validated.getDisplayName() +"<br>");
out.println("IdentityDomain = "+ id_token_validated.getIdentityDomain() +"<br>");
out.println("UserName = "+ id_token_validated.getUserName()+"<br>");
out.println("UserId = "+ id_token_validated.getUserId()+"<br>");
out.println("Issuer = "+ id_token_validated.getIssuer()+"<br>");

java.util.List<oracle.security.jps.idcsbinding.api.IDCSAppRole> appRoles = id_token_validated.getAppRoles();
if(!appRoles.isEmpty()){
   out.println("App Roles:<br>");
   for(oracle.security.jps.idcsbinding.api.IDCSAppRole appRole: appRoles){
      out.println("&nbsp;appRole = "+ appRole.getName() +"<br>");
   }//for
}//if

java.util.List<oracle.security.jps.idcsbinding.api.IDCSGroup> groups = id_token_validated.getGroupMembership();
if(!groups.isEmpty()){
   out.println("Groups:<br>");
   for(oracle.security.jps.idcsbinding.api.IDCSGroup group: groups){
      out.println("&nbsp;group = "+ group.getName() +"<br>");
   }//for
}//if
%>
</p>
<p><b>Access Token:</b></p><p><%=access_token_string%></p>

To sign the user out from single sign-on between the application and Oracle Identity Cloud Service, the Java web application implements the LogoutServlet, which maps the /logout URL:

public class LogoutServlet extends HttpServlet {
    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session=request.getSession();
        String id_token = (String)session.getAttribute("id_token");
        session.invalidate();
        Map options = new ConnectionOptions().getOptions();
        String logoutURL = (String)options.get(Constants.AUDIENCE_SERVICE_URL) + (String)options.get("logoutSufix") +"?post_logout_redirect_uri=http%3A//localhost%3A8080&id_token_hint="+ id_token;
        response.sendRedirect(logoutURL);
    }
}

此 servlet 会使应用程序的会话无效,然后将用户的 Web 浏览器重定向到 Oracle Identity Cloud Service 的 OAuth 注销 URL。