瞭解 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 保護的其他應用程式,而不需要重新認證。

瞭解搭配 Java 應用程式使用 SDK 的主要使用案例

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 Commons Collections 4.1

  • Apache Commons Lang 3.7

  • JSON 小型和快速剖析器 2.3

  • 尼布斯 LangTag 1.4.3

  • 寧布斯 JOSE+JWT 5.14

  • OAuth 2.0 SDK 搭配 OpenID Connect 擴充功能 5.30

Oracle 提供範例 Java Web 應用程式,示範如何使用 Java SDK。此應用程式使用 Servlet 技術建置,以簡化學習體驗,瞭解 Java SDK 的運作方式。應用程式會使用 Maven 取得所有程式庫並產生 Web 應用程式資源 (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 應用程式會實行對應 /auth URL 的 AuthServlet 類別。當使用者決定使用 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。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() 方法會傳回包含使用者資訊 (例如顯示名稱、使用者 ID) 的 IdToken 物件執行處理,以及指派給使用者的群組和應用程式角色清單。存取記號和 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>

若要將使用者從應用程式與 Oracle Identity Cloud Service 之間的單一登入登出,Java Web 應用程式會實作對應 /logout URL 的 LogoutServlet

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。