2017-03-07 55 views
0

我也跟着上設置與Java EE的WebSocket的端點本教程:的WebSocket安全 - 安全的一個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通過

  • 改善的WebSocket安全(WSS://而不是WS://) - 完成
  • 設置用戶認證(web.xml中) - 完成
  • 執行SSL通信(web.xml)中 - 進行
  • 確保與令牌(有限的壽命)
的WebSocket連接

我的問題:如何驗證我在ServerEndpoint的LoginBean中創建的令牌?

Bonus問題:我是否錯過了在Java EE中確保websockets安全的一些重要部分?

這是我到目前爲止有:

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

爲了使我加入' JSP *的.js ' JavaScript的EL評價工作 到'web.xml'對於我的用例來說這沒問題,因爲頁面的訪問速度非常低。 –

回答

1

您可以使用Servlet過濾器進行身份驗證。

下面是我前段時間爲保護chat端點而編寫的過濾器示例。它從稱爲access-token的查詢參數中提取訪問令牌,並將令牌驗證委託給名爲Authenticator的bean。

你可以很容易地適應您的需求:

/** 
* 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; 
     } 
    } 
} 

聊天終點是一樣的東西:

@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); 
    } 

    ... 

} 

的應用程序可用here

+1

非常感謝您的回答。這正是我所尋找的:-)。 –