HTTP 토큰을 사용하여 SSO 인증을 수행하는 Flash Builder 응용 프로그램 만들기 creating-flash-builder-applicationsthat-perform-sso-authentication-using-http-tokens

이 문서의 샘플 및 예제는 JEE 환경의 AEM Forms에 대해서만 적용됩니다.

HTTP 토큰을 사용하여 SSO(Single Sign-On) 인증을 수행하는 Flash Builder을 사용하여 클라이언트 애플리케이션을 만들 수 있습니다. 예를 들어 Flash Builder을 사용하여 웹 기반 응용 프로그램을 만든다고 가정합니다. 다음은 각 보기가 서로 다른 AEM Forms 작업을 호출하는 애플리케이션에 서로 다른 보기가 포함되어 있다고 가정합니다. 각 Forms 작업에 대해 사용자를 인증하는 대신 사용자가 한 번 인증할 수 있는 로그인 페이지를 만들 수 있습니다. 인증되면 사용자는 다시 인증하지 않고도 여러 작업을 호출할 수 있습니다. 예를 들어 사용자가 Workspace(또는 다른 Forms 애플리케이션)에 로그인한 경우 해당 사용자는 다시 인증할 필요가 없습니다.

클라이언트 응용 프로그램에 SSO 인증을 수행하는 데 필요한 응용 프로그램 논리가 포함되어 있지만 AEM Forms User Management에서는 실제 사용자 인증을 수행합니다. HTTP 토큰을 사용하여 사용자를 인증하려면 클라이언트 애플리케이션이 인증 관리자 서비스의 authenticateWithHTTPToken 작업. 사용자 관리는 HTTP 토큰을 사용하여 사용자를 인증할 수 있습니다. 이후 AEM Forms에 대한 원격 또는 웹 서비스 호출의 경우 인증을 위해 자격 증명을 전달할 필요가 없습니다.

NOTE
이 섹션을 읽기 전에 원격을 사용하여 AEM Forms을 호출하는 방법을 잘 알고 있는 것이 좋습니다. (참조: AEM Forms Remoting을 사용하여 AEM Forms 호출.)

다음 AEM Forms 단기 프로세스 MyApplication/EncryptDocument는 사용자가 SSO를 사용하여 인증되면 호출됩니다. 입력 및 출력 값과 같은 이 프로세스에 대한 자세한 내용은 단기 프로세스 예.)

cf_cf_encryptdocumentprocess2

NOTE
이 프로세스는 기존 AEM Forms 프로세스를 기반으로 하지 않습니다. 이 프로세스를 호출하는 방법에 대해 설명하는 코드 예제를 따라 MyApplication/EncryptDocument workbench를 사용합니다. (참조: Workbench 사용.)

Flash Builder을 사용하여 빌드된 클라이언트 애플리케이션은에 구성된 사용자 관리자의 보안 서블릿과 상호 작용합니다. /um/login/um/logout. 즉, 클라이언트 애플리케이션이 /um/login 시작 중 URL을 입력하여 사용자의 상태를 확인합니다. 그러면 사용자 관리자가 사용자 상태에 응답합니다. 클라이언트 응용 프로그램과 User Manager 보안 서블릿은 HTTP를 사용하여 통신합니다.

요청 형식

보안 서블릿에는 다음 입력 변수가 필요합니다.

  • um_no_redirect - 이 값은 다음과 같아야 합니다. true. 이 변수는 User Manager 보안 서블릿에 대한 모든 요청을 동반합니다. 또한 보안 서블릿이 Flex 클라이언트 또는 기타 웹 애플리케이션에서 들어오는 요청을 구분하는 데 도움이 됩니다.
  • j_username - 이 값은 로그인 양식에 제공된 사용자의 로그인 식별자 값입니다.
  • j_password - 이 값은 로그인 양식에 제공된 사용자의 해당 암호입니다.

다음 j_password 값은 자격 증명 요청에만 필요합니다. 암호 값이 지정되지 않은 경우 보안 서블릿은 사용 중인 계정이 이미 인증되었는지 확인합니다. 그럴 경우 계속 진행할 수 있지만 보안 서블릿이 사용자를 다시 인증하지 않습니다.

NOTE
i18n을 제대로 처리하려면 이러한 값이 POST 형식인지 확인하십시오.

응답 형식

