2017-03-07 7 views
0

をWebSocketのセキュア:のWebSocketセキュリティ - 私は、Java EEでのWebSocketエンドポイントを設定するには、このチュートリアルに従ったJava EEで

https://technology.amis.nl/2013/06/22/java-ee-7-ejb-publishing-cdi-events-that-are-pushed-over-websocket-to-browser-client/

明白な理由のために、セキュリティに関して行うことにいくつかのより多くの作業が(そこにあります、SSLなし、アクセス制限/認証など)。

だから私の目標は、SSL(WSS://ではなく、WSの://)を使用して

  • でのWebSocketのセキュリティを向上させることである -
  • セットアップユーザー真偽(web.xml)を行って -
  • を行ってを
  • は、SSL通信(web.xml)を強制 -
  • を行うトークン(限られた寿命)
とのWebSocket接続を確保します

私の質問: LoginBeanで作成したトークンをServerEndpointで確認するにはどうすればよいですか?

ボーナス質問: Java EEのWebソケットを保護するための重要な部分がありますか?

これは私がこれまで持っているものです。

ServerEndpoint

import javax.websocket.server.ServerEndpoint; 

@ServerEndpoint("/user/endpoint/{token}") 
public class ThisIsTheSecuredEndpoint { 

    @OnOpen 
    public void onOpen(@PathParam("token") String incomingToken, 
    Session session) throws IOException { 

     //How can i check if the token is valid? 

    }  
} 

LoginBean

@ManagedBean 
@SessionScoped 
public class LoginBean { 

public String login() { 

    FacesContext facesContext = FacesContext.getCurrentInstance(); 
    HttpServletRequest request = (HttpServletRequest)facesContext.getExternalContext().getRequest(); 

    try { 
     request.login("userID", "password"); 

     HttpSession session = request.getSession(); 

     // here we put the token in the session 
     session.setAttribute("token", "someVeeeeryLongRandomValue123hfgrtwpqllkiw"); 


    } catch (ServletException e) { 
     facesContext.addMessage(null, new FacesMessage("Login failed.")); 
     return "error"; 
    } 

    return "home"; 
} 

}

Javascipt

これは私がのWebSocketへの接続に使用するコードです:

// use SSL 
// retrive the token from session via EL-expression #{session.getAttribute("token")} 
var wsUri = "wss://someHost.com/user/endpoint/#{session.getAttribute("token")}"; 
var websocket = new WebSocket(wsUri); 

websocket.onerror = function(evt) { onError(evt) }; 

function onError(evt) { 
    writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data); 
} 

// For testing purposes 
var output = document.getElementById("output"); 
websocket.onopen = function(evt) { onOpen(evt) }; 

function writeToScreen(message) { 
    output.innerHTML += message + "<br>"; 
} 

function onOpen() { 
    writeToScreen("Connected to " + wsUri); 
} 

ウェブ-XML:

で "/ユーザー/ *" ディレクトリを確保ログインしてSSL通信を実施する

<security-constraint> 
    ... 
    <web-resource-name>Secured Area</web-resource-name> 
    <url-pattern>pathToSecuredDicrtoy</url-pattern>  
    ...  
    <user-data-constraint> 
     <transport-guarantee>CONFIDENTIAL</transport-guarantee> 
    </user-data-constraint> 
    ... 
</security-constraint> 
<login-config> 
    <auth-method>FORM</auth-method> ...   
</login-config> 

注:私はJSFを使用しています

フィードバックは高く評価されます。

+0

*の.js ' を 'web.xml'に変換しています。私の使用目的では、ページのアクセス速度が非常に低いので、これは大丈夫です。 –

答えて

1

認証目的でサーブレットフィルタを使用できます。

次に、chatエンドポイントを保護するために書いたフィルタの例を示します。それはaccess-tokenというクエリパラメータからアクセストークンを抽出し、AuthenticatorというBeanにトークン検証を委任します。

あなたは簡単にあなたのニーズに適応させることができます。アプリケーションが利用可能であるhere

@ServerEndpoint("/chat") 
public class ChatEndpoint { 

    private static final Set<Session> sessions = 
      Collections.synchronizedSet(new HashSet<>()); 

    @OnOpen 
    public void onOpen(Session session) { 
     sessions.add(session); 
     String username = session.getUserPrincipal().getName(); 
     welcomeUser(session, username); 
    } 

    ... 

} 

/** 
* Access token filter for the chat websocket. Requests without a valid access token 
* are refused with a <code>403</code>. 
* 
* @author cassiomolin 
*/ 
@WebFilter("/chat/*") 
public class AccessTokenFilter implements Filter { 

    @Inject 
    private Authenticator authenticator; 

    @Override 
    public void init(FilterConfig filterConfig) throws ServletException { 

    } 

    @Override 
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, 
      FilterChain filterChain) throws IOException, ServletException { 

     HttpServletRequest request = (HttpServletRequest) servletRequest; 
     HttpServletResponse response = (HttpServletResponse) servletResponse; 

     // Extract access token from the request 
     String token = request.getParameter("access-token"); 
     if (token == null || token.trim().isEmpty()) { 
      returnForbiddenError(response, "An access token is required to connect"); 
      return; 
     } 

     // Validate the token and get the user who the token has been issued for 
     Optional<String> optionalUsername = authenticator.getUsernameFromToken(token); 
     if (optionalUsername.isPresent()) { 
      filterChain.doFilter(
        new AuthenticatedRequest(
          request, optionalUsername.get()), servletResponse); 
     } else { 
      returnForbiddenError(response, "Invalid access token"); 
     } 
    } 

    private void returnForbiddenError(HttpServletResponse response, String message) 
      throws IOException { 
     response.sendError(HttpServletResponse.SC_FORBIDDEN, message); 
    } 

    @Override 
    public void destroy() { 

    } 

    /** 
    * Wrapper for a {@link HttpServletRequest} which decorates a 
    * {@link HttpServletRequest} by adding a {@link Principal} to it. 
    * 
    * @author cassiomolin 
    */ 
    private static class AuthenticatedRequest extends HttpServletRequestWrapper { 

     private String username; 

     public AuthenticatedRequest(HttpServletRequest request, String username) { 
      super(request); 
      this.username = username; 
     } 

     @Override 
     public Principal getUserPrincipal() { 
      return() -> username; 
     } 
    } 
} 

チャットエンドポイントは、のようなものでした。私は `追加javascriptのエル評価作業の は の のJSPようにするに

+1

ありがとうございます。これはまさに私が探していたものでした:-)。 –

関連する問題