2017-03-07 65 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

非常感谢您的回答。这正是我所寻找的:-)。 –

相关问题