보안 서블릿 구성 위치: /um/login 을(를) 사용하여 응답합니다 URLVariables 포맷. 이 형식에서 컨텐츠 유형의 출력은 text/plain입니다. 출력에는 앰퍼샌드(&) 문자로 구분된 이름 값 쌍이 포함되어 있습니다. 응답에는 다음 변수가 포함됩니다.

  • authenticated - 값은 다음 중 하나입니다 true 또는 false.

  • authstate - 이 값은 다음 값 중 하나를 포함할 수 있습니다.

    • CREDENTIAL_CHALLENGE - 이 상태는 User Manager가 어떤 방법으로도 사용자의 ID를 확인할 수 없음을 나타냅니다. 인증이 진행되려면 사용자의 사용자 이름과 암호가 필요합니다.
    • SPNEGO_CHALLENGE- 이 상태는 와 동일하게 처리됨 CREDENTIAL_CHALLENGE.
    • COMPLETE - 이 상태는 사용자 관리자가 사용자를 인증할 수 있음을 나타냅니다.
    • FAILED - 이 상태는 사용자 관리자가 사용자를 인증할 수 없음을 나타냅니다. 이 상태에 대한 응답으로 flex 클라이언트는 사용자에게 오류 메시지를 표시할 수 있습니다.
    • LOGGED_OUT - 이 상태는 사용자가 성공적으로 로그아웃되었음을 나타냅니다.
  • assertionid - 상태가 인 경우 COMPLETE 그러면 사용자의 assertionId 값. 클라이언트 애플리케이션은 AuthResult 사용자용입니다.

로그인 프로세스

클라이언트 응용 프로그램이 시작되면 POST을 /um/login 보안 서블릿. 예, https://<your_serverhost>:<your_port>/um/login?um_no_redirect=true. 요청이 User Manager 보안 서블릿에 도달하면 다음 단계를 수행합니다.

  1. 라는 쿠키를 찾습니다. lcAuthToken. 사용자가 이미 다른 Forms 애플리케이션에 로그인되어 있는 경우 이 쿠키가 존재합니다. 쿠키가 발견되면 해당 콘텐츠의 유효성이 검사됩니다.
  2. 헤더 기반 SSO가 활성화되면 서블릿은 구성된 헤더를 찾아 사용자의 ID를 결정합니다.
  3. SPNEGO가 활성화되면 서블릿은 SPNEGO를 시작하고 사용자의 ID를 확인하려고 시도합니다.

