2017-05-09 461 views
18

我在我的web應用程序中集成spring security(即登錄部分)時遇到了一些問題。我的後端運行在本地主機:8080和FrontEnd(帶有AngularJS 2的Ionic 2)運行在localhost:8100。我設法登錄(至少我是這麼認爲的),但請求的其餘部分變得不可用,我得到以下錯誤在Chrome開發者控制檯:使用Java和Angular2的Spring Security登錄

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

當郵差測試它似乎是工作,我登錄,然後我可以在http://localhost:8080/company/getAll上執行POST請求,並且一切按預期工作。

大概我失去了一些東西(如令牌),但無法弄清楚什麼。我對Ionic和Spring Security都是新手,所以請原諒我,如果這是微不足道的。我試着用谷歌搜索各種教程,但無法找到任何東西(其中大部分都使用JSP)。

我怎麼能得到這個工作?我的前端應該怎麼樣?

這裏是從後端控制器我的登錄方法:

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

這是我WebSecurityConfig:

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

最後我的前端代碼負責記錄中:

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

如果你需要任何額外的細節,請讓我知道,我會提供他們。

在此先感謝!

我不明白的接入控制好原產地的錯誤,但我想反正根據意見修改它,現在我得到一個「無效CORS請求」

請求和響應頭從郵遞員登錄: enter image description here

編輯: 這裏是Spring Security的日誌。但是,即使FrontEnd正在調用POST,Spring也只註冊一個OPTIONS請求並且沒有POST。

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

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: 所以加入CORSFilter在接受的答案我也不得不修改我在前臺請求,以便與JSESSIONID cookie被每個請求發送建議之後。無論如何,我必須添加以下所有我的reqquests: let options = new RequestOptions({ headers: headers, withCredentials: true });

+1

這絕對是一個CORS配置問題。 – galovics

+0

你看過http://stackoverflow.com/questions/33645511/why-my-ajax-showing-preflight-is-invalid-redirect-error –

回答

8

這似乎是與CORS配置有關的問題。瀏覽器在您的實際GET調用之前確實執行OPTION預檢請求。我在這方面沒有太多的知識,但你可以嘗試在彈簧後端添加下面的過濾器。

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

並在您的WebSecurityConfig的configure方法中添加下面的行。

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

+0

你曾經嘗試刪除if if條件if(req.getMethod( ).equalsIgnoreCase(「options」))'來自CORS過濾器,可能是由於這個「未授權」即將到來。否則,我不能看到它添加此過濾器後未授權的原因。基本上我在這裏試圖做的是允許'選項'使用'訪問控制允許方法'標題的Http方法,因爲它確實需要預檢請求。 –

+0

好吧,所以在經過大規模修改並添加了corsfilter之後,它終於可以工作 –

1

的解決方案是非常簡單的。 POST上的用戶名和密碼需要作爲FormData提交,而不是正文的一部分。我與我的應用程序有同樣的問題,經過漫長的鬥爭,我創建了一個HTML表單並進行了POST,然後它像魅力一樣工作。

查看下面的實例。您應該可以使用任何ID註冊並嘗試在瀏覽器中登錄並登錄它。它在服務器端使用Spring引導,在UI上使用Angular 4。

現場演示:http://shop-venkatvp.rhcloud.com/#/

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

春季安全配置:https://github.com/reflexdemon/shop/blob/master/src/main/java/org/shop/WebSecurityConfig.java#L20-L34

登錄控制器:https://github.com/reflexdemon/shop/blob/master/src/main/java/org/shop/page/controller/LoginController.java

希望這有幫助。

+1

登錄工作,但問題仍然存在:對於所有其他請求,它不會將JSESSIONID cookie作爲請求的一部分發送。 –

1

您是否嘗試明確允許OPTIONS請求?像這樣:

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

我正在編寫一個Angular 4和spring引導集成文章。我沒有完成文章,但源代碼完成。我不知道你爲什麼試圖從兩個不同的主機提供你的應用程序,這不必要地通過注入一個cors要求並且必須管理兩個安全上下文來使你的應用程序複雜化。如果這不是絕對的要求,我會服務於同一服務器上的所有內容。

看看一個全面整合的角度和春季啓動的這個項目的GitHub:https://github.com/tschiman/tutorials/tree/master/spring-cloud/spring-cloud-bootstrap/gateway。如果你不需要,你可以忽略那裏的zuul東西。它對角度UI的支持沒有影響。

如果要啓用CSRF,你應該看看這個承諾:be4b206478c136d24ea1bf8b6f93da0f3d86a6c3

如果閱讀是不決策意識這些都是你需要遵循的步驟:

  1. 移動你角源代碼到你的春天啓動的應用程序
  2. 修改您角cli.json文件有一個輸出目錄中的靜態資源或資源公用文件夾:"outDir":"../../resources/static/home"此設置當您運行使用命令行NG構建的輸出目錄角度cli。
  3. 修改您的登錄的默認成功URL重定向到/home/index.html .formLogin().defaultSuccessUrl("/home/index.html", true)這將迫使登錄成功總是重定向到您的角度應用。
  4. 如果要自動構建的角度應用到行家那麼這個插件添加到您的POM文件:

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

如果你有我很高興能進一步迴應,如果這是不夠的其他問題讓你得到它的工作。

+1

我認爲問題在您想要使用實時重載(如Webpack或其他)來進行UI開發。有了這樣的配置,這是不可能的,你需要重新啓動速度慢的開發Java服務器。 –

0

首先 - 這絕對是一個CORS配置問題,因爲您在不同的地址上運行後端和前端。

你可以通過後端來支持CORS請求,但這是一個骯髒的解決方案,因爲你可能不希望CORS在生產版本的應用程序。 更清晰的方法是將前端或後端配置爲服務器,作爲另一方的代理。

我更喜歡配置前端代理請求到後端。這樣它就不會對應用程序的生產版本產生影響。

下一部分假定您正在使用angular-cli(正如您應該使用angular2應用程序)。

他們在他們的文檔中有一個專門用於代理配置的部分:https://www.npmjs.com/package/angular-cli#proxy-to-backend

我有內部/API背景下我所有的REST服務,所以我proxy.conf.json看起來是這樣的:

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

哪裏http://localhost:8083是我的後端。然後你可以運行你的應用程序:

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

並只使用你的前端地址。不需要CORS配置。

P.S.如果你不使用angular-cli我確定你可以使用這個方法來處理你的任何工具。在切換到angular-cli之前,我還使用了lite-server