自定义登录表单。配置Spring安全性以获取JSON响应 [英] Custom login form. Configure Spring security to get a JSON response

查看:202
本文介绍了自定义登录表单。配置Spring安全性以获取JSON响应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的应用程序,它被分成两部分:

I have a simple application which is splitted into 2 parts :


  • 使用Spring-boot / Spring公开REST服务的后端安全性

  • 仅包含静态文件的前端。

请求由nginx接收监听端口80的服务器。

The requests are received by a nginx server which listens on port 80.


  • 如果请求URL以/ api /开头,则请求被重定向到后端。

  • 否则,请求由提供静态文件的nginx处理。

我创建了一个自定义登录表单(在前端部分),我正在尝试配置Spring-boot服务器。

I created a custom login form (in the frontend part) and I am trying to configure the Spring-boot server.

有很多例子我可以看到如何定义登录成功网址和登录错误网址,但我不希望Spring-security重定向用户。如果登录成功或HTTP 40x登录失败,我希望Spring-security使用HTTP 200回答。

There are a lot of examples where I can see how to define a "login success" url and a "login error" url but I do not want Spring-security to redirect the user. I want Spring-security to answer with HTTP 200 if the login succeeded or HTTP 40x is the login failed.

换句话说:我希望后端只回答JSON ,从不HTML。

In other words : I want the backend to only answer with JSON, never HTML.

到目前为止,当我提交登录表单时,请求被重定向,我得到默认的Spring登录表单作为答案。

Up to now, when I submit the login form, the request is redirected and I get the default Spring login form as an answer.

我试图使用 .formLogin()。loginProcessingUrl(/ login); 而不是 loginPage ()

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
      .withUser("user").password("password").roles("ADMIN");
  }

  @Override
  public void configure(HttpSecurity http) throws Exception {
    http
      .authorizeRequests()
        .anyRequest().authenticated()
        .and()
      .formLogin()
        .loginProcessingUrl("/login");


推荐答案

感谢M. Denium并感谢本指南,我可以找到解决方案。

Thanks to M. Denium and thanks to this guide, I could find the solution.

首先,我遇到了登录表单本身的配置问题。由于后端的上下文路径设置为 / api ,自定义表单应该将表单参数提交到 / api / login 但我实际上是将数据提交到 / api / login / (最后注意额外的 / )。

First, I had a configuration problem with the login form itself. As the backend has a context-path set to /api, the custom form should have submitted the form params to /api/login but I was actually submitting the data to /api/login/ (Notice the extra / at the end).

结果,我在不知不觉中试图访问受保护的资源!因此,请求由默认的 AuthenticationEntryPoint 处理,默认行为是将用户重定向到登录页面。

As a result, I was unknowingly trying to access a protected resource! Hence, the request was handled by the default AuthenticationEntryPoint which default behavior is to redirect the user to the login page.

作为解决方案,我实现了自定义AuthenticationEntryPoint:

As a solution, I implemented a custom AuthenticationEntryPoint :

private AuthenticationEntryPoint authenticationEntryPoint() {
  return new AuthenticationEntryPoint() {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
      httpServletResponse.getWriter().append("Not authenticated");
      httpServletResponse.setStatus(401);
    }
  };
}

然后在配置中使用它:

http
  .exceptionHandling()
  .authenticationEntryPoint(authenticationEntryPoint())

我为其他处理程序做了同样的事情:

and I did the same for the other handlers :

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
        .withUser("user").password("password").roles("ADMIN");
  }

  @Override
  public void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
          .anyRequest().authenticated()
        .and()
          .formLogin()
          .successHandler(successHandler())
          .failureHandler(failureHandler())
        .and()
          .exceptionHandling()
            .accessDeniedHandler(accessDeniedHandler())
            .authenticationEntryPoint(authenticationEntryPoint())
        .and()
          .csrf().csrfTokenRepository(csrfTokenRepository()).and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
    ;
  }

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

  private AuthenticationFailureHandler failureHandler() {
    return new AuthenticationFailureHandler() {
      @Override
      public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.getWriter().append("Authentication failure");
        httpServletResponse.setStatus(401);
      }
    };
  }

  private AccessDeniedHandler accessDeniedHandler() {
    return new AccessDeniedHandler() {
      @Override
      public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        httpServletResponse.getWriter().append("Access denied");
        httpServletResponse.setStatus(403);
      }
    };
  }

  private AuthenticationEntryPoint authenticationEntryPoint() {
    return new AuthenticationEntryPoint() {
      @Override
      public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.getWriter().append("Not authenticated");
        httpServletResponse.setStatus(401);
      }
    };
  }

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

这篇关于自定义登录表单。配置Spring安全性以获取JSON响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