보안 서블릿이 사용자와 일치하는 유효한 토큰을 찾으면 보안 서블릿을 사용하여 계속 진행하여 authstate=COMPLETE. 그렇지 않으면 보안 서블릿은 authstate=CREDENTIAL_CHALLENGE. 다음 목록은 이러한 값을 설명합니다.

  • Case authstate=COMPLETE: 사용자가 인증되었음을 나타내고 assertionid 값에는 사용자에 대한 어설션 식별자가 포함됩니다. 이 단계에서 클라이언트 애플리케이션은 AEM Forms에 연결할 수 있습니다. 해당 URL에 대해 구성된 서블릿은 AuthResult 를 호출하여 사용자용 AuthenticationManager.authenticate(HttpRequestToken) 메서드를 사용합니다. 다음 AuthResult 인스턴스는 사용자 관리자 컨텍스트를 작성하여 세션에 저장할 수 있습니다.
  • Case authstate=CREDENTIAL_CHALLENGE: 보안 서블릿에 사용자의 자격 증명이 필요함을 나타냅니다. 응답으로서, 클라이언트 애플리케이션은 로그인 화면을 사용자에게 표시하고 획득된 크리덴셜을 보안 서블릿(예를 들어, https://<your_serverhost>:<your_port>/um/login?um_no_redirect=true&j_username=administrator&j_password=password). 인증이 성공하면 보안 서블릿은 다음으로 응답합니다 authstate=COMPLETE.

인증이 여전히 실패하면 보안 서블릿은 authstate=FAILED. 이 값에 응답하기 위해 클라이언트 애플리케이션은 자격 증명을 다시 획득하라는 메시지를 표시할 수 있다.

NOTE
While authstate=CREDENTIAL_CHALLENGE: 클라이언트가 획득한 자격 증명을 POST 양식의 보안 서블릿에 전송하는 것이 좋습니다.

로그아웃 프로세스

클라이언트 응용 프로그램이 로그아웃되면 다음 URL로 요청을 보낼 수 있습니다.

https://<your_serverhost>:<your_port>/um/logout?um_no_redirect=true

이 요청을 수신하면 User Manager 보안 서블릿은 lcAuthToken 쿠키 및 응답 authstate=LOGGED_OUT. 클라이언트 응용 프로그램이 이 값을 받은 후 응용 프로그램에서 정리 작업을 수행할 수 있습니다.

SSO를 사용하여 AEM Forms 사용자를 인증하는 클라이언트 애플리케이션 만들기 creating-a-client-application-that-authenticates-aem-forms-users-using-sso

SSO 인증을 수행하는 클라이언트 애플리케이션을 만드는 방법을 보여 주기 위해 예제 클라이언트 애플리케이션이 만들어집니다. 다음 그림은 클라이언트 애플리케이션이 SSO를 사용하여 사용자를 인증하기 위해 수행하는 단계를 보여 줍니다.

cf_cf_flexsso

이전 그림에서는 클라이언트 응용 프로그램이 시작될 때 발생하는 응용 프로그램 흐름에 대해 설명합니다.

  1. 클라이언트 응용 프로그램이 applicationComplete 이벤트.
  2. 에 대한 호출 ISSOManager.singleSignOn 만들어짐. 클라이언트 애플리케이션은 User Manager 보안 서블릿에 요청을 전송합니다.
  3. 보안 서블릿이 사용자를 인증하면 ISSOManager 디스패치 SSOEvent.AUTHENTICATION_SUCCESS. 이에 대한 응답으로 클라이언트 애플리케이션이 기본 페이지를 표시합니다. 이 예제에서 기본 페이지는 MyApplication/EncryptDocument라는 AEM Forms 단기 프로세스를 호출합니다.
  4. 보안 서블릿이 사용자가 유효한지 확인할 수 없는 경우 애플리케이션이 사용자 자격 증명을 다시 요청합니다. 다음 ISSOManager 클래스 발송 SSOEvent.AUTHENTICATION_REQUIRED 이벤트. 클라이언트 응용 프로그램은 로그인 페이지를 표시합니다.
  5. 로그인 페이지에 제공된 자격 증명은 ISSOManager.login 메서드를 사용합니다. 인증이 성공하면 3단계로 이동합니다. 그렇지 않으면 SSOEvent.AUTHENTICATION_FAILED 이벤트가 트리거됩니다. 클라이언트 응용 프로그램은 로그인 페이지와 적절한 오류 메시지를 표시합니다.

클라이언트 응용 프로그램 만들기 creating-the-client-application

클라이언트 애플리케이션은 다음 파일로 구성됩니다.

다음 그림은 클라이언트 응용 프로그램을 시각적으로 보여줍니다.

cf_cf_sso_project

NOTE
um과 views라는 두 개의 패키지가 있습니다. 클라이언트 응용 프로그램을 만들 때는 파일을 적절한 패키지에 배치해야 합니다. 또한 adobe-remoting-provider.swc 파일을 프로젝트의 클래스 경로에 추가해야 합니다. (참조: AEM Forms Flex 라이브러리 파일 포함.)

SSOStandalone.mxml 파일 만들기 creating-the-ssostandalone-mxml-file

다음 코드는 SSOStandalone.mxml 파일을 나타냅니다.

 <?xml version="1.0" encoding="utf-8"?>
 <mx:Application
                 layout="absolute"
                 applicationComplete="initApp()"
                 height="400" width="550"
                 xmlns:v="views.*"
                 backgroundColor="#EDE8F0" viewSourceURL="srcview/index.html">
     <mx:Script>
         <![CDATA[
             import mx.utils.URLUtil;
             import um.SSOEvent;
             import mx.core.UIComponent;
             import um.SSOManager;
             import mx.rpc.events.ResultEvent;
             import mx.utils.ObjectUtil;
             import mx.controls.Alert;

             [Bindable]
             private var _serverURL:String;

             private var _ssoManager:SSOManager;

             private var _progress:UIComponent;

             private var _loginPage:UIComponent;

             private function initApp():void{
                 _serverURL = determineServerUrl();
                 _ssoManager = new SSOManager(_serverURL);

                 _ssoManager.addEventListener(SSOEvent.AUTHENTICATION_FAILED,loginHandler);
                 _ssoManager.addEventListener(SSOEvent.AUTHENTICATION_SUCCESS,loginHandler);
                 _ssoManager.addEventListener(SSOEvent.AUTHENTICATION_REQUIRED,loginHandler);
                 _ssoManager.addEventListener(SSOEvent.LOGOUT_COMPLETE,loginHandler);
                 _ssoManager.addEventListener(SSOEvent.AUTHENTICATION_FAULT,loginHandler);

                 trace("[Main] Add the required event handlers for authentication");
                 _ssoManager.singleSignOn();

                 showBusy();
             }

             private function determineServerUrl():String
             {
                 var s:String ;
                 var appUrl:String = Application.application.url;
                 var givenUrl:String  = ExternalInterface.call("serverUrl.toString");
                 trace("[Main] Application url ["+appUrl+"] Given url ["+givenUrl+"]");
                 if(appUrl != null && appUrl.search("^http") != -1){
                     s = appUrl;
                 }
                 if(s == null){
                     s = givenUrl;
                 }
                 if(s== null){
                     s = "https://hiro-xp:8080/";
                 }
                 s = URLUtil.getFullURL(s,"/");
                 trace("[Main] Would be using ["+s+"] as serverUrl");
                 return s;
             }

             private function loginHandler(event:SSOEvent):void
             {
                 trace("[Main] Handling event "+event.type);
                 switch(event.type)
                 {
                     case SSOEvent.AUTHENTICATION_FAILED:
                         viewContent.selectedChild = login;
                         login.showLoginFailed();
                         break;
                     case SSOEvent.AUTHENTICATION_SUCCESS:
                         viewContent.selectedChild = remoting;
                         break;
                     case SSOEvent.AUTHENTICATION_REQUIRED:
                         viewContent.selectedChild = login;
                         break;
                     case SSOEvent.LOGOUT_COMPLETE:
                         viewContent.selectedChild = logout;
                         break;
                     case SSOEvent.AUTHENTICATION_FAULT:
                         Alert.show("Error doing authentication. Root error ["+event.rootEvent+"]","Authentication Fault",Alert.OK);
                 }
             }

             public function get ssoManager():SSOManager
             {
                 return _ssoManager;
             }

             public function showBusy():void
             {
                 viewContent.selectedChild = progress;
             }

             public function get serverUrl():String
             {
                 return _serverURL;
             }

         ]]>
     </mx:Script>
     <mx:ViewStack x="0" y="0" id="viewContent" >
         <v:login id="login" />
         <v:remoting id="remoting"  />
         <v:progress id="progress" />
         <v:logout id="logout"/>
     </mx:ViewStack>
 </mx:Application>

ISSOManager.as 파일 만들기 creating-the-issomanager-as-file

다음 코드는 ISSOManager.as 파일을 나타냅니다.

 package um
 {
     import flash.events.IEventDispatcher;

     /**
      * The <code>ISSOManager</code> expose operations related to Single Sign On (SSO) in AEM Forms
      * environment. The application should register appropriate <code>SSOEvent</code> handlers prior
      * to calling any of the following operations
      */
     public interface ISSOManager extends IEventDispatcher
     {
         /**
          * Tries to validate whether the user has an already existing session or not (SSO Scenarios). The application
          * may call this method during the initialization. In general this call would lead to one of the
          * following events getting dispatched
          * <ul>
          * <li>SSOEvent.AUTHENTICATION_SUCCESS - If a SSO session was found and valid
          * <li>SSOEvent.AUTHENTICATION_REQUIRED - No SSO session was found and as such authentication is required in
          * the form of username and password.
          * <li>SSOEvent.AUTHENTICATION_FAULT - Some error has occured while connecting to the server
          * </ul>
          */
         function singleSignOn():void;

         /**
          * Authenticates the user using username and password. It may lead to one of the following events
          * <ul>
          * <li>SSOEvent.AUTHENTICATION_SUCCESS - The authentication is successful and a session is established
          * <li>SSOEvent.AUTHENTICATION_FAILED - Authentication has failed
          * </ul>
          */
         function login(username:String, password:String):void;

         /**
          * Terminates the current session and logs out the user.
          */
         function logout():void;

         /**
          * Get the assertionId for the logged in user
          */
         function get assertionId():String;
     }
 }

SSOEvent.as 파일 생성 creating-the-ssoevent-as-file

다음 코드는 SSOEvent.as 파일을 나타냅니다.

 package um
 {
     import flash.events.Event;

     /**
      * The <code>SSOEvent</code> is dispatched for SSO related events
      */
     public class SSOEvent extends Event
     {
         /**
          * This type of event would be dispatched when the Authentication process is successful. Authentication
          * might have been done with SSO or username and password. As a response to this event the application
          * can show the welcome page to the user
          * The application may want to perform specific check for permission/role so as to verify the user is allowed.
          * So as a response to this event the application would do those checks and then only show the welcome page
          */
         public static const AUTHENTICATION_SUCCESS:String = "authenticationSuccess";

         /**
          * This type of event would be dispatched when authentication fails using the username, password.
          * As a response to this type of event an application can show an error message to the user.
          * This event would only happen when authentication is done using username and password and NOT in
          * SSO case.
          */
         public static const AUTHENTICATION_FAILED:String = "authenticationFailed";

         /**
          * This type of event would be dispatched when authentication using SSO is not achieved. And due to
          * that we require the user's username and password for authentication. As a response to this event
          * the application can show the login page to the user.
          */
         public static const AUTHENTICATION_REQUIRED:String = "authenticationRequired";

         /**
          * This type of event would be dispatched when logout is complete. As a response to this event the
          * application may show a logout page informing the user that he has been logged out. Or the application
          * can take the user back to login page
          */
         public static const LOGOUT_COMPLETE:String = "logoutComplete";

         /**
          * This type of event would be dispatched when ever there is a problem in doing Authentication. The root cause
          * can be obtained from the <code>rootEvent</code>.
          */
         public static const AUTHENTICATION_FAULT:String = "authenticationFault";

         private var _rootEvent:Event;

         public function SSOEvent(type:String, rootEvent:Event=null)
         {
             super(type,true,false);
             _rootEvent = rootEvent;
         }

         /**
          * The root event. If current event type is <code>AUTHENTICATION_FAULT</code> then it would be an
          * <code>IOErrorEvent</code> in other cases it would be complete event. Its basic use is to extract the root
          * cause if there is an authentication fault.
          */
         public function get rootEvent():Event
         {
             return _rootEvent;
         }
     }
 }

SSOManager.as 파일 만들기 creating-the-ssomanager-as-file

다음 코드는 SSOManager.as 파일을 나타냅니다.

 package um
 {
     import flash.events.Event;
     import flash.events.EventDispatcher;
     import flash.events.IOErrorEvent;
     import flash.external.ExternalInterface;
     import flash.net.URLLoader;
     import flash.net.URLLoaderDataFormat;
     import flash.net.URLRequest;
     import flash.net.URLVariables;

     import mx.utils.ObjectUtil;

     /**
      * Manages the SSO related operations and dispatches appropriate events. It would connect to the UM Filter/Servlet
      * at <code>um/login</code> The UM response would be of form of url encoded variables. It would look for
      * <code>authstate</code> value in the response and depending on that it would proceed.
      *
      * <p>If there is an IO_Error while initial attempt to UM then it would assume it as a 401 response. And it would
      * be assumed that SPNEGO based authenticatin is not working and therefore user would be shown a login page.
      */
     public class SSOManager extends EventDispatcher implements ISSOManager
     {
         private static const SSO_URL:String = "um/login";
         private static const SSO_LOGOUT_URL:String = "um/logout";
         private static const AUTH_COOKIE_NAME:String = "lcAuthToken";

         private var _serverUrl:String;
         private var _assertionId:String;

         /**
          * Constructs an SSOManager with the given server url.
          *
          * @param serverUrl - The uri of the server to connect to. it must be without any context path for example,
          * http://localhost:8080/. The SSOManager would directly append the path of UM exposed SSO url to it
          * for its operations
          */
         public function SSOManager(serverUrl:String)
         {
             _serverUrl = serverUrl;
         }

         public function singleSignOn():void
         {
             sendRequest(SSO_URL,true);
         }

         public function login(username:String, password:String):void
         {
             sendRequest(SSO_URL,false,
                 function(request:URLRequest,vars:URLVariables):void
                 {
                     vars.j_username = username;
                     vars.j_password = password;
                 }
             );
         }

         public function logout():void
         {
             sendRequest(SSO_LOGOUT_URL);
         }

         public function get assertionId():String
         {
             return _assertionId;
         }



         /**
          * Connects to the UM security service.
          */
         private function sendRequest(relativeUrl:String,authenticationRequest:Boolean=false, requestProcessor:Function=null):void
         {
             var loader:URLLoader = new URLLoader();
             loader.dataFormat = URLLoaderDataFormat.VARIABLES;
             var request:URLRequest = new URLRequest(_serverUrl + relativeUrl);
             trace("[SSOmanager] Contacting ["+request.url+"]");
             var vars:URLVariables = new URLVariables();
             vars.um_no_redirect = "true";
             request.data = vars;
             if(requestProcessor != null){
                 requestProcessor(request,vars);
             }

             loader.addEventListener(Event.COMPLETE,authHandler);
             //if its an authentication request then only treat io error as a possible 401
             //for others treat them as faults
             if(authenticationRequest){
                 loader.addEventListener(IOErrorEvent.IO_ERROR,httpAuthenticationHandler);
             }else{
                 loader.addEventListener(IOErrorEvent.IO_ERROR,authFaultHandler);
             }
             trace("[SSOmanager] Sending request "+ ObjectUtil.toString(request));
             loader.load(request);
         }

         private function authHandler(event:Event):void
         {
             var loader:URLLoader = URLLoader(event.target);
             var response:URLVariables = URLVariables(loader.data);
             trace("[SSOmanager] Processing response ["+ObjectUtil.toString(response)+"]");
             handleAuthResult(response["authstate"],response);
         }

         /**
          * Handles the IOErrorEvent. Flash would dispatch IOEvent in response to HTTP 401.
          * There is no way to distinguish it from the genuine IOError.
          */
         private function httpAuthenticationHandler(event:IOErrorEvent):void
         {
             trace("[SSOmanager] Processing IOErrorEvent ["+ObjectUtil.toString(event)+"]");
             handleAuthResult("CREDENTIAL_CHALLENGE");

         }

         /**
          * Dispatches appropriate <code>SSOEvent</code> on the basis of the <code>authstate</code>
          * value of the response.
          * The response is url encoded in for of
          * <pre>
          * authenticated=false&authstate=SPNEGO_CHALLENGE
          * </pre>
          * Depending on <code>authstate</code> the SSOEvent is dispatched
          */
         private function handleAuthResult(authState:String,response:URLVariables = null):void
         {
             trace("[SSOmanager] processing state "+authState);
             switch(authState)
             {
                 case "FAILED"  :
                     dispatchEvent(new SSOEvent(SSOEvent.AUTHENTICATION_FAILED));
                     break;
                 case "COMPLETE" :
                     _assertionId = response ? response["assertionid"] : null;
                     dispatchEvent(new SSOEvent(SSOEvent.AUTHENTICATION_SUCCESS));
                     break;
                 case "CREDENTIAL_CHALLENGE" :
                     dispatchEvent(new SSOEvent(SSOEvent.AUTHENTICATION_REQUIRED));
                     break;
                 case "LOGGED_OUT" :
                     dispatchEvent(new SSOEvent(SSOEvent.LOGOUT_COMPLETE));
                     break;
                 default:
                     dispatchEvent(new SSOEvent(SSOEvent.AUTHENTICATION_REQUIRED));
                     break;
             }
         }

         private function authFaultHandler(event:Event):void
         {
             dispatchEvent(new SSOEvent(SSOEvent.AUTHENTICATION_FAULT,event));
         }

     }
 }

UserManager.as 파일 만들기 creating-the-usermanager-as-file

다음 코드는 UserManager.as 파일을 나타냅니다.

 package um
 {
     import flash.events.Event;
     import mx.rpc.soap.WebService;
     import mx.rpc.soap.Operation;
     import mx.rpc.IResponder;
     import mx.rpc.events.FaultEvent;
     import mx.rpc.events.ResultEvent;
     import mx.rpc.soap.LoadEvent;

     public class UserManager
     {
         private var _ssoManager:ISSOManager;
         private var _serverUrl:String;

         public function UserManager(ssoManager:ISSOManager,serverUrl:String)
         {
             _serverUrl = serverUrl;
             _ssoManager = ssoManager;
         }

         public function retrieveAssertion(responder:IResponder):String
         {
             var assertionId:String = _ssoManager.assertionId;
             if(!assertionId)
             {
                 trace("[UserManager] AssertionId not found");
                 return null;
             }

             var ws:WebService = new WebService();
             var wsdl:String = _serverUrl+'soap/services/AuthenticationManagerService?wsdl&lc_version=8.2.1';
             ws.loadWSDL(wsdl);
             ws.addEventListener(LoadEvent.LOAD,
                 function(event:Event):void
                 {
                     trace("[UserManager] WSDL loaded");
                     var authenticate:Operation = ws.authenticateWithHttpToken as Operation;
                     authenticate.resultFormat = "e4x";
                     authenticate.addEventListener(ResultEvent.RESULT,
                         function(event:Event):void
                         {
                             responder.result(event);
                         }
                     );
                     authenticate.send({assertionId:assertionId});
                 }
             );

             ws.addEventListener(FaultEvent.FAULT,
                 function(event:Event):void
                 {
                     responder.fault(event);
                 }
             );
             return null;
         }
     }
 }

login.mxml 파일 만들기 creating-the-login-mxml-file

다음 코드는 login.mxml 파일을 나타냅니다.

 <?xml version="1.0" encoding="utf-8"?>
 <mx:Canvas  width="500" height="400">
     <mx:Script>
         <![CDATA[
             import mx.core.Application;
             public function showLoginFailed():void
             {
                 loginMessage.text = "Username or Password incorrect";
             }

             private function doLogin():void
             {
                 Application.application.ssoManager.login(j_username.text,j_password.text);
                 Application.application.showBusy();
             }

         ]]>
     </mx:Script>

     <mx:VBox height="113" width="244" x="128" y="144" horizontalAlign="center" verticalGap="10">
         <mx:HBox width="100%">
             <mx:HBox width="100%" verticalAlign="middle" horizontalAlign="center" height="32">
                 <mx:Label text="Username" fontWeight="bold"/>
                 <mx:TextInput id="j_username"/>
             </mx:HBox>
         </mx:HBox>
         <mx:HBox width="100%" height="33" horizontalAlign="center" horizontalGap="10" verticalAlign="middle">
             <mx:Label text="Password" fontWeight="bold"/>
             <mx:TextInput displayAsPassword="true" id="j_password"/>
         </mx:HBox>
         <mx:Button label="Login" click="doLogin()"/>
     </mx:VBox>
     <mx:Text x="128" y="122" id="loginMessage" width="230" height="14"/>
     <mx:Label x="154" y="65" text="AEM Forms SSO Demo" fontFamily="Georgia" fontSize="20" color="#0A0A0A"/>
 </mx:Canvas>

logout.mxml 파일 만들기 creating-the-logout-mxml-file

다음 코드는 logout.mxml 파일을 나타냅니다.

 <?xml version="1.0" encoding="utf-8"?>
 <mx:Canvas  width="500" height="400">
     <mx:Label x="97" y="188" text="You have successfully logged out from the application"/>

 </mx:Canvas>

progress.mxml 파일 만들기 creating-the-progress-mxml-file

다음 코드는 progress.mxml 파일을 나타냅니다.

 <?xml version="1.0" encoding="utf-8"?>
 <mx:Canvas >
     <mx:Label x="151" y="141" text="Wait...."/>
     <mx:SWFLoader source="LoadingCircle.swf" width="50" height="50" horizontalCenter="0" verticalCenter="0"/>
 </mx:Canvas>

remoting.mxml 파일 만들기 creating-the-remoting-mxml-file

다음 코드는 다음을 호출하는 remoting.mxml 파일을 나타냅니다 MyApplication/EncryptDocument 프로세스. 문서가 프로세스로 전달되므로 보안 문서를 AEM Forms으로 전달하는 애플리케이션 논리가 이 파일에 있습니다. (참조: 원격을 사용하여 프로세스를 호출할 보안 문서 전달.)

 <?xml version="1.0" encoding="utf-8"?>
 <mx:Canvas  width="664" height="400" creationComplete="initializeChannelSet()" xmlns:views="views.*">
     <mx:Script>

         <![CDATA[

             import mx.rpc.livecycle.DocumentReference;
             import flash.net.FileReference;
             import flash.net.URLRequest;
             import flash.events.Event;
             import flash.events.DataEvent;
             import mx.messaging.ChannelSet;
             import mx.messaging.channels.AMFChannel;
             import mx.rpc.events.ResultEvent;
             import mx.collections.ArrayCollection;
             import mx.rpc.AsyncToken;
             import um.UserManager;
             import mx.rpc.events.ResultEvent;
             import mx.rpc.events.FaultEvent;
             import mx.core.Application;
             import mx.rpc.Responder;
             import mx.utils.ObjectUtil;

             // Classes used in file retrieval
             private var fileRef:FileReference = new FileReference();
             private var docRef:DocumentReference = new DocumentReference();
             private var parentResourcePath:String = "/";
             //private var serverPort:String = "'[server]:[port]'";
             private var serverPort:String = "'[server]:[port]'";
             private var now1:Date;
             private var userManager:UserManager;

             // Define a ChannelSet object.
             public var cs:ChannelSet;

             // Holds information returned from AEM Forms
             [Bindable]
             public var progressList:ArrayCollection = new ArrayCollection();


             // Set up channel set to invoke AEM Forms.
             // This must be done before calling any service or process, but only
             // once for the entire application.
             private function initializeChannelSet():void {
                 cs = new ChannelSet();
                 cs.addChannel(new AMFChannel("remoting-amf", "https://" + serverPort + "/remoting/messagebroker/amf"));
                 EncryptDocument.channelSet = cs;

             //Get the user that is authenticated
             userManager = new UserManager(Application.application.ssoManager,Application.application.serverUrl);
             userManager.retrieveAssertion(
                     new mx.rpc.Responder(
                         function(event:ResultEvent):void
                         {
                             var name:String = XML(event.currentTarget.lastResult)..*::authenticatedUser.*::userid.text();
                             username.text = "Welcome "+name;
                         },
                         function(event:FaultEvent):void
                         {
                             mx.controls.Alert.show(event.fault.faultString,'Error')
                         }
                     )
                 );

             }

             // Call this method to upload the file.
             // This creates a file picker and lets the user select a PDF file to pass to the EncryptDocument process.
             private function uploadFile():void {
                 fileRef.addEventListener(Event.SELECT, selectHandler);
                 fileRef.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA,completeHandler);
                 fileRef.browse();
             }

             // Gets called for selected file. Does the actual upload via the file upload servlet.
             private function selectHandler(event:Event):void {
                 var authTokenService:RemoteObject = new RemoteObject("LC.FileUploadAuthenticator");
                 authTokenService.addEventListener("result", authTokenReceived);
                 authTokenService.channelSet = cs;
                 authTokenService.getFileUploadToken();
             }

             private function authTokenReceived(event:ResultEvent):void
             {
                 var token:String = event.result as String;
                 var request:URLRequest = DocumentReference.constructRequestForUpload("https://hiro-xp:8080", token);

                 try
                 {
                     fileRef.upload(request);
                 }
                 catch (error:Error)
                 {
                     trace("Unable to upload file.");
                 }
             }

             // Called once the file is completely uploaded.
             private function completeHandler(event:DataEvent):void {

                 // Set the docRefs url and referenceType parameters
                 docRef.url = event.data as String;
                 docRef.referenceType=DocumentReference.REF_TYPE_URL;
                 executeInvokeProcess();
             }

             //This method invokes the EncryptDocument process
             public function executeInvokeProcess():void {
                 //Create an Object to store the input value for the EncryptDocument process
                 now1 = new Date();

                 var params:Object = new Object();
                 params["inDoc"]=docRef;

                 // Invoke the EncryptDocument process
                 var token:AsyncToken;
                 token = EncryptDocument.invoke(params);
                 token.name = name;
             }


             // This method handles a successful conversion invocation
             public function handleResult(event:ResultEvent):void
             {

                 //Retrieve information returned from the service invocation
                 var token:AsyncToken = event.token;
                 var res:Object = event.result;
                 var dr:DocumentReference = res["outDoc"] as DocumentReference;
                 var now2:Date = new Date();

                 // These fields map to columns in the DataGrid
                 var progObject:Object = new Object();
                 progObject.filename = token.name;
                 progObject.timing = (now2.time - now1.time).toString();
                 progObject.state = "Success";
                 progObject.link = "<a href='" + dr.url + "'> open </a>";
                 progressList.addItem(progObject);
             }


             private function resultHandler(event:ResultEvent):void {
             // Do anything else here.

             }

             private function logout():void
             {
                 Application.application.ssoManager.logout();
                 Application.application.showBusy();
             }


         ]]>

     </mx:Script>

     <mx:RemoteObject id="EncryptDocument" destination="MyApplication/EncryptDocument" result="resultHandler(event);">
             <mx:method name="invoke" result="handleResult(event)"/>
     </mx:RemoteObject>


     <!--//This consists of what is displayed on the webpage-->
     <mx:Panel id="lcPanel" title="EncryptDocument  (Deprecated for AEM forms) AEM Forms Remoting Example"
           height="25%" width="25%" paddingTop="10" paddingLeft="10" paddingRight="10"
           paddingBottom="10">
         <mx:Label width="100%" color="blue"
                   id="username"/>

         <mx:DataGrid x="10" y="0" width="500" id="idProgress" editable="false"
                          dataProvider="{progressList}" height="231" selectable="false" >
         <mx:columns>
                 <mx:DataGridColumn headerText="Filename" width="200" dataField="filename" editable="false"/>
                 <mx:DataGridColumn headerText="State" width="75" dataField="state" editable="false"/>
                 <mx:DataGridColumn headerText="Timing" width="75" dataField="timing" editable="false"/>
                 <mx:DataGridColumn headerText="Click to Open" dataField="link" editable="false" >
                 <mx:itemRenderer>

                         <mx:Component>
                         <mx:Text x="0" y="0" width="100%" htmlText="{data.link}"/>
                         </mx:Component>
                     </mx:itemRenderer>
             </mx:DataGridColumn>
         </mx:columns>
     </mx:DataGrid>
     <mx:Button label="Select File" click="uploadFile()" />
     <mx:Button label="Logout"  click="logout()" />
     </mx:Panel>
 </mx:Canvas>

