java 带有自定义 AngularJS 登录页面的 Spring Boot 和安全性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28460071/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 13:38:09  来源:igfitidea点击:

Spring Boot and Security with custom AngularJS Login page

javaangularjsspringspring-securityspring-boot

提问by Andrew

I'm implementing a custom AngularJS login page for Spring Security and having issues authenticating.

我正在为 Spring Security 实现一个自定义的 AngularJS 登录页面,但遇到了身份验证问题。

Followed this tutorial/example, and their example works fine locally.

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

However, when I try to implement this myself, authentication fails. I'm not sure where my mistake is.

但是,当我尝试自己实现此功能时,身份验证失败。我不确定我的错误在哪里。

A POST is made to /login with credentials, (the curl is identical to the example), and I receive a 302 Found with a redirect to GET /login/, which returns a 404 Not Found.

使用凭据对 /login 进行 POST,(curl 与示例相同),我收到 302 Found 并重定向到 GET /login/,返回 404 Not Found。

When I try to POST to /login, Spring does not generate any debug logs. So I'm not sure how it is serving the 302.

当我尝试 POST 到 /login 时,Spring 不会生成任何调试日志。所以我不确定它是如何为 302 服务的。

My code can be found here:

我的代码可以在这里找到:

Notable changes (And most likely the source of my issues):

显着变化(很可能是我的问题的根源):

  • File structure changes

  • Using strictly Angular (No jQuery) - Which results in a different function needed to make the POST request

  • Using bower instead of wro4j

  • Angular code styling/scoping

  • 文件结构变化

  • 严格使用 Angular(无 jQuery) - 这会导致发出 POST 请求所需的不同功能

  • 使用凉亭代替 wro4j

  • 角度代码样式/范围

Many related Spring Security questions suggest the POST request is formatted incorrectly, but mine appears to be the same as the example (at least when I copy to curl in chrome dev console). Others suggest implementing custom authorization providers, but it is not needed in the example, so I'm perplexed to what the difference is between mine and the example. Help me Stack Exchange, you're my only hope.

许多相关的 Spring Security 问题表明 POST 请求的格式不正确,但我的似乎与示例相同(至少当我在 chrome 开发控制台中复制到 curl 时)。其他人建议实现自定义授权提供程序,但在示例中不需要它,所以我对我的和示例之间的区别感到困惑。帮助我 Stack Exchange,你是我唯一的希望。

Dev Tools: imgurDOTcom/a/B2KmV

开发工具:imgurDOTcom/a/B2KmV

Relevant code:

相关代码:

login.js

登录.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;
            });
        }

    }]);

application.java

应用程序.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;
        }
    }

}

回答by hya

Try adding WebSecuritConfigAdapter

尝试添加 WebSecuritConfigAdapter

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

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

回答by Zohaib Jawaid

There is one thing worng with login.jsthat it invokes authenticate()which calls /user and you get a redirect to GET /login/. Spring looks for login.jspwhich is not there and end up with 404 Not Found.

login.js有一件事情是它调用authenticate()它调用 /user 并且你得到一个到 GET /login/ 的重定向。Spring 查找不存在的login.jsp并以 404 Not Found 结束。

You can make it work by taking following steps:

您可以通过以下步骤使其工作:

1) Remove invocation of authenticate()from line 38 in login.js

1)authenticate()从 login.js 中的第 38 行删除调用

2) Add login processing URL like:

2) 添加登录处理 URL,如:

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

3) Change your login URL to 'perform_login' like:

3) 将您的登录 URL 更改为“perform_login”,例如:

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

and it works, you get the user.

它有效,你得到了用户。

Refer to http://www.baeldung.com/spring-security-loginfor spring security config.

有关 spring 安全配置,请参阅http://www.baeldung.com/spring-security-login

回答by Mathan

This kind of error is most likely a Spring Security configuration problem.

这种错误很可能是 Spring Security 配置问题。

when i read your spring security, 'loginPage' is commented.
Also your :

当我阅读您的 Spring Security 时,评论了“登录页面”。
还有你的:

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

Seems weird to me.

对我来说似乎很奇怪。

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

Should be fine.

应该没事。

And i'm not very fond of Angular, but your authenticate() method is called (just after it's definition) and it does a GET on 'user' which is not in your 'permitAll' matcher.

而且我不是很喜欢 Angular,但是你的 authenticate() 方法被调用(就在它的定义之后),它对不在你的 'permitAll' 匹配器中的'user' 执行 GET。

So consider doing this differently. Wether you add the matcher, which is not a good practice to permit user data free access. Or get the user info after you authenticate.

因此,请考虑以不同的方式执行此操作。您是否添加了匹配器,这不是允许用户数据自由访问的好习惯。或者在您进行身份验证后获取用户信息。

Cheers

干杯

回答by johnmin

Can you try adding an AuthenticationSuccessHandler to override the default Spring success handler which redirects requests

您可以尝试添加一个 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);
      }
    };
  }

In your configuration add the authentication success handler

在您的配置中添加身份验证成功处理程序

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

回答by Alex Chernyshev

  1. Enable more Spring logging: create application.properties and put:
  1. 启用更多 Spring 日志记录:创建 application.properties 并放置:

logging.level.ROOT=DEBUGYou'll see full authentication process details and actual error.

logging.level.ROOT=DEBUG您将看到完整的身份验证过程详细信息和实际错误。

  1. You have CSRF protection enabled:

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

    and CSRF token is extracted from cookie instead of url parameter:

    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())) {

  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())) {

So in that case you need to verify that cookie value is also provided with request.

因此,在这种情况下,您需要验证请求中是否也提供了 cookie 值。