2017-06-23 1 views
2

J'ai ce serveur embarqué Jetty à mon localhost: 8008 et ai une autre application Angular à localhost: 4200 accédant. était capable d'utiliser CORS dans le code ci-dessous, en ajoutant le CrossOriginFilter à ce svrContext (où se trouvent mes services web).Jetty Embedded: Utilisation de CORS + authentification de base (ConstraintSecurityHandler)

Mais, si je mets ce sûr booléenne vrai, mon application angulaire donne un message comme:

OPTIONS http://localhost:8008/server/admin/session 401 (Unauthorized) scheduleTask @ zone.js:2263 webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ zone.js:410 onScheduleTask @ zone.js:300 webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ zone.js:404 webpackJsonp.../../../../zone.js/dist/zone.js.Zone.scheduleTask @ zone.js:235 webpackJsonp.../../../../zone.js/dist/zone.js.Zone.scheduleMacroTask @ zone.js:258 (anonymous) @ zone.js:2287 proto.(anonymous function) @ zone.js:1426 (anonymous) @ http.es5.js:1275 webpackJsonp.../../../../rxjs/Observable.js.Observable._trySubscribe @ Observable.js:57 webpackJsonp.../../../../rxjs/Observable.js.Observable.subscribe @ Observable.js:45 webpackJsonp.../../../../rxjs/operator/map.js.MapOperator.call @ map.js:54 webpackJsonp.../../../../rxjs/Observable.js.Observable.subscribe @ Observable.js:42 webpackJsonp.../../../../../src/app/login/login.component.ts.LoginComponent.loginAdmin @ login.component.ts:42 (anonymous) @ LoginComponent.html:3 handleEvent @ core.es5.js:12076 callWithDebugContext @ core.es5.js:13535 debugHandleEvent @ core.es5.js:13123 dispatchEvent @ core.es5.js:8688 (anonymous) @ core.es5.js:10850 schedulerFn @ core.es5.js:3647 webpackJsonp.../../../../rxjs/Subscriber.js.SafeSubscriber.__tryOrUnsub @ Subscriber.js:238 webpackJsonp.../../../../rxjs/Subscriber.js.SafeSubscriber.next @ Subscriber.js:185 webpackJsonp.../../../../rxjs/Subscriber.js.Subscriber._next @ Subscriber.js:125 webpackJsonp.../../../../rxjs/Subscriber.js.Subscriber.next @ Subscriber.js:89 webpackJsonp.../../../../rxjs/Subject.js.Subject.next @ Subject.js:55 webpackJsonp.../../../core/@angular/core.es5.js.EventEmitter.emit @ core.es5.js:3621 webpackJsonp.../../../forms/@angular/forms.es5.js.FormGroupDirective.onSubmit @ forms.es5.js:4801 (anonymous) @ LoginComponent.html:3 handleEvent @ core.es5.js:12076 callWithDebugContext @ core.es5.js:13535 debugHandleEvent @ core.es5.js:13123 dispatchEvent @ core.es5.js:8688 (anonymous) @ core.es5.js:9299 (anonymous) @ platform-browser.es5.js:2668 webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:424 onInvokeTask @ core.es5.js:3924 webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:423 webpackJsonp.../../../../zone.js/dist/zone.js.Zone.runTask @ zone.js:191 ZoneTask.invoke @ zone.js:486 login:1 XMLHttpRequest cannot load http://localhost:8008/server/admin/session. 

Réponse à la demande prévol ne passe pas vérification de contrôle d'accès: Non L'en-tête 'Access-Control-Allow-Origin' est présent sur la ressource demandée. L'origine 'http://localhost:4200' n'est donc pas autorisée accès. La réponse contenait le code d'état HTTP 401.

Qu'est-ce qui me manque? J'ai été capable d'utiliser les deux séparément - le SecurtiyHandler avec une même application d'origine, et une demande d'origine croisée (avec l'authentification de base désactivée).

Voici mon code:

public class JettyLauncher { 

    private static Server server; 
    private static Boolean prod = true; 
    private static String protocol = "https"; 
    private static String host = "localhost"; 
    private static int port = 8080; 
    private static String staticContent = ""; 
    private static String services = ""; 
    private static Boolean secure = true; 
    public static Resystoken tokenClass; 
    private static String realm = ""; 
    private static Boolean logToFile = false; 
    public static String url; 
    public static String usuario; 
    public static String senha; 

    public static void main(String[] args) throws Exception { 
     launch(); 
    } 

    public static void launch() throws IOException, Exception{ 

     String buildPath = new File("").getAbsolutePath(); 
     System.out.println("* Build path: " + buildPath); 

     prod = Boolean.valueOf(PropertySource.props.getProperty("resysclagem.launch.prod")); 
     String sufixoProd = ""; 

     if(prod){ 
      System.out.println("* Production mode ON (Prod mode)"); 
      sufixoProd = ".prod"; 
     } else { 
      System.out.println("* Not prod application (Dev mode)"); 
     } 

     services = PropertySource.props.getProperty("resysclagem.launch.services"); 
     staticContent = PropertySource.props.getProperty("resysclagem.webapps"); 
     secure = Boolean.valueOf(PropertySource.props.getProperty("resysclagem.launch.security.basicauth")); 
     realm = PropertySource.props.getProperty("resysclagem.realm.props"); 
     logToFile = Boolean.valueOf(PropertySource.props.getProperty("resysclagem.launch.logtofile")); 

     tokenClass = new Resystoken(
            PropertySource.props.getProperty("resysclagem.launch.token.key"), 
            Boolean.valueOf(PropertySource.props.getProperty("resysclagem.launch.security.requiretoken")) 
            ); 

     url = PropertySource.props.getProperty("resysclagem.bd"+ sufixoProd +".url"); 
     usuario = PropertySource.props.getProperty("resysclagem.bd"+ sufixoProd +".usuario"); 
     senha = PropertySource.props.getProperty("resysclagem.bd"+ sufixoProd +".senha"); // pensar em em maneira de encriptar 

     protocol = PropertySource.props.getProperty("resysclagem.launch"+ sufixoProd +".protocol"); 
     host = PropertySource.props.getProperty("resysclagem.launch"+ sufixoProd +".host"); 


     if(!host.equalsIgnoreCase("localhost")) { 
      host = Inet4Address.getLocalHost().getHostAddress(); 
     } 
     try { 
      port = Integer.valueOf(PropertySource.props.getProperty("resysclagem.launch"+ sufixoProd +".port")); 
     } catch(NumberFormatException nfe){ 
      String strPort = PropertySource.props.getProperty("resysclagem.launch"+ sufixoProd +".port"); 
      if(strPort.equalsIgnoreCase("heroku")){ 
       port = Integer.valueOf(System.getenv("PORT")); 
      } 
     } 


     server = new Server(port); 
     HandlerList a = new HandlerList(); 

     HttpConfiguration http_config = new HttpConfiguration(); 
     SslContextFactory sslContextFactory = new SslContextFactory(); 
     if(protocol.equalsIgnoreCase("https")){ 
      port = port + 1; 
      // https://github.com/eclipse/jetty.project/blob/jetty-9.3.x/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java 
      // HTTP Configuration 
      http_config.setSecureScheme("https"); 
      http_config.setSecurePort(port); // port 8443 
      http_config.setOutputBufferSize(32768); 
      http_config.setRequestHeaderSize(8192); 
      http_config.setResponseHeaderSize(8192); 
      http_config.setSendServerVersion(true); 
      http_config.setSendDateHeader(false); 

      // SSL Context Factory    
      String keystorePath = buildPath + File.separator + "resources" + File.separator + "reskey"; 
      File f = new File(keystorePath); 
      if(!f.exists()) { 
       throw new Exception("File doesn't exist at "+ keystorePath); 
      } 
      sslContextFactory.setKeyStorePath(keystorePath); 
      String obf = Password.obfuscate("resysadmin*$"); 
      sslContextFactory.setKeyStorePassword(obf); 
      sslContextFactory.setKeyManagerPassword(obf); 
      sslContextFactory.setTrustStorePath("resources" + File.separator + "/reskey"); 
      sslContextFactory.setTrustStorePassword("resysadmin*$"); 
      sslContextFactory.setExcludeCipherSuites("SSL_RSA_WITH_DES_CBC_SHA", 
        "SSL_DHE_RSA_WITH_DES_CBC_SHA", "SSL_DHE_DSS_WITH_DES_CBC_SHA", 
        "SSL_RSA_EXPORT_WITH_RC4_40_MD5", 
        "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", 
        "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", 
        "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"); 

      // SSL HTTP Configuration 
      HttpConfiguration https_config = new HttpConfiguration(http_config); 
      https_config.addCustomizer(new SecureRequestCustomizer()); 

      // SSL Connector 
      ServerConnector sslConnector = new ServerConnector(server, 
       new SslConnectionFactory(sslContextFactory,HttpVersion.HTTP_1_1.asString()), 
       new HttpConnectionFactory(https_config)); 
      sslConnector.setPort(port);  // 8443 
      server.addConnector(sslConnector); 
     } 

     //static content 
     ResourceHandler resourceHandler= new ResourceHandler(); 
     resourceHandler.setResourceBase(staticContent);  // webapps 
     resourceHandler.setDirectoriesListed(true); 
     resourceHandler.setWelcomeFiles(new String[]{"index.html"}); 
     ContextHandler webContext = new ContextHandler("/"); /* the server uri path */ 
     webContext.setHandler(resourceHandler); 

     //webservices 
     ResourceConfig config = new ResourceConfig(); 
     config.register(MultiPartFeature.class); 
     config.packages("resysclagem.ws.services"); 
     config.register(JacksonFeature.class); 
     ServletHolder servlet = new ServletHolder(new ServletContainer(config)); 
     ServletContextHandler svrContext = new ServletContextHandler(server, services); //context path, services == /server 
     svrContext.addServlet(servlet, "/*"); //server/endpoints 
     svrContext.setInitParameter("jersey.config.server.provider.packages", "com.jersey.jaxb,com.fasterxml.jackson.jaxrs.json"); 
     svrContext.setInitParameter("com.sun.jersey.api.json.POJOMappingFeature", "true"); 
     /* 
     * TODO aqui: posteriormente, autorizar apenas o domínio do módulo WEB a fazer alterações aqui 
     */ 
     FilterHolder holder = new FilterHolder(new CrossOriginFilter()); 
     holder.setInitParameter("allowedMethods", "GET,POST,PUT,DELETE,HEAD,OPTIONS"); 
     //holder.setInitParameter("allowedOrigins", "http://localhost:4200"); 
     holder.setInitParameter("allowedHeaders", "Content-Type, Accept, X-Requested-With"); 
     svrContext.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST)); 

     a.addHandler(webContext); // index 
     a.addHandler(svrContext); 

     // basic authentication - ver resysclagem.realm.props 
     if (secure){ 

      HashLoginService loginService = new HashLoginService("ResysRealm", 
                     realm); 
      server.addBean(loginService); 

      ConstraintSecurityHandler security = new ConstraintSecurityHandler(); 


      // This constraint requires authentication and in addition that an 
      // authenticated user be a member of a given set of roles for 
      // authorization purposes. 
      Constraint constraint = new Constraint(); 
      constraint.setName("auth"); 
      constraint.setAuthenticate(true); 
      constraint.setRoles(new String[] { "user", "admin" }); 

      // Binds a url pattern with the previously created constraint. The roles 
      // for this constraing mapping are mined from the Constraint itself 
      // although methods exist to declare and bind roles separately as well. 
      ConstraintMapping mapping = new ConstraintMapping(); 
      mapping.setPathSpec("/*"); 
      mapping.setConstraint(constraint); 
      security.setConstraintMappings(Collections.singletonList(mapping)); 
      security.setAuthenticator(new BasicAuthenticator()); //base64 
      security.setLoginService(loginService); 
      /* 
      HashLoginService login = new HashLoginService(); 
      login.setName("Test Realm"); 
      login.setConfig("./resources/realm.properties"); 
      login.setHotReload(false); 
      server.addBean(login); 
      */ 

      security.setHandler(a); 
      server.setHandler(security);    

     } else { 
      System.out.println("* Warn: services and static content are not secured by authentication"); 
      server.setHandler(a); 
     } 

     try { 
      if(logToFile){   
       System.out.println("* From here, output will be printed on log file instead of console."); 
       PrintStream out = new PrintStream(new FileOutputStream("launchLog.txt")); 
       System.setOut(out); 
      } 
      server.start(); 
      System.out.println("* Up at "+ host +":"+ port); 
      server.join(); 
     } catch (Exception e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
      throw e; 
     } 
    } 

    public static boolean isRunning() { 
     if (server == null){ 
      return false; 
     } 
     return server.isRunning(); 
    } 

} 

Répondre

2

Le problème est que les blocs de gestionnaire de sécurité toutes les demandes non authentifiées, y compris CORS un message pré-vol, qui aspire.

En résumé, Jetty ne supporte pas cette combinaison (HTTP Basic auth + CORS) sortie de l'emballage. Vous devrez écrire du code personnalisé pour le faire.

+0

Est-il possible d'autoriser uniquement certaines requêtes (comme OPTIONS, qui est utilisée pour les messages de contrôle en amont) pour contourner SecurityHandler? Je vais essayer un code personnalisé, après tout, je pense que votre réponse est suffisamment claire pour être acceptée :) –

+1

@RodrigoNantes: Je suis sûr qu'il existe un moyen de contourner le SecurityHandler pour les requêtes OPTIONS, mais j'ai abandonné après quelques essaie. : - Je vous recommande de poster sur les forums de jetée, puis de répondre ici si/quand vous obtenez une réponse. ;) J'ai vu quelques exemples avancés de web.xml que j'ai essayé d'imiter par programmation, mais pas de chance. –

0

Dans le cas où quelqu'un d'autre se heurte à ce non-sens. Vous devez ajouter un gestionnaire à votre jetty.xml. Voici un exemple Handler (https://github.com/datadidit/jaxrs-dynamic-security/blob/master/jetty-cors-handler/src/main/java/com/datadidit/cors/CORSHandler.java).

+0

Où dois-je ajouter/définir ce gestionnaire sur une jetée intégrée? J'ai essayé cette approche et j'essayais aussi avec un CORSFilter (qui implémente ContainerResponseFilter), mais les deux jusqu'à présent n'ont pas réussi –

+0

Avez-vous regardé ce web.xml (https://github.com/datadidit/jaxrs-dynamic-security /blob/master/jaxrs-basic-example/src/main/webapp/WEB-INF/web.xml). Devrait être capable de faire quelque chose de similaire dans votre Embedded Jetty. – mkwyche