추가 정보 additional-information

다음 섹션은 클라이언트 응용 프로그램과 사용자 관리자 보안 서블릿 간의 통신을 설명하는 추가 세부 정보를 제공합니다.

새 인증 발생 a-new-authentication-occurs

이 경우 사용자는 클라이언트 애플리케이션에서 AEM Forms으로 처음 로그인합니다. (사용자와 관련된 이전 세션이 없습니다.) 다음에서 applicationComplete 이벤트, SSOManager.singleSignOn 사용자 관리자에게 요청을 보내는 메서드가 호출됩니다.

GET /um/login?um%5Fno%5Fredirect=true HTTP/1.1

사용자 관리자 보안 서블릿은 다음 값으로 응답합니다.

HTTP/1.1 200 OK

authenticated=false&authstate=CREDENTIAL_CHALLENGE

이 값에 대한 응답으로 SSOEvent.AUTHENTICATION_REQUIRED 값이 발송되었습니다. 그 결과, 클라이언트 애플리케이션은 사용자에게 로그인 화면을 디스플레이한다. 자격 증명이 User Manager 보안 서블릿에 다시 제출됩니다.

GET /um/login?um%5Fno%5Fredirect=true&j%5Fusername=administrator&j%5Fpassword=password HTTP/1.1

사용자 관리자 보안 서블릿은 다음 값으로 응답합니다.

 HTTP/1.1 200 OK
 Set-Cookie: lcAuthToken=53630BC8-F6D4-F588-5D5B-4668EFB2EC7A; Path=/
 authenticated=true&authstate=COMPLETE&assertionid=53630BC8-F6D4-F588-5D5B-4668EFB2EC7A

