복붙노트

[SPRING] Spring Security : 페이지의 북마크를 허용하기 위해 로그인 URL에 리디렉션 쿼리 매개 변수를 추가하는 방법은 무엇입니까?

SPRING

Spring Security : 페이지의 북마크를 허용하기 위해 로그인 URL에 리디렉션 쿼리 매개 변수를 추가하는 방법은 무엇입니까?

문제 시나리오

나는 현재 스프링 부트와 관련된 스프링 프로젝트 (보안 및 클라우드와 같은)를 기반으로하는 내 애플리케이션의 로그인 페이지에서 작업하고있다. 내 응용 프로그램의 사용자가 로그인 페이지를 북마크하기 때문에이 동작을 수행해야합니다. 내가 잠재적 인 문제에 대해 생각하기 시작했을 때, 응용 프로그램은 페이지를 북마크 한 후에 (여러 개의 URL이 될 수 있기 때문에) 리디렉션 할 위치를 알 수 없다는 것을 알았습니다. 일반적으로 사용자는 / dashboard가 아니며 인증 정보가 없기 때문에 로그인으로 리디렉션됩니다. 사용이 자신의 자격 증명을 제시하면 응용 프로그램이 사용자를 리디렉션합니다. 하지만 현재 세션에서 리디렉션 위치를 알리는 SavedRequest가 응용 프로그램에 저장되어있을 가능성이 있습니다.

달성하고자하는 것

기본적으로 달성하고자하는 것은 응용 프로그램이 사용자가 / login url에 책갈피를 설정 한 후에 어디로 갈지를 아는 것입니다. 이상적인 상황은 / login url이 redirect 매개 변수를 포함하는 것입니다. 예를 들어.

이제 사용자가 2 단계에서 제공된 URL을 북마크에 추가 한 다음 잠시 후 북마크를 클릭하면 합리적인 리디렉션을위한 충분한 정보가 애플리케이션에 제공됩니다.

누군가가 더 나은 접근법을 알고 있다면 나는 그것을 듣고 싶다.

지금까지 수행 된 단계

지금까지 StackOverflow에 대한 또 다른 대답에 대한 해결책을 찾았습니다. 올바른 방향으로 나아가는 것처럼 보이지만 원하는 기능 중 일부가 여전히 누락되었습니다.

LoginUrlAuthenticationEntryPoint 클래스의 사용자 정의 구현을 작성하여 시작했습니다. 이것은 다음과 같은 시작 메서드를 재정의합니다.

public class CustomLoginUrlAuthenticaitonEntryPoint extends LoginUrlAuthenticationEntryPoint 
{
  @Override
  public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException 
  {
    if (!request.getRequestURI().equals(this.getLoginFormUrl())) 
    {
      RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
      redirectStrategy.sendRedirect(request, response, getLoginFormUrl() + "?redirect=" + request.getRequestURI() + "?" + request.getQueryString());
    }
  }
}

그런 다음이 사용자 지정 클래스를 기본 인증 진입 점으로 HttpSecurity에 추가했습니다.

@Configuration
@Order(-20)
public class SecurityConfig extends WebSecurityConfigurerAdapter 
{
  @Override
  protected void configure(HttpSecurity http) throws Exception 
  {
    http
      .formLogin()
        .loginPage("/login")
        .permitAll()
      .and()
        .exceptionHandling()
        .authenticationEntryPoint(new CustomLoginUrlAuthenticationEntryPoint("/login"));
  }
}

마지막으로 로그인 페이지를 서버에 연결하는 사용자 정의 로그인 컨트롤러를 구현했습니다.

