2017-03-07 4 views
0

folgte ich dieses Tutorial zum Einrichten eines websocket Endpunkt mit Java EE up:websocket Sicherheit - Sichern Sie sich einen Websocket in Java EE

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

Aus offensichtlichen Gründen gibt es einige mehr Arbeit in Bezug auf die Sicherheit getan werden (zB keine SSL und Zugangsbeschränkung/Authentifizierung).

So ist mein Ziel websocket Sicherheit von

  • mit SSL zu verbessern (WSS: // statt ws: //) - fertig
  • Setup-Benutzerauthentifizierung (web.xml) - fertig
  • SSL-Kommunikation (web.xml) erzwingen - fertig
  • die websocket Verbindung mit einem Token (begrenzte Lebensdauer) sichern

Meine Frage: Wie kann ich das Token, das ich in der LoginBean am ServerEndpoint erstellt habe, überprüfen?

Bonusfrage: Habe ich einige wichtige Teile bei der Sicherung von Websockets in Java EE vermisst?

Dies ist, was ich bisher:

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

dies ist der Code, den ich verwenden möchte den websocket verbinden:

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

web-xml:

sichern "/ user/*" Verzeichnis mit einem Anmelden und SSL-Kommunikation erzwingen

<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> 

Hinweis: Ich verwende JSF

Jede Rückmeldung würde sehr geschätzt werden.

+0

Javascript el Auswertung Arbeit mache ich 'hinzugefügt jsp * js ' zu der 'web.xml' Für meinen Anwendungsfall ist das ok, da die Seite sehr niedrige Zugriffsraten hat. –

Antwort

1

Sie könnten einen Servlet-Filter für Authentifizierungszwecke verwenden.

Hier ist ein Beispiel für einen Filter, den ich vor einiger Zeit geschrieben habe, um einen chat Endpunkt zu schützen. Es extrahiert das Zugriffstoken aus einem Abfrageparameter namens access-token und delegiert die Tokenvalidierung an eine Bean namens Authenticator.

Sie können es auf Ihre Bedürfnisse leicht anpassen:

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

Der Chat Endpunkt war so etwas wie:

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

    ... 

} 

Die Anwendung verfügbar here ist.

+1

Vielen Dank für Ihre Antwort. Genau das habe ich gesucht :-). –