5

我目前正在爲一個學校項目創建一個簡單的應用程序,Spring Boot後端和AngularJS前端,但有一個安全問題,我似乎無法解決。彈簧啓動安全顯示Http-Basic-Auth在登錄失敗後彈出

登錄完美地工作,但是當我輸入一個錯誤的密碼時,默認的登錄彈出窗口顯示出來,這有點煩人。我已經嘗試過註釋'BasicWebSecurity'並將httpBassic設置爲禁用,但沒有任何結果(意味着登錄過程根本不起作用)。

我的安全類:

package be.italent.security; 

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.boot.autoconfigure.security.SecurityProperties; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.core.annotation.Order; 
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 
import org.springframework.security.config.annotation.web.builders.WebSecurity; 
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 
import org.springframework.security.core.userdetails.UserDetailsService; 
import org.springframework.security.web.csrf.CsrfFilter; 
import org.springframework.security.web.csrf.CsrfToken; 
import org.springframework.security.web.csrf.CsrfTokenRepository; 
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; 
import org.springframework.web.filter.OncePerRequestFilter; 
import org.springframework.web.util.WebUtils; 

import javax.servlet.Filter; 
import javax.servlet.FilterChain; 
import javax.servlet.ServletException; 
import javax.servlet.http.Cookie; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import java.io.IOException; 

@Configuration 
@EnableGlobalMethodSecurity(prePostEnabled = true) 
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) 
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { 

    @Autowired 
    private UserDetailsService userDetailsService; 

    @Autowired 
    public void configure(AuthenticationManagerBuilder auth) throws Exception { 
     auth.userDetailsService(userDetailsService); 
    } 

    @Override 
    public void configure(WebSecurity web){ 
     web.ignoring() 
     .antMatchers("/scripts/**/*.{js,html}") 
     .antMatchers("/views/about.html") 
     .antMatchers("/views/detail.html") 
     .antMatchers("/views/home.html") 
     .antMatchers("/views/login.html") 
     .antMatchers("/bower_components/**") 
     .antMatchers("/resources/*.json"); 
    } 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http.httpBasic() 
        .and() 
       .authorizeRequests() 
       .antMatchers("/user", "/index.html", "/", "/projects/listHome", "/projects/{id}", "/categories", "/login").permitAll().anyRequest() 
       .authenticated() 
        .and() 
       .csrf().csrfTokenRepository(csrfTokenRepository()) 
        .and() 
       .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class).formLogin(); 
    } 

    private Filter csrfHeaderFilter() { 
     return new OncePerRequestFilter() { 
      @Override 
      protected void doFilterInternal(HttpServletRequest request, 
              HttpServletResponse response, FilterChain filterChain) 
        throws ServletException, IOException { 
       CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class 
         .getName()); 
       if (csrf != null) { 
        Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN"); 
        String token = csrf.getToken(); 
        if (cookie == null || token != null 
          && !token.equals(cookie.getValue())) { 
         cookie = new Cookie("XSRF-TOKEN", token); 
         cookie.setPath("/"); 
         response.addCookie(cookie); 
        } 
       } 
       filterChain.doFilter(request, response); 
      } 
     }; 
    } 

    private CsrfTokenRepository csrfTokenRepository() { 
     HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository(); 
     repository.setHeaderName("X-XSRF-TOKEN"); 
     return repository; 
    } 
} 

是否有人對如何防止出不破壞其餘這個彈出的想法?

解決方案

將此添加到我的角度配置:

myAngularApp.config(['$httpProvider', 
    function ($httpProvider) { 
    $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 
    } 
]); 
+0

您的安全配置看起來有點混亂。首先你激活httpBasic。那麼你想使用表單登錄,然後你想禁用httpBasic。 對我而言,它並不清楚你想要保護什麼以及以何種方式。這個csrfHeaderFilter看起來像你有一個angularJS前端是這種情況? –

+0

廢話,我做了一個錯誤的複製/粘貼。最終的httpBasic()。disable()不應該在那裏。我的錯!這確實是一個angularJS前端。 – Daemun

+0

好的。答案將在幾分鐘內提供。希望這是你想達到的目標。如果不只是在評論中告訴我。 –

回答

13

讓我們先從你的問題

它不是一個「春啓動安全彈出窗口」的瀏覽器彈出如果您的Spring Boot應用程序的響應包含以下標頭,則顯示:

WWW-Authenticate: Basic 

在您的安全配置中出現一個.formLogin()。這不應該是必需的。儘管您想通過AngularJS應用程序中的表單進行身份驗證,但您的前端是一個獨立的JavaScript客戶端,應該使用httpBasic而不是表單登錄。

如何您的安全配置可能看起來像

我刪除了.formLogin()

@Override 
protected void configure(HttpSecurity http) throws Exception { 
    http 
      .httpBasic() 
       .and() 
      .authorizeRequests() 
      .antMatchers("/user", "/index.html", "/", "/projects/listHome", "/projects/{id}", "/categories", "/login").permitAll().anyRequest() 
      .authenticated() 
       .and() 
      .csrf().csrfTokenRepository(csrfTokenRepository()) 
       .and() 
      .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class); 
} 

如何處理瀏覽器彈出

正如前面所提到的彈出,如果顯示出來您的Spring Boot應用程序的響應包含標題WWW-Authenticate: Basic。對於Spring Boot應用程序中的所有請求,這不應該被禁用,因爲它允許您輕鬆地在瀏覽器中瀏覽api。

Spring Security有一個默認配置,允許您在每個請求中告訴Spring Boot應用程序不要在響應中添加此標頭。這是由以下頭設置你的要求做:

X-Requested-With: XMLHttpRequest 

如何將此頭添加到您的AngularJS應用的每個請求

您可以直接在應用程序配置添加默認頭象即:

yourAngularApp.config(['$httpProvider', 
    function ($httpProvider) { 
    $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 
    } 
]); 

後端現在將一個401響應,你有你的應用程序的角度來處理(例如通過一個攔截器)進行響應。

如果你需要一個例子如何做到這一點,你可以看看我的shopping list app。它與春季啓動和角js完成。