๐Ÿ“Œ Spring Security - ์ธ์ฆ ๋ฐ ๊ถŒํ•œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ ํ•ธ๋“ค๋Ÿฌ ์ •๋ณต

My Pale Blue Dotยท2025๋…„ 4์›” 30์ผ
0

SPRING

๋ชฉ๋ก ๋ณด๊ธฐ
35/36
post-thumbnail

๐Ÿ“… ๋‚ ์งœ

2025-04-30


๐Ÿ“ ํ•™์Šต ๋‚ด์šฉ

Spring Security์—์„œ ์ธ์ฆ(Authentication)๊ณผ ๊ถŒํ•œ(Authorization) ์ฒ˜๋ฆฌ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ˆ์™ธ ์ƒํ™ฉ์„ ํ•ธ๋“ค๋Ÿฌ๋กœ ์„ธ๋ถ„ํ™”ํ•˜์—ฌ ์ฒ˜๋ฆฌํ•œ๋‹ค.

์ด 6๊ฐœ์˜ ์ปค์Šคํ…€ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์ธ์ฆ, ์ธ๊ฐ€, ๋กœ๊ทธ์ธ, ๋กœ๊ทธ์•„์›ƒ ์ „๋ฐ˜์˜ ํ๋ฆ„์„ ์ œ์–ดํ•˜์˜€๋‹ค.


1๏ธโƒฃ ์ธ์ฆ ์‹คํŒจ ๋ฐ ๊ถŒํ•œ ๋ถ€์กฑ ์˜ˆ์™ธ์ฒ˜๋ฆฌ

โœ… CustomAuthenticationEntryPoint

๋ฏธ์ธ์ฆ ์‚ฌ์šฉ์ž๊ฐ€ ๋ณดํ˜ธ๋œ ์ž์›์— ์ ‘๊ทผํ•  ๋•Œ ํ˜ธ์ถœ (401 Unauthorized)

public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException) throws IOException {
        log.error("CustomAuthenticationEntryPoint's commence invoke...");
        response.sendRedirect(request.getContextPath() + "/login?error=true");
    }
}

โœ… CustomAccessDeniedHandler

์ธ์ฆ์€ ๋˜์—ˆ์ง€๋งŒ ๊ถŒํ•œ์ด ๋ถ€์กฑํ•œ ๊ฒฝ์šฐ ํ˜ธ์ถœ (403 Forbidden)

public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
                       AccessDeniedException accessDeniedException) throws IOException {
        log.error("CustomAccessDeniedHandler's handle invoke...");
        response.sendRedirect(request.getContextPath() + "/login?error=true");
    }
}

2๏ธโƒฃ ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ ํ•ธ๋“ค๋Ÿฌ

โœ… CustomSuccessHandler

๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ ๋ฉ”์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™

public class CustomSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException {
        log.info("CustomSuccessHandler's onAuthenticationSuccess invoke...");
        response.sendRedirect(request.getContextPath() + "/");
    }
}

โœ… CustomLoginFailureHandler

๋กœ๊ทธ์ธ ์‹คํŒจ ์‹œ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ (๋ณด์•ˆ์ƒ ๋ฉ”์‹œ์ง€ ๋…ธ์ถœ X)

public class CustomLoginFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                        AuthenticationException exception) throws IOException {
        log.error("CustomLoginFailureHandler's onAuthenticationFailure invoke...");
        response.sendRedirect(request.getContextPath() + "/login?error=true");
    }
}

3๏ธโƒฃ ๋กœ๊ทธ์•„์›ƒ ์ฒ˜๋ฆฌ ํ•ธ๋“ค๋Ÿฌ

โœ… CustomLogoutHandler

๋กœ๊ทธ์•„์›ƒ ์š”์ฒญ ์‹œ ์„ธ์…˜ ๋ฌดํšจํ™” ๋“ฑ ์ปค์Šคํ…€ ์ž‘์—… ์ฒ˜๋ฆฌ

public class CustomLogoutHandler implements LogoutHandler {
    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
        log.info("CustomLogoutHandler's logout invoke...");
        HttpSession session = request.getSession(false);
        if (session != null) session.invalidate();
    }
}

โœ… CustomLogoutSuccessHandler

๋กœ๊ทธ์•„์›ƒ ์™„๋ฃŒ ํ›„ ๋ฉ”์ธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ

public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
                                Authentication authentication) throws IOException {
        log.info("CustomLogoutSuccessHandler's onLogoutSuccess invoke...");
        response.sendRedirect(request.getContextPath() + "/");
    }
}

๐Ÿ”ง ์ „์ฒด Spring Security ์„ค์ • ์˜ˆ์‹œ

@Override
protected void configure(HttpSecurity http) throws Exception {
    // CSRF ๋น„ํ™œ์„ฑํ™” (ํ…Œ์ŠคํŠธ ์šฉ๋„, ์šด์˜ํ™˜๊ฒฝ์—์„œ๋Š” ๊ผญ ํ™œ์„ฑํ™”!)
    http.csrf().disable();

    // ๊ถŒํ•œ ์ฒดํฌ
    http.authorizeRequests()
        .antMatchers("/", "/join", "/login").permitAll()
        .antMatchers("/user").hasRole("USER")
        .antMatchers("/manager").hasRole("MANAGER")
        .antMatchers("/admin").hasRole("ADMIN")
        .anyRequest().authenticated();

    // ๋กœ๊ทธ์ธ ์„ค์ •
    http.formLogin()
        .loginPage("/login")
        .permitAll()
        .successHandler(new CustomSuccessHandler())
        .failureHandler(new CustomLoginFailureHandler());

    // ๋กœ๊ทธ์•„์›ƒ ์„ค์ •
    http.logout()
        .permitAll()
        .addLogoutHandler(new CustomLogoutHandler())
        .logoutSuccessHandler(new CustomLogoutSuccessHandler());

    // ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์„ค์ •
    http.exceptionHandling()
        .authenticationEntryPoint(new CustomAuthenticationEntryPoint())
        .accessDeniedHandler(new CustomAccessDeniedHandler());
}

๐Ÿ”ฅ ์ •๋ฆฌ

์ฒ˜๋ฆฌ ์ƒํ™ฉํ•ธ๋“ค๋Ÿฌ์„ค๋ช…
์ธ์ฆ๋˜์ง€ ์•Š์€ ์‚ฌ์šฉ์ž ์ ‘๊ทผCustomAuthenticationEntryPoint/login?error=true๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
๊ถŒํ•œ ๋ถ€์กฑ ์ ‘๊ทผCustomAccessDeniedHandler/login?error=true๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
๋กœ๊ทธ์ธ ์„ฑ๊ณตCustomSuccessHandler/ ๋ฉ”์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™
๋กœ๊ทธ์ธ ์‹คํŒจCustomLoginFailureHandler์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ์—†์ด /login?error=true๋กœ ์ด๋™
๋กœ๊ทธ์•„์›ƒ ์š”์ฒญ ์ฒ˜๋ฆฌCustomLogoutHandler์„ธ์…˜ ๋ฌดํšจํ™” ๋“ฑ ์ง์ ‘ ์ž‘์—… ๊ฐ€๋Šฅ
๋กœ๊ทธ์•„์›ƒ ์™„๋ฃŒ ์ฒ˜๋ฆฌCustomLogoutSuccessHandler/ ๋ฉ”์ธ์œผ๋กœ ์ด๋™

๐Ÿ”— ์ฐธ๊ณ  ์ž๋ฃŒ


profile
Here, My Pale Blue.๐ŸŒ

0๊ฐœ์˜ ๋Œ“๊ธ€