@Controller
public class LoginController 
{
  @RequestMapping(value = "/login", method = RequestMethod.GET)
  public ModelAndView login(@RequestParam(value = "redirect", required = false) String redirect) 
  {
    ModelAndView model = new ModelAndView();
    // Do something with the redirect url;
    model.setViewName("login");
    return model;
  }

하지만 일단 이것을 구현하면 리디렉션이 올바르게 작동하고있는 것처럼 보였습니다. (/ 대시 보드? param = 값이 / login? redirect = / dashboard? param = value로 리디렉션되었지만 로그인 페이지가 표시되지 않았습니다. 그러나 / login url을 직접 방문하면 로그인 페이지가 표시됩니다.

그래서 나는 / login url에 커스텀 질의 매개 변수를 추가하는 것이 옳다고 생각한다.하지만 구현이 완벽하지는 않다. 누군가가 문제를 파악하거나 내 문제에 대해 더 나은 해결책을 제공 할 수 있습니까?

미리 감사드립니다.

해결법

  1. ==============================

    1.경고 : 매개 변수를 사용하여 리디렉션 할 위치를 결정하면 Open Redirect Vulnerabilities까지 응용 프로그램을 열 수 있습니다. 사용자 입력에 따라 리디렉션을 수행 할 때 매우주의하십시오.

    경고 : 매개 변수를 사용하여 리디렉션 할 위치를 결정하면 Open Redirect Vulnerabilities까지 응용 프로그램을 열 수 있습니다. 사용자 입력에 따라 리디렉션을 수행 할 때 매우주의하십시오.

    계속 EntryPoint

    첫 번째 단계는 URL이있는 매개 변수를 포함하는 역할을하는 AuthenticationEntryPoint를 작성하여 로그인 양식을 표시 할 때 URL에서 계속 진행하는 것입니다. 이 예제에서는 continue라는 매개 변수를 사용합니다.

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
    import org.springframework.security.web.util.UrlUtils;
    import org.springframework.web.util.UriComponentsBuilder;
    
    /**
     * @author Rob Winch
     *
     */
    public class ContinueEntryPoint extends LoginUrlAuthenticationEntryPoint {
    
        public ContinueEntryPoint(String loginFormUrl) {
            super(loginFormUrl);
        }
    
        @Override
        protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response,
                AuthenticationException exception) {
    
            String continueParamValue = UrlUtils.buildRequestUrl(request);
            String redirect = super.determineUrlToUseForThisRequest(request, response, exception);
            return UriComponentsBuilder.fromPath(redirect).queryParam("continue", continueParamValue).toUriString();
        }
    }
    

    WebSecurityConfig

    다음 단계는 ContinueEntryPoint를 사용하는 보안 구성을 포함시키는 것입니다. 예 :

    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .exceptionHandling()
                    .authenticationEntryPoint(new ContinueEntryPoint("/login"))
                    .and()
                .authorizeRequests()
                    .antMatchers("/login").permitAll()
                    .anyRequest().authenticated()
                    .and()
                .formLogin();
        }
    }
    

    LoginController

    마지막으로 사용자가 이미 인증 된 경우 매개 변수로 리디렉션하는 LoginController를 만들어야합니다. 예 :

    import javax.validation.constraints.Pattern;
    
    import org.hibernate.validator.constraints.NotBlank;
    
    public class RedirectModel {
        @Pattern(regexp="^/([^/].*)?$")
        @NotBlank
        private String continueUrl;
    
        public void setContinue(String continueUrl) {
            this.continueUrl = continueUrl;
        }
    
        public String getContinue() {
            return continueUrl;
        }
    }
    
    @Controller
    public class LoginController {
    
        @RequestMapping("/login")
        public String login(Principal principal, @Valid @ModelAttribute RedirectModel model, BindingResult result) {
            if (!result.hasErrors() && principal != null) {
                // do not redirect for absolute URLs (i.e. https://evil.com)
                // do not redirect if we are not authenticated
                return "redirect:" + model.getContinue();
            }
            return "login";
        }
    }
    

    전체 샘플

    전체 예제는 rwinch / spring-security-sample에서 github에서 찾을 수 있습니다 (34087954-continue-on-login 브랜치). git을 사용하지 않으려는 경우 쉽게 다운로드 할 수 있습니다.

  2. ==============================

    2.덜 제한적인 로그인을 시도하십시오. 보안 구성에 다음을 추가하십시오.

    덜 제한적인 로그인을 시도하십시오. 보안 구성에 다음을 추가하십시오.

    http
    .authorizeRequests()
        .antMatchers("/login").permitAll()
        ...
    

    원본 스레드

  3. from https://stackoverflow.com/questions/34087954/spring-security-how-to-add-a-redirect-query-parameter-to-the-login-url-to-allow by cc-by-sa and MIT license