그 결과, authstate=COMPLETE the SSOEvent.AUTHENTICATION_SUCCESS 가 발송되었습니다. 클라이언트 애플리케이션은 필요한 경우 추가 처리를 수행할 수 있다. 예를 들어 사용자가 인증된 날짜 및 시간을 추적하는 로그를 만들 수 있습니다.

사용자가 이미 인증되었습니다. the-user-is-already-authenticated

이 경우 사용자가 이미 AEM Forms에 로그인한 다음 클라이언트 애플리케이션으로 이동합니다. 클라이언트 응용 프로그램은 시작 중에 User Manager 보안 서블릿에 연결합니다.

 GET /um/login?um%5Fno%5Fredirect=true HTTP/1.1
 Cookie: JSESSIONID=A4E0BCC2DD4BCCD3167C45FA350BD72A; lcAuthToken=53630BC8-F6D4-F588-5D5B-4668EFB2EC7A

사용자가 이미 인증되었으므로 사용자 관리자 쿠키가 있고 사용자 관리자 보안 서블릿으로 전송됩니다. 그러면 서블릿이 assertionId 값을 지정하고 유효한지 확인합니다. 유효한 경우 authstate=COMPLETE 가 반환됩니다. 그렇지 않은 경우 authstate=CREDENTIAL_CHALLENGE 가 반환됩니다. 다음은 일반적인 응답입니다.

 HTTP/1.1 200 OK
        authenticated=true&authstate=COMPLETE&assertionid=53630BC8-F6D4-F588-5D5B-4668EFB2EC7A

이 경우 로그인 화면은 표시되지 않고 대신 시작 화면으로 바로 이동합니다.

recommendation-more-help
19ffd973-7af2-44d0-84b5-d547b0dffee2