작성
·
432
0
아래처럼 securityConfig에 커스텀 로그아웃 경로에 대해 어떠한 permitAll 같은 권한 설정을 하지 않았는데도 제가 작성한 컨트롤러까지 잘 들어가서 로그아웃처리가 됩니다. 왜 허용이 되어있는거죠? 별도로 로그아웃 경로에 대해 permitAll 하지 않으면 접근이 막혀야 하는 것 아닌가요?
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
@Bean
public AuthenticationProvider authenticationProvider() {
return new FormAuthenticationProvider(userDetailsService, passwordEncoder());
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations());
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/users").permitAll()
.antMatchers("/myPage").hasRole("USER")
.antMatchers("/messages").hasRole("MANAGER")
.antMatchers("/config").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login_proc")
.defaultSuccessUrl("/")
.permitAll();
}
}
답변 1
0
네
일단 위의 코드에서는 커스텀한 로그아웃 설정이 보이지는 않습니다.
그리고 좀 더 부연 설명을 드리자면 스프링 시큐리티에서는 로그아웃 페이지를 자동 생성하는 필터를 제공하고 있습니다.
DefaultLogoutPageGeneratingFilter 필터입니다.
이 필터는 기본적으로 /logout url 로 요청하는 것에 대해서는 권한을 체크하기 앞서 로그아웃 페이지로 이동시켜 버립니다.
public class DefaultLogoutPageGeneratingFilter extends OncePerRequestFilter {
private RequestMatcher matcher = new AntPathRequestMatcher("/logout", "GET");
private Function<HttpServletRequest, Map<String, String>> resolveHiddenInputs = (request) -> Collections.emptyMap();
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (this.matcher.matches(request)) {
renderLogout(request, response);
}
else {
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.format("Did not render default logout page since request did not match [%s]",
this.matcher));
}
filterChain.doFilter(request, response);
}
}
private void renderLogout(HttpServletRequest request, HttpServletResponse response) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append("<!DOCTYPE html>\n");
sb.append("<html lang=\"en\">\n");
sb.append(" <head>\n");
sb.append(" <meta charset=\"utf-8\">\n");
sb.append(" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n");
sb.append(" <meta name=\"description\" content=\"\">\n");
sb.append(" <meta name=\"author\" content=\"\">\n");
sb.append(" <title>Confirm Log Out?</title>\n");
sb.append(" <link href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css\" "
+ "rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" "
+ "crossorigin=\"anonymous\">\n");
sb.append(" <link href=\"https://getbootstrap.com/docs/4.0/examples/signin/signin.css\" "
+ "rel=\"stylesheet\" crossorigin=\"anonymous\"/>\n");
sb.append(" </head>\n");
sb.append(" <body>\n");
sb.append(" <div class=\"container\">\n");
sb.append(" <form class=\"form-signin\" method=\"post\" action=\"" + request.getContextPath()
+ "/logout\">\n");
sb.append(" <h2 class=\"form-signin-heading\">Are you sure you want to log out?</h2>\n");
sb.append(renderHiddenInputs(request)
+ " <button class=\"btn btn-lg btn-primary btn-block\" type=\"submit\">Log Out</button>\n");
sb.append(" </form>\n");
sb.append(" </div>\n");
sb.append(" </body>\n");
sb.append("</html>");
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write(sb.toString());
}
위의 코드를 보시면
if (this.matcher.matches(request)) {
renderLogout(request, response);
}
/logout 으로 요청하면 로그아웃 페이지를 렌더링한다는 구문입니다.
그리고 로그아웃 페이지를 만들고 있습니다.
또 한가지는 DefaultLoginPageGeneratingFilter 입니다.
로그인 페이지를 렌더링하는 필터인데 소스를 보면 이런 구문이 있습니다.
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
boolean loginError = isErrorPage(request);
boolean logoutSuccess = isLogoutSuccess(request);
if (isLoginUrlRequest(request) || loginError || logoutSuccess) {
String loginPageHtml = generateLoginPageHtml(request, loginError, logoutSuccess);
response.setContentType("text/html;charset=UTF-8");
response.setContentLength(loginPageHtml.getBytes(StandardCharsets.UTF_8).length);
response.getWriter().write(loginPageHtml);
return;
}
chain.doFilter(request, response);
}
위의 구문을 보시면 logoutSuccess 가 true 이면 로그인 페이지로 이동하도록 되어 있습니다.
즉 로그아웃 실행을 하게 되면 이 필터가 후속처리를 하고 있습니다.
그래서 전체적으로 보면 설정클래스에서 권한을 permitAll 로 정의하지 않았지만 로그인, 로그아웃 전용 필터가 앞서 요청을 가로채어 처리를 하고 있기 때문입니다.
로그인, 로그아웃 필터를 참고하시면 되겠습니다.