2017-05-09 5 views
18

Ich habe Probleme mit der Integration der Federsicherheit (nämlich des Login-Teils) in meiner Webanwendung. Mein BackEnd läuft unter localhost: 8080 und das FrontEnd (Ionic 2 mit AngularJS 2) läuft unter localhost: 8100. Ich konnte einloggen (zumindest glaube ich so), aber der Rest der Anfragen nicht mehr zur Verfügung, und ich bekomme die folgenden Fehler in der Chrome-Entwicklerkonsole:Spring Security Login mit Java und Angular2

XMLHttpRequest cannot load http://localhost:8080/company/getAll. Response for preflight is invalid (redirect) 

Wenn mit Postman testet es scheint zu funktionieren, ich einloggen und dann kann ich POST-Anfragen unter http://localhost:8080/company/getAll ausführen und alles funktioniert wie vorgesehen.

Vermutlich vermisse ich etwas (wie ein Token), kann aber nicht herausfinden, was. Ich bin sowohl mit Ionic als auch mit Spring Security neu, also bitte vergib mir, wenn das etwas Triviales ist. Ich habe versucht, verschiedene Tutorials zu googlen, konnte aber nichts finden (die meisten benutzten JSP).

Wie könnte ich das zur Arbeit bringen? Wie sollen meine Anfragen vom Frontend aussehen?

Hier ist meine Login-Methode vom Back-End-Controller:

@Configuration 
@EnableWebSecurity 
@ComponentScan("com.SAB.service") 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

    @Autowired 
    private UserDetailsService userDetailsService; 

    @Bean 
    public BCryptPasswordEncoder bCryptPasswordEncoder() { 
     return new BCryptPasswordEncoder(); 
    } 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http 
      .authorizeRequests() 
      .antMatchers("/resources/**", "/registration").permitAll() 
      .anyRequest().authenticated() 
      .and() 
      .formLogin() 
      .loginPage("/user/login") 
      .permitAll() 
      .and() 
      .logout() 
      .permitAll(); 
     http 
      .csrf().disable(); 
     http.formLogin().defaultSuccessUrl("http://localhost:8100/", true); 
     //.and().exceptionHandling().accessDeniedPage("/403") 
     //.and().sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true); 
    } 

    @Autowired 
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 
     auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); 
    } 
} 

Und schließlich mein FrontEnd Code verantwortlich für die Anmeldung:

login(){ 
    var body = 'username='+this.loginForm.controls['username'].value+'&password='+this.loginForm.controls['password'].value; 
    var headers = new Headers(); 
    headers.append('Content-Type', 'application/x-www-form-urlencoded'); 
    //headers.append("Access-Control-Allow-Origin", "*"); 

    this.http 
     .post('http://localhost:8080/user/login', 
     body, { 
      headers: headers 
     }) 
     .subscribe(data => { 
     console.log('ok'); 
     console.log(data) 
     }, error => { 
     console.log(JSON.stringify(error.json())); 
     }); 
    } 

@RequestMapping(value = "/login", method = RequestMethod.GET) 
    @CrossOrigin(origins = "*") 
    public ResponseEntity<GeneralResponse> login(Model model, String error, String logout) { 
     System.out.println("LOGIN"+model.toString()); 

     if (error != null) { 
      System.out.println("1.IF"); 
      model.addAttribute("error", "Your username and password is invalid."); 
     } 

     if (logout != null) { 
      System.out.println("2.IF"); 
      model.addAttribute("message", "You have been logged out successfully."); 
     } 

     return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new GeneralResponse(true, "FAILURE: Error")); 
    } 

Das ist mein WebSecurityConfig ist Wenn Sie weitere Details benötigen, lassen Sie es mich wissen und ich werde sie zur Verfügung stellen.

Vielen Dank im Voraus!

ich die Access-Controll-Origin-Fehler nicht bekommen, aber ich habe versucht, es trotzdem zu den Kommentaren nach ändern und ich jetzt eine „Invalid CORS Anfrage“

Request und Response-Header für die Anmeldung von Postbote: enter image description here

EDIT: Hier sind die Spring Security-Protokolle. Spring registriert jedoch nur eine OPTIONS-Anforderung und kein POST, obwohl das FrontEnd POST aufruft.

************************************************************ 

Request received for OPTIONS '/login': 

[email protected] 

servletPath:/login 
pathInfo:null 
headers: 
host: localhost:8080 
connection: keep-alive 
access-control-request-method: POST 
origin: http://localhost:8100 
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36 
access-control-request-headers: access-control-allow-origin 
accept: */* 
referer: http://localhost:8100/ 
accept-encoding: gzip, deflate, sdch, br 
accept-language: en-US,en;q=0.8 


Security filter chain: [ 
    WebAsyncManagerIntegrationFilter 
    SecurityContextPersistenceFilter 
    HeaderWriterFilter 
    CorsFilter 
    LogoutFilter 
    UsernamePasswordAuthenticationFilter 
    RequestCacheAwareFilter 
    SecurityContextHolderAwareRequestFilter 
    AnonymousAuthenticationFilter 
    SessionManagementFilter 
    ExceptionTranslationFilter 
    FilterSecurityInterceptor 
] 


************************************************************ 
2017-05-12 19:18:28.527 DEBUG 16500 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy  : /login at position 1 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter' 
2017-05-12 19:18:28.527 DEBUG 16500 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy  : /login at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter' 
2017-05-12 19:18:28.527 DEBUG 16500 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists 
2017-05-12 19:18:28.527 DEBUG 16500 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created. 
2017-05-12 19:18:28.529 DEBUG 16500 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy  : /login at position 3 of 12 in additional filter chain; firing Filter: 'HeaderWriterFilter' 
2017-05-12 19:18:28.529 DEBUG 16500 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy  : /login at position 4 of 12 in additional filter chain; firing Filter: 'CorsFilter' 
2017-05-12 19:18:28.541 DEBUG 16500 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.se[email protected]5baaaa2b 
2017-05-12 19:18:28.541 DEBUG 16500 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession. 
2017-05-12 19:18:28.541 DEBUG 16500 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed 

UPDATE: So nach dem Hinzufügen der CORSFilter in der akzeptierte Antwort schlug ich hatte auch meine Wünsche im Frontend, damit das Cookie mit dem JSESSIONID ändern bei jeder Anfrage gesendet werden. Bassistisch musste ich ALLES zu meinen Anfragen hinzufügen: let options = new RequestOptions({ headers: headers, withCredentials: true });

+1

Dies ist definitiv ein CORS-Konfigurationsproblem. – galovics

+0

hast du http://stackoverflow.com/questions/33645511/why-my-ajax-showing-preflight-is-invalid-redirect-error –

Antwort

8

Dies scheint ein Problem mit der CORS-Konfiguration zu sein. Der Browser führt die OPTION-Preflight-Anforderung vor dem eigentlichen GET-Aufruf durch. Ich habe nicht viel Wissen in diesem Bereich, aber du könntest versuchen, den Filter unten zum Frühlingsrückende hinzuzufügen.

public class CORSFilter extends GenericFilterBean { 

    public void doFilter(ServletRequest request, ServletResponse res, FilterChain chain) 
     throws IOException, ServletException { 
     HttpServletRequest req = (HttpServletRequest) request; 
     HttpServletResponse response = (HttpServletResponse) res; 
     response.addHeader("Access-Control-Allow-Origin", "*"); 
     response.addHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE"); 
     response.addHeader("Access-Control-Max-Age", "3600"); 
     response.addHeader("Access-Control-Allow-Headers", "X-Requested-With, X-Auth-Token"); 
     response.addHeader("Access-Control-Allow-Credentials", "true"); 

     if(req.getMethod().equalsIgnoreCase("options")){ 
      return; 
     } 
     chain.doFilter(request, response); 
    } 
} 

Und fügen Sie die folgende Zeile in der configure-Methode Ihrer WebSecurityConfig hinzu.

.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class);

+0

Haben Sie einmal versucht, nach dem Entfernen der Bedingung If (req.getMethod()) .equalsIgnoreCase ("options")) 'vom CORS-Filter, kann aufgrund dessen" Unauthorized "kommen. Ansonsten sehe ich keinen Grund, warum es nach dem Hinzufügen dieses Filters nicht mehr erlaubt ist. Grundsätzlich, was ich hier versuche, ist 'Option' Http-Methode mit 'Access-Control-Allow-Methoden' Header, weil es für Preflight-Anfrage erfordert. –

+0

ok, also nach massiven Änderungen und Hinzufügen der Corsfilter, hat es endlich funktioniert –

1

Die Lösung ist sehr einfach. Der Benutzername und das Passwort für POST müssen als FormData und nicht als Teil von body übergeben werden. Ich hatte das gleiche Problem mit meiner App und nach einem langen Kampf habe ich ein HTML-Formular erstellt und einen POST gemacht, dann funktionierte es wie ein Zauber.

Siehe unten Live-Beispiel. Sie sollten in der Lage sein, sich mit einer beliebigen ID anzumelden und zu versuchen, sich einzuloggen und es in Ihrem Browser zu veröffentlichen. Es verwendet Spring-Boot für Serverseite und Angular 4 auf UI.

Live Demo:http://shop-venkatvp.rhcloud.com/#/

Anmeldung HTML:https://github.com/reflexdemon/shop/blob/master/src/app/login/login.component.html#L7-L31

Spring Security-Konfiguration:https://github.com/reflexdemon/shop/blob/master/src/main/java/org/shop/WebSecurityConfig.java#L20-L34

Login-Controller:https://github.com/reflexdemon/shop/blob/master/src/main/java/org/shop/page/controller/LoginController.java

Ich hoffe, das ist hilfreich.

+1

Login funktioniert, aber das Problem besteht: für alle anderen Anfragen sendet es nicht die JSESSIONID Cookie als Teil der Anfragen –

1

Haben Sie versucht, OPTIONS-Anfragen explizit zuzulassen? Gefällt mir:

protected void configure(HttpSecurity http) throws Exception { 
    ... 
    http.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll(); 
    ... 
} 
1

Ich bin gerade dabei, einen Angular 4 und Spring Boot Integration Artikel zu schreiben. Ich habe den Artikel noch nicht fertig, aber der Quellcode ist fertig. Ich bin mir nicht sicher, warum Sie versuchen, Ihre Anwendung von zwei verschiedenen Hosts zu bedienen, dies verkompliziert unnötigerweise Ihre Anwendung, indem sie eine Cors-Anforderung einführt und zwei Sicherheitskontexte verwalten muss. Wenn das keine absolute Voraussetzung ist, würde ich alles vom selben Server aus bedienen.

Werfen Sie einen Blick auf dieses Github-Projekt für eine vollständige Integration von Winkel- und Federbalg: https://github.com/tschiman/tutorials/tree/master/spring-cloud/spring-cloud-bootstrap/gateway. Du kannst die Zuul-Sachen dort ignorieren, wenn du das nicht brauchst. Es hat keinen Einfluss auf die Unterstützung der eckigen UI.

Wenn Sie csrf aktivieren, und Sie sollten, schauen Sie sich diese verpflichten: be4b206478c136d24ea1bf8b6f93da0f3d86a6c3

Wenn dass das Lesen nicht diese machen Sinn die Schritte, die Sie folgen müssen:

  1. Bewegen Sie Quellcode in Ihre Spring-Boot-App
  2. Ändern Sie Angular-CLI.JSON-Datei, um ein Ausgabeverzeichnis in Ihren Ressourcen statische oder Ressourcen öffentlichen Ordner zu haben: "outDir":"../../resources/static/home" Dies legt das Ausgabeverzeichnis für, wenn Sie ng Build von der Befehlszeile mit ausführen der Winkel kli.
  3. Ändern Sie die Standard-URL Ihres Login-Eintrags auf /home/index.html um. .formLogin().defaultSuccessUrl("/home/index.html", true) Dies erzwingt eine erfolgreiche Anmeldung, um immer auf Ihre eckige App umzuleiten.
  4. Wenn Sie den Aufbau Ihrer Winkel App in Maven automatisieren dann dieses Plugin Ihre POM-Datei hinzu:

    <plugin> 
         <artifactId>maven-antrun-plugin</artifactId> 
         <executions> 
          <execution> 
           <phase>generate-resources</phase> 
           <configuration> 
            <tasks> 
             <exec executable="cmd" osfamily="windows" dir="${project.basedir}/src/main/angular/ui"> 
              <arg value="/c"/> 
              <arg value="ng"/> 
              <arg value="build"/> 
             </exec> 
             <exec executable="/bin/sh" osfamily="mac" dir="${project.basedir}/src/main/angular/ui"> 
              <arg value="-c"/> 
              <arg value="ng build"/> 
             </exec> 
            </tasks> 
           </configuration> 
           <goals> 
            <goal>run</goal> 
           </goals> 
          </execution> 
         </executions> 
        </plugin> 
    

Wenn Sie weitere Fragen bin ich glücklich weiter zu reagieren, wenn dies nicht genug war, damit du es funktionierst.

+1

Ich denke, die Probleme beginnen, wenn Sie wollen Verwenden Sie Live-Reload (wie Webpack oder was auch immer) für die UI-Entwicklung. Mit dieser Konfiguration ist es nicht möglich und Sie müssen Java Server neu starten, was die Entwicklung verlangsamt. –

0

Vor allem - das ist definitiv ein CORS-Konfigurationsproblem, weil Sie Backend und Frontend auf verschiedenen Adressen ausführen.

Sie können twick backend, um CORS-Anfragen zu unterstützen, aber das ist eine schmutzige Lösung, vor allem da Sie wahrscheinlich CORS in Produktion Version von Ihnen nicht wollen. Viel sauberer Ansatz würde sein, Frontend oder Backend zu Server als Proxy für die andere zu konfigurieren.

Ich bevorzuge es, Frontend zu Proxy-Anfragen zu Backend zu konfigurieren. Auf diese Weise hat es keine Auswirkungen auf die Produktionsversion der App.

Der nächste Teil geht davon aus, dass Sie angle-cli verwenden (wie Sie sollten mit angular2 App).

Sie haben einen Abschnitt in ihren Dokumenten speziell für Proxy-Konfiguration: https://www.npmjs.com/package/angular-cli#proxy-to-backend.

Ich habe alle meine REST-Services innerhalb /api Kontext, so sieht mein proxy.conf.json wie folgt aus:

{ 
    "/api": { 
    "target": "http://localhost:8083", 
    "secure": false 
    }, 
    "/login": { 
    "target": "http://localhost:8083", 
    "secure": false 
    }, 
    "/logout": { 
    "target": "http://localhost:8083", 
    "secure": false 
    } 
} 

Wo http://localhost:8083 mein Backend ist. Sie können dann Ihre App mit:

ng serve --proxy-config proxy.conf.json 

und verwenden Sie nur Ihre Front-End-Adresse. Keine CORS-Konfiguration erforderlich.

P.S. Wenn Sie angle-cli nicht verwenden, können Sie diesen Ansatz mit jedem Tool verwenden, das Sie verwenden, um frontent zu dienen. Vor dem Wechsel auf eckig-cli habe ich dies auch mit lite-server verwendet.

Verwandte Themen