2012-05-30 5 views
6

우리는 (1) 동기 작업을위한 JSON/HTTP REST API와 (2) cometd API를 사용하여 브라우저와 웹 컨테이너간에 상호 작용하는 Jetty 8.1, dojo 및 cometd를 사용하는 웹 애플리케이션을 보유하고 있습니다. 서버로부터 수많은 이벤트를 수신합니다.HTTP 세션과 cometd 세션 페어링

cometd가 가능할 때마다 일반 HTTP 대신 websocket을 사용하기 때문에 특히 두 가지 다른 API의 인증 세션을 우아하게 관리하는 방법에 대해 완전히 명확하지 않습니다. 응용 프로그램은 표준 Jetty LDAP 모듈을 사용하여 양식 기반 인증을 사용하고 있습니다. 그래서 HTTP 관점에서 컨테이너는 다음과 같습니다 표준 JSESSIONID와 브라우저를 제공합니다 : 시몬 보르의 포스트는 cometd 핸드 셰이크 중에이 토큰을 전달하기 위해 권장되는 솔루션입니다 보인다 here을 바탕으로

Cookie: jsessionid=758E2FAD7C199D722DA8B5E243E0E27D 

무엇을하는 우리 하고있다.

우리가 가진 문제는 HTTP 세션과 Bayeux cometd 세션이라는 근본적으로 다른 두 가지 세션입니다. 잠재적 인 메모리 누수 및 보안 문제와 같은 이유로 우리는 이들이 한 마디로 종료되거나 "쌍으로"있기를 바랍니다. 사용자의 HTTP 세션이 종료 된 경우 해당 Bayeux 세션도 종료되도록하고 그 반대도 마찬가지입니다. 이 작업을 수행하는 권장 방법이 있습니까?

답변

11

HTTP 세션과 CometD 세션의 수명주기가 다릅니다. 예를 들어 임시 연결에 실패하면 CometD 세션이 실패하고 서버가 클라이언트에게 다시 핸드 셰이크하도록 요청하여 다른 CometD 세션 (동일한 사용자를 나타내지 만 다른 CometD가 clientId 인 세션). 같은 경우에 HttpSession은 그대로 유지됩니다.

이 점을 염두에두면 사용자 이름 인 HttpSession과 해당 ServerSession 사이의 매핑을 응용 프로그램 수준에서 유지 관리해야합니다. 이 매핑을 HttpCometDMapper이라고합시다. 새 사용자가 로그인 할 때마다 이름 (또는 사용자의 다른 고유 한 식별자) 인 HttpSession과 현재 ServerSession을 등록합니다. 두 단계 프로세스가 필요합니다. 사용자 이름과 HttpSession을 먼저 연결 한 다음 ServerSession과 동일한 사용자 이름을 연결해야합니다.

CometD 재 핸드 쉐이크가 수행되면 매퍼를 새로운 ServerSession으로 업데이트합니다.

당신은 그래서 파괴 때, 당신은 매퍼에서 현재 CometD ServerSession를 검색하고 그 위에 ServerSession.disconnect()를 호출하는 HttpSessionHttpSessionListener를 등록하여 두 개의 세션을 연결할 수 있습니다.

CometD에는 HttpSession과 같은 비활성 시간 제한 개념이 없기 때문에 viceversa가 약간 까다 롭습니다. 그것은 당신 자신의 논리로 응용 프로그램에서 구현되어야합니다.

한 그 일의 부분은 그처럼 ServerSessionRemoveListener를 등록하는 것입니다

serverSession.addListener(new ServerSession.RemoveListener() 
{ 
    public void removed(ServerSession session, boolean timeout); 
    { 
     if (!timeout) 
     { 
      // Explicitly disconnected, invalidate the HttpSession 
      httpCometDMapper.invalidate(session); 
     } 
    } 
}); 

이 청취자는 명시 적으로 클라이언트에서 연결을 끊습니다 (서버 - 재진입 조심) 감시합니다.

비 명시 적 연결 해제에 대해 동일한 메커니즘을 구현하는 것이 다소 어렵습니다.이 경우 timeout 매개 변수는 true이지만 일시적인 네트워크 오류 (클라이언트가 정상적으로 사라지는 것과 반대 됨)로 인해 발생할 수 있으며 동일한 사용자가 이미 ServerSession으로 다시 핸드 셰이크 할 수 있습니다.

이 경우 응용 프로그램 시간 초과가 문제를 해결할 수 있다고 생각합니다. 시간 제한으로 인해 ServerSession이 제거 된 것을 볼 때 해당 사용자를 기록하고 응용 프로그램 시간 초과를 시작합니다. 동일한 사용자가 다시 핸드 셰이크하면 응용 프로그램 시간 초과를 취소합니다. 그렇지 않으면 사용자가 실제로 사라지고 응용 프로그램 제한 시간이 만료되며 HttpSession도 무효화됩니다.

위의 내용은 아이디어와 제안 일뿐입니다. 실제 구현은 애플리케이션 세부 사항에 크게 의존합니다 (CometD가 제공하지 않는 이유입니다).

핵심 포인트는 매퍼 인 HttpSessionListenerRemoveListener이며 이러한 구성 요소의 수명주기를 알고 있습니다. 일단 관리하면 애플리케이션에 적합한 코드를 작성할 수 있습니다.

마지막으로, CometD는 에서 얻을 수있는 BayeuxContext 인스턴스를 통해 HttpSession과 상호 작용할 수있는 전송 불가시 방법을 가지고 있습니다. 나는 이것을보고 단순화 할 수 있는지, 특히 HttpSession에 저장된 토큰을 검색하는 것이 좋습니다.

0

임시 연결 실패 후 BayeuxClient를 생성 할 때 문제가 있습니까?

아래 코드로 시도해 볼 수 있습니다.

try { 
     log.info("Running streaming client example...."); 
     makeConnect(); 


    } catch (Exception e) { 
     handleException("Error while setup the salesforce connection.", e); 
    } 
} 



private void makeConnect() { 
    try{ 
     client = makeClient(); 
     client.getChannel(Channel.META_HANDSHAKE).addListener 
       (new ClientSessionChannel.MessageListener() { 
        public void onMessage(ClientSessionChannel channel, Message message) { 
         log.info("[CHANNEL:META_HANDSHAKE]: " + message); 
         boolean success = message.isSuccessful(); 
         if (!success) { 
          String error = (String) message.get("error"); 
          if (error != null) { 
           log.error("Error during HANDSHAKE: " + error); 
          } 

          Exception exception = (Exception) message.get("exception"); 
          if (exception != null) { 
           handleException("Exception during HANDSHAKE: ", exception); 
          } 
         } 
        } 
       }); 

     client.getChannel(Channel.META_CONNECT).addListener(
       new ClientSessionChannel.MessageListener() { 
        public void onMessage(ClientSessionChannel channel, Message message) { 
         log.info("[CHANNEL:META_CONNECT]: " + message); 
         boolean success = message.isSuccessful(); 

         if (!success) { 
          client.disconnect(); 
          makeConnect(); 
          String error = (String) message.get("error"); 
          if (error != null) { 
           //log.error("Error during CONNECT: " + error); 
          } 
         } 
        } 

       }); 

     client.getChannel(Channel.META_SUBSCRIBE).addListener(
       new ClientSessionChannel.MessageListener() { 
        public void onMessage(ClientSessionChannel channel, Message message) { 
         log.info("[CHANNEL:META_SUBSCRIBE]: " + message); 
         boolean success = message.isSuccessful(); 
         if (!success) { 
          String error = (String) message.get("error"); 
          if (error != null) { 
           makeConnect(); 
           log.error("Error during SUBSCRIBE: " + error); 
          } 
         } 
        } 
       }); 
     client.handshake(); 
     log.info("Waiting for handshake"); 
     boolean handshaken = client.waitFor(waitTime, BayeuxClient.State.CONNECTED); 
     if (!handshaken) { 
      log.error("Failed to handshake: " + client); 
     } 
     log.info("Subscribing for channel: " + channel); 
     client.getChannel(channel).subscribe(new MessageListener() { 
      public void onMessage(ClientSessionChannel channel, Message message) { 
       injectSalesforceMessage(message); 
      } 
     }); 
     log.info("Waiting for streamed data from your organization ..."); 
    }catch (Exception e) { 
     handleException("Error while setup the salesforce connection.", e); 
    } 

}