簡體   English   中英

帶有自定義 AngularJS 登錄頁面的 Spring Boot 和安全性

[英]Spring Boot and Security with custom AngularJS Login page

我正在為 Spring Security 實現一個自定義的 AngularJS 登錄頁面,但遇到了身份驗證問題。

遵循本教程/示例,他們的示例在本地運行良好

但是,當我嘗試自己實現時,身份驗證失敗。 我不確定我的錯誤在哪里。

使用憑據對 /login 進行 POST,(curl 與示例相同),我收到 302 Found 並重定向到 GET /login/,返回 404 Not Found。

當我嘗試 POST 到 /login 時,Spring 不會生成任何調試日志。 所以我不確定它是如何為 302 服務的。

我的代碼可以在這里找到:

顯着變化(很可能是我的問題的根源):

  • 文件結構變化

  • 嚴格使用 Angular(無 jQuery) - 這會導致發出 POST 請求所需的不同功能

  • 使用涼亭代替 wro4j

  • 角度代碼樣式/范圍

許多相關的 Spring Security 問題表明 POST 請求的格式不正確,但我的似乎與示例相同(至少當我在 chrome dev 控制台中復制到 curl 時)。 其他人建議實現自定義授權提供程序,但在示例中不需要它,所以我對我的和示例之間的區別感到困惑。 幫助我 Stack Exchange,你是我唯一的希望。

開發工具:imgurDOTcom/a/B2KmV

相關代碼:

登錄.js

 'use strict'; angular .module('webApp') .controller('LoginCtrl', ['$root`enter code here`Scope', '$scope', '$http', '$location', '$route', function($rootScope, $scope, $http, $location, $route) { console.log("LoginCtrl created."); var vm = this; vm.credentials = { username: "", password: "" }; //vm.login = login; $scope.tab = function(route) { return $route.current && route === $route.current.controller; }; var authenticate = function(callback) { $http.get('user').success(function(data) { console.log("/user success: " + JSON.stringify(data)); if (data.name) { console.log("And Authenticated!"); $rootScope.authenticated = true; } else { console.log("But received invalid data."); $rootScope.authenticated = false; } callback && callback(); }).error(function(response) { console.log("/user failure." + JSON.stringify(response)); $rootScope.authenticated = false; callback && callback(); }); }; authenticate(); $scope.login = function() { var data2 = 'username=' + encodeURIComponent(vm.credentials.username) + '&password=' + encodeURIComponent(vm.credentials.password); $http.post('login', data2, { headers : { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function() { authenticate(function() { if ($rootScope.authenticated) { console.log("Login succeeded"); $location.path("/"); $scope.error = false; $rootScope.authenticated = true; } else { console.log("Login failed with redirect"); $location.path("/login"); $scope.error = true; $rootScope.authenticated = false; } }); }).error(function() { console.log("Login failed"); $location.path("/login"); $scope.error = true; $rootScope.authenticated = false; }) }; $scope.logout = function() { $http.post('logout', {}).success(function() { $rootScope.authenticated = false; $location.path("/"); }).error(function() { console.log("Logout failed"); $rootScope.authenticated = false; }); } }]);

應用程序.java

package com.recursivechaos.springangularstarter;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
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.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
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.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
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;
import java.security.Principal;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@SpringBootApplication
@RestController
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @RequestMapping("/user")
    public Principal user(Principal user) {
        return user;
    }

    @RequestMapping("/resource")
    public Map<String, Object> home() {
        Map<String, Object> model = new HashMap<>();
        model.put("id", UUID.randomUUID().toString());
        model.put("content", "Hello World");
        return model;
    }

    @Configuration
    @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
    protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.
                formLogin().
                //loginPage("/#/login").
            and().
                logout().
            and().
                authorizeRequests().
                antMatchers("/index.html", "/home/**", "/login/**", "/bower_components/**", "/", "/main.js", "/login/", "/navigation/**","/login","login/","/login.html").
                permitAll().
                anyRequest().
                authenticated().
            and().
                csrf().
                csrfTokenRepository(csrfTokenRepository()).
            and().
                addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
        }

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

}

嘗試添加 WebSecuritConfigAdapter

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
            .authorizeRequests()
            .antMatchers("/**").permitAll()
            .anyRequest().authenticated();
    }
}

login.js有一件事是調用authenticate() ,它調用/user,然后你會重定向到GET/login/。 Spring 查找不存在的login.jsp並以 404 Not Found 結束。

您可以通過以下步驟使其工作:

1) 從 login.js 的第 38 行中刪除對authenticate()調用

2) 添加登錄處理 URL,如:

http.
     formLogin().
     loginProcessingUrl("/perform_login").
     and().
     logout()
 ....

3) 將您的登錄 URL 更改為“perform_login”,例如:

$http.post('perform_login', data2, {
            headers : {
                'Content-Type': 'application/x-www-form-urlencoded'
            }
        })....

它有效,你得到了用戶。

有關 spring 安全配置,請參閱http://www.baeldung.com/spring-security-login

這種錯誤很可能是 Spring Security 配置問題。

當我閱讀您的 Spring Security 時,評論了“登錄頁面”。
還有你的:

antMatchers("/index.html", "/home/**", "/login/**", "/bower_components/**", "/", "/main.js", "/login/", "/navigation/**","/login","login/","/login.html")

對我來說似乎很奇怪。

antMatchers("/index.html", "/home**", "/login**", "/bower_components**", "/main.js", "/navigation**")

應該沒事。

而且我不是很喜歡 Angular,但是您的 authenticate() 方法被調用(就在它的定義之后)並且它對不在您的“permitAll”匹配器中的“user”執行 GET。

因此,請考慮以不同的方式執行此操作。 您是否添加了匹配器,這不是允許用戶數據自由訪問的好習慣。 或者在您進行身份驗證后獲取用戶信息。

干杯

您可以嘗試添加一個 AuthenticationSuccessHandler 來覆蓋重定向請求的默認 Spring 成功處理程序嗎

private AuthenticationSuccessHandler successHandler() {
    return new AuthenticationSuccessHandler() {
      @Override
      public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        httpServletResponse.getWriter().append("OK");
        httpServletResponse.setStatus(200);
      }
    };
  }

在您的配置中添加身份驗證成功處理程序

http.
                formLogin().successHandler(successHandler())
            and().
                logout().
            and().
                authorizeRequests().
                antMatchers("/index.html", "/home/**", "/login/**", "/bower_components/**", "/", "/main.js", "/login/", "/navigation/**","/login","login/","/login.html").
                permitAll().
                anyRequest().
                authenticated().
            and().
                csrf().
                csrfTokenRepository(csrfTokenRepository()).
            and().
                addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
  1. 啟用更多 Spring 日志記錄:創建 application.properties 並放置:

logging.level.ROOT=DEBUG您將看到完整的身份驗證過程詳細信息和實際錯誤。

  1. 您啟用了 CSRF 保護:

    and().csrf(). csrfTokenRepository(csrfTokenRepository()). and(). addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);

    並且CSRF令牌是從cookie而不是url參數中提取的:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM