Spring Security๋ ๋ฌด์์ผ๊น
by JiwonDev# ์ธ์ฆ๊ณผ ์ธ๊ฐ
- ์ธ์ฆ(Authentication) ์ ๋ด๊ฐ ๋๊ตฌ์ธ์ง ์๋ณํ๊ณ , ์ ์ฆํ๋ ๊ณผ์ ์ด๋ค.
- ์ธ๊ฐ(Authorization) ์ ์ธ์ฆ์ด ์๋ฃ๋ ์ฌ์ฉ์์ ๊ถํ์ ์ค์ ํ๋ ๊ณผ์ ์ด๋ค.
- ์ ๊ทผ ์ฃผ์ฒด(Principal) ์ ๋ณดํธ๋ ๋์์ ์ ๊ทผํ๋ ์ ์ ๋๋ ์์คํ ์ ์๋ฏธํ๋ค.
- ์ญํ (Role) ์ ๋ฐ๋ผ ๋ค๋ฅธ ๊ถํ์ ๋ถ์ฌํด์ผํ๋ค.
# ์๋ธ๋ฆฟ๊ณผ ํํฐ
์ธ์ฆ, ์ธ๊ฐ๋ฅผ ๋ด๋นํ๋ ์ฝ๋๋ ๊ธฐ์กด์ ์๋น์ค์ ํจ๊ป ์์ฑํ ์ ์๋ค. ์ค์ ๋ก ์๋ธ๋ฆฟ ์ด๊ธฐ์๋ ๊ทธ๋ฌ์๋ค.
ํ์ง๋ง [๋น์ฆ๋์ค ๋ก์ง]๊ณผ [์ธ์ฆ, ์ธ๊ฐ๋ฅผ ๋ด๋นํ๋ ๋ก์ง]์ ์๋ก์ ์ญํ ์ด ๋ค๋ฅด๊ณ ์ ํ ๊ด๋ จ์ด ์๋ค. ๋ชจ๋ ๊ฐ์ฒด์ ๊ฐ์ ๋ณด์์ ์ ์ฉํ ๊ฒฝ์ฐ ์ฝ๋๋ ๋ฐ๋ณต๋๊ณ ์์ ํ์ ๋ ์๋ฌด๋ฐ ์๊ด์๋ ์์ญ๊น์ง ๋ณ๊ฒฝ์ด ์ ํ๋๋ ์๋นํ ํฐ ๋จ์ ์ด ์๋ค.
๊ทธ๋์ ์๋ธ๋ฆฟ 2.3๋ถํฐ ํํฐ๋ผ๋ ๊ฐ๋ ์ ๋์ ํ๊ณ , ํํฐ๋ ์๋ธ๋ฆฟ๊ณผ ์ ์ฌํ๋ Request, Response๋ฅผ ๋จผ์ ๋ฐ์ ์กฐ์ํ ์ ์์๋ค. ์์ฒญ์ด ๋ด๋น ์๋ธ๋ฆฟ์ ๋๋ฌํ๊ธฐ ์ ํํฐ ์ฒด์ธ์ ๊ฑฐ์น๋๋ก ํด์ ์ด๋ฅผ ํด๊ฒฐํ์๋ค.

๊ธฐ์กด์ ์ธ์ฆ, ์ธ๊ฐ๋ ๋ณด์์ ๊ด๋ จ๋ ๋ถ๋ถ์ ์ด ํํฐ๋ฅผ ์ด์ฉํ์ฌ ๊ตฌํํ๋๊ฒ ์ผ๋ฐ์ ์ด์๋ค. ์ฐ๋ฆฌ๊ฐ ํํ ์๊ณ ์๋ ๋ก๊ทธ์ธ/๋ก๊ทธ์์์ ์์๋ก ๋ค๋ฉด ์๋์ ๊ฐ๋ค.
1. ์ฌ์ฉ์๋ ๋ก๊ทธ์ธ์ ํ๋ค.
2. ์์คํ ์ ๋ก๊ทธ์ธํ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ธ์ ์ ์ ์ฅํ๋ค.
3. ์ฌ์ฉ์๋ ๊ธฐ๋ฅ์ ์์ฒญํ๋ค.
4. ์์คํ ์ ์ฌ์ฉ์๊ฐ ์์ฒญํ ๊ธฐ๋ฅ์ ์ํํ๊ธฐ ์ ์ ์์ฒญํ ์ฌ์ฉ์์ ์ธ์ ์ ์ฒดํฌํ๋ค.
5. ์์คํ ์ ์ฌ๋ฐ๋ฅธ ์ธ์ ์ผ ๊ฒฝ์ฐ ๊ธฐ๋ฅ ์น์ธ, ์ธ์ ์ด ์๊ฑฐ๋ ๊ถํ์ด ์๋ ์ฌ์ฉ์์ธ ๊ฒฝ์ฐ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ์ด๋.
@WebServlet(name = "loadAppConfig", urlPatterns = { "/loadConfig" }, loadOnStartup = 1) public class LoggingFilter implements Filter { // ํํฐ ๊ฐ์ฒด๊ฐ ์์ฑ๋ ๋ ์ด๊ธฐํ์ ์ฌ์ฉ // config ๊ฐ์ฒด์์ ํํฐ ์ ๋ณด, ์ฃผ์ด์ง ํ๋ผ๋ฉํ, ์๋ธ๋ฆฟ ์ ๋ณด๋ฑ๋ฅผ ์ป์ ์ ์๋ค. public void init(FilterConfig config) throws ServletException { System.out.println("ํํฐ ์ด๊ธฐํ ๋จ"); } //์์ฒญ์๋ง๋ค ํํฐ๊ฐ ์คํํ ๋ฉ์๋ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { boolean flag = false; if (request instanceof HttpServletRequest) { HttpServletRequest req = (HttpServletRequest) request; //์ธ์
๋ฐ์์ด HttpSession session = req.getSession(); if (session != null) { if (session.getAttribute("customInfo") != null) { flag = true;} } if (flag) { //๋ก๊ทธ์ธ์ ํ์๊ฒฝ์ฐ ๋ค์ ํํฐ ์ฒด์ธ์ผ๋ก ์ด๋ chain.doFilter(request, response); } else { //๋ก๊ทธ์ธ ํ์ง ์์์ผ๋ฏ๋ก ๋ก๊ทธ์ธํ์ด์ง๋ก ํฌ์๋ RequestDispatcher rd = request.getRequestDispatcher("/member/login.jsp"); rd.forward(request, response); } } //ํํฐ ๊ฐ์ฒด๊ฐ ์ ๊ฑฐ๋ ๋ ์คํ public void destroy() { System.out.println("ํํฐ ์ ๊ฑฐ๋จ."); } }
# ์คํ๋ง์์์ ํํฐ
์คํ๋ง MVC๋ ๋ง๋ฒ์ ๋๊ตฌ๊ฐ ์๋๋ค. ์ถ์ํ ๋์์ ๋ฟ ์๋ฐ์ ์๋ธ๋ฆฟ์ผ๋ก ํฐ์บฃ ๊ฐ์ WAS์์ ๋์ํ๋๊ฑด ๋์ผํ๋ค.
์คํ๋ง MVC๋ ํ๋ก ํธ ์ปจํธ๋กค๋ฌ ํจํด์ ์ด์ฉํ๋ค. ์ฆ ๋จ ํ๋์ Dispatcher Servlet์ ์ด์ฉํ์ฌ ์์ฒญ์ ๋ฐ๊ณ ์๋น์ค๋ฅผ ์ถ์ํ ํ๋ค.

์ฆ ์คํ๋ง์์๋ ํํฐ๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ ์ ์๋ค. ์คํ๋ง ๋ถํธ๋ฅผ ์ด์ฉํ๋ฉด ์๋์ ๊ฐ์ด ๊ฐ๋จํ๊ฒ ๋ฑ๋กํ ์ ์๋ค.
// ํํฐ๋ฅผ ์ปดํฌ๋ํธ ์๋์ค์บ์ผ๋ก ๋ฑ๋ก ํ๋ ๋ฐฉ๋ฒ @ServletComponentScan @WebServlet(name = "loadAppConfig", urlPatterns = { "/*" }, loadOnStartup = 1) public Class LogFilter{ ... }
// FilterRegistrationBean ๋ฅผ ์ด์ฉํด์ ์๋์ผ๋ก ๋ฑ๋กํ๋ ๋ฐฉ๋ฒ // ๋ฌผ๋ก ํํฐ ๊ฐ์ฒด๋ฅผ ๊ทธ๋๋ก ๋ฑ๋กํด๋ ์๊ด์์ง๋ง, ์ฌ์ฉํ๋ฉด ๋ค์ํ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์๋ค. import org.springframework.boot.web.servlet.FilterRegistrationBean; @Configuration public class WebConfig { @Bean public FilterRegistrationBean logFilter() { FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>(); filterRegistrationBean.setFilter(new LogFilter()); filterRegistrationBean.setOrder(1); // ํํฐ์์๋ ์๋๋ ์ฒด์ธ ์์ ์ง์ ๋ ๊ฐ๋ฅ filterRegistrationBean.addUrlPatterns("/*"); // ํํฐ๋ฅผ ์ ์ฉํ URL ๊ท์น ์ถ๊ฐ return filterRegistrationBean; } }
ํํฐ ๊ฐ์ฒด๋ฅผ ๊ตฌํํ ๋์๋ ์คํ๋ง์์ ์ ๊ณตํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ ์ ํ๊ฒ ์ฌ์ฉํ๋ฉด ํธ๋ฆฌํ๋ค.
@Slf4j public class LoginCheckFilter implements Filter { // ๋ก๊ทธ์ธ์ด ํ์์๋ URL ๋ชจ์ (whiteList) private static final String[] whitelist = {"/", "/members/add", "/login", "/logout", "/css/*"}; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; String requestURI = httpRequest.getRequestURI(); HttpServletResponse httpResponse = (HttpServletResponse) response; try { log.info("์ธ์ฆ ์ฒดํฌ ํํฐ ์์ {}", requestURI); if (isLoginWhitelistPath(requestURI)) { // ๋ก๊ทธ์ธ์ด ํ์ํ ํ์ด์ง ์ ์ HttpSession session = httpRequest.getSession(false); if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null) { // ๋ฏธ์ธ์ฆ ์ฌ์ฉ์ ์์ฒญ -> ๋ก๊ทธ์ธ์ผ๋ก redirect httpResponse.sendRedirect("/login?redirectURL=" + requestURI); return; } } chain.doFilter(request, response); } catch (Exception e) { throw e; //์์ธ์ฒ๋ฆฌ & ๋ก๊น
๊ฐ๋ฅํ์ง๋ง, ํฐ์บฃ๊น์ง ์์ธ๋ฅผ ๋ณด๋ด์ฃผ์ด์ผ ํจ } finally { log.info("์ธ์ฆ ์ฒดํฌ ํํฐ ์ข
๋ฃ {} ", requestURI); } } // ํ์ดํธ ๋ฆฌ์คํธ URL์ ๊ฒฝ์ฐ ์ธ์ฆ ์ฒดํฌX private boolean isLoginWhitelistPath(String requestURI) { // ์คํ๋ง์ PatternMatchUtils๋ฅผ ์ฌ์ฉ. ์ด๋ฅผ ์ง์ ๊ตฌํํ๋ฉด ์ฝ๋๊ฐ ๋งค์ฐ ๋ณต์กํด์ง๋ค. return !PatternMatchUtils.simpleMatch(whitelist, requestURI); } }
# ์คํ๋ง ์ธํฐ์ ํฐ
์๋ธ๋ฆฟ์ ์๋ฐ ์น ํ์ค์์ ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ด๋ผ๋ฉด, ์ธํฐ์
ํฐ๋ ์คํ๋ง MVC์์๋ง ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ด๋ค.
์๋ธ๋ฆฟ๊ณผ ์๊ด์์ด Dispatcher Servlet์์ ์ปจํธ๋กค๋ฌ์ ๊ฐ๊ธฐ ์ ์ ์คํ๋๋ค. ์ฆ ์คํ๋ง MVC์ ํต์ฌ์ธ Dispatcher Servlet์์ ๋์ํ๋ ๋ชจ๋ ๊ฐ์ฒด๋ค, ์์ธ์ ์ ๊ทผํ ์ ์๋ค.

public interface HandlerInterceptor { // ์ปจํธ๋กค๋ฌ ์์ฒญ ์ default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { } // ์ปจํธ๋กค๋ฌ ํธ์ถ ์ดํ (๋ทฐ์ ์ ๋ฌํ Model ๊ฐ์ฒด ์ ๊ณต) default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } // ์์ ํ๊ฒ HTTP ์์ฒญ์ด ๋๋ ์ดํ (Exception ์ ๊ณต) default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } }


# ์๋ธ๋ฆฟ vs ์ธํฐ์ ํฐ
์ธํฐ์ ํฐ์ ๊ธฐ๋ฅ์ด ๋ ๊ฐ๋ ฅํ๊ธด ํ์ง๋ง, ์ด๋ ์คํ๋ง์์๋ง ์ฌ์ฉํ ์ ์๋ ๊ธฐ๋ฅ์ด๋ค.
์๋ธ๋ฆฟ๊ณผ ํํฐ๋ J2EE ํ์ค ์คํ์ ์ ์ ๋์ด์๋ ๊ธฐ๋ฅ์ด๊ธฐ์ Web app์ ์ ์ญ์ ์ผ๋ก ์ฒ๋ฆฌํด์ผํ๋ ๊ธฐ๋ฅ์ ํํฐ๋ก ๊ตฌํํ๋ ๊ฒ์ด ์ข๊ณ , ํด๋ผ์ด์ธํธ์ ๋ค์ด์ค๋ ๋ํ ์ผํ ์ฒ๋ฆฌ๋ ์ธํฐ์ ํฐ์ ๋ค์ํ ๊ธฐ๋ฅ์ผ๋ก ์ฒ๋ฆฌํ๋๊ฒ ๊น๋ํ๋ค.

์ฐธ๊ณ ๋ก ์คํ๋ง MVC์์ ์์ฒญ์ ์ฒ๋ฆฌํ๋ ํ๋ฆ์ ์๋์ ๊ฐ๋ค.

# ์คํ๋ง์์ ํํฐ๋ ์ด๋ป๊ฒ ์ถ๊ฐ๊ธฐ๋ฅ์ ์ ๊ณตํ๋๊ฑฐ์ฃ ?
์คํ๋ง์์๋ ์๋ธ๋ฆฟ ์์ฒญ์ด ๋ค์ด์ค๋ฉด ์คํ๋ง ์ปจํ ์ด๋(ApplicationContext)์ ์ฐ๊ฒฐํ ์ ์๋ DelegationFilterProxy๋ผ๋ ์๋ธ๋ฆฟ ํํฐ๋ฅผ ์ ๊ณตํ๋ค.
DelegationFilterProxy๋ฅผ ํตํด ์คํ๋ง ๋น์ผ๋ก ๊ตฌํํ ํํฐ๋ฅผ ๋ฑ๋กํ๊ณ , ์๋ธ๋ฆฟ ํ์ค ํํฐ์ฒ๋ผ ๋์ํ๊ฒ ๋๋ค.

์ฆ, Spring Security๊ฐ Filter๋ฅผ ์ฌ์ฉํด์ ๊ตฌํํ๋ค๋ ๋ง์, DelegationFilterProxy๋ฅผ ํตํด ํํฐ๋ฅผ ๋ฑ๋กํ๋ค๋ ๋ง์ด๋ค.
Spring Security๋ FilterChainProxy๋ฅผ ํตํด ๋ค์ํ ํํฐ๊ธฐ๋ฅ์ ๊ตฌํํ๋ฉฐ, ์ด๋ ์คํ๋ง ๋น์ด๊ธฐ์ DelegationFilterProxy๋ฅผ ํตํด ํํฐ์ ์ถ๊ฐ๋๋ค.

FilterChainProxy๋ Spring Security์์ ์ ๊ณตํ๋ ํน์ ํํฐ๋ก ๋ค์ํ ์ธ์คํด์ค๋ฅผ SecurityFilterChain์ ํตํด ์์์ ํ๊ณ ์๋ค. ์ผ์ข ์ ์คํ๋ง ์ํ๋ฆฌํฐ๋ฅผ ์ํ ๋์คํจ์ณ ์๋ธ๋ฆฟ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค.
https://bloowhale.tistory.com/13
Spring Security ๊ตฌ์กฐ์ ์ธ์ฆ ๋ฐฉ์์ ์ดํด - 01
01. Spring Security ๊ตฌ์กฐ ์๊ฐ Filter of Spring Security Filter ๋ ๋ฌด์์ด๊ณ ์ด๋ค ์ญํ ์ ํ๋๊ฐ? Filter๋ ์ง์ญ์ ํ์๋ฉด ์ฌ๊ณผ๊ธฐ ๋ฅผ ๋ปํฉ๋๋ค. ๊ทธ๋ ๋ค๋ฉด ์๋ฐ์์์ Filter, Spring Security ์์์ ํํฐ๋ ๋ฌด..
bloowhale.tistory.com
# Spring Security๋?
์คํ๋ง ์ฌ๋จ์์ ์ ๊ณตํ๋ ๋ชจ๋ ์ค ํ๋์ด๋ฉฐ, ์คํ๋ง์ ๊ธฐ๋ฐ์ผ๋ก ํ๋ ๋ณด์ ํ๋ ์์ํฌ์ด๋ค.
Spring Security๋ฅผ ์ฌ์ฉํ๋ฉด ์์ฒด์ ์ผ๋ก ์ธ์ ์ ์ฒดํฌํ๊ณ , ๋ฆฌ๋ค์ด๋ ์ ํ๋ ๊ฒ๋ค์ ์ถ์ํํ์ฌ ํธํ๊ฒ ๊ตฌํํ ์ ์๋ค.
Spring Security๋ ์คํ๋ง ์์กด์ฑ์ ์์ ๊ธฐ ์ํด ํํฐ(Filter)๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ค. ์ฆ ๊ธฐ์กด์ Spring MVC, ๋น์ฆ๋์ค ์ฝ๋์ ์๋ฒฝํ๊ฒ ๋ถ๋ฆฌํ์ฌ ๊ด๋ฆฌ, ๋์ํ ์ ์๋ค.
Spring Seucirty ์์๋ ๊ฐ๋ฐ์๊ฐ ์ง์ ๋ง๋ค์ด์ผํ ํํฐ๋ค์ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํ๋ค.
์ด๋ ๊ฒ ์ ๊ณตํ๋ 10๊ฐ ์ด์์ ํํฐ๋ค์ Security Filter Chain์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค.

๊ฐ ํํฐ์ ๋ํด ๊ฐ๋จํ๊ฒ ์ค๋ช ํ๋ฉด ์๋์ ๊ฐ๋ค.
- SecurityContextPersistenceFilter : SecurityContextRepository์์ SecurityContext๋ฅผ ๊ฐ์ ธ์ค๊ฑฐ๋ ์ ์ฅํ๋ค.
- LogoutFilter : ์ค์ ๋ ๋ก๊ทธ์์ URL๋ก ์ค๋ ์์ฒญ์ ๊ฐ์ํ๋ฉฐ, ํด๋น ์ ์ ๋ฅผ ๋ก๊ทธ์์ ์ฒ๋ฆฌํ๋ค.
- (UsernamePassword)AuthentocationFilter
(์์ด๋์ ๋น๋ฐ๋ฒํธ๋ฅผ ์ฌ์ฉํ๋ form ๊ธฐ๋ฐ ์ธ์ฆ) ์ค์ ๋ ๋ก๊ทธ์ธ URL๋ก ์ค๋ ์์ฒญ์ ๊ฐ์ํ๋ฉฐ, ์ ์ ์ธ์ฆ์ ์ฒ๋ฆฌ.
1. AuthenticationManager๋ฅผ ํตํ ์ธ์ฆ ์คํ.
2. ์ธ์ฆ ์ฑ๊ณต ์, ์ป์ Authentication ๊ฐ์ฒด๋ฅผ SecurityContext์ ์ ์ฅ ํ, AuthenticationSuccessHandler ์คํ.
3. ์ธ์ฆ ์คํจ ์, AuthenticationFailureHandler ์คํ. - DefaultLoginPageGeneratingFilter : ์ธ์ฆ์ ์ํ ๋ก๊ทธ์ธ ํผ URL์ ๊ฐ์.
- BasicAuthentocationFilter : HTTP ๊ธฐ๋ณธ ์ธ์ฆ ํค๋๋ฅผ ๊ฐ์ํ์ฌ ์ฒ๋ฆฌ.
- RequestCacheAwareFilter : ๋ก๊ทธ์ธ ์ฑ๊ณต ํ , ์๋ ์์ฒญ ์ ๋ณด๋ฅผ ์ฌ๊ตฌ์ฑํ๊ธฐ ์ํด ์ฌ์ฉ.
- SecurityContextHolderAwareRequestFilter :
HttpServletRequestWrapper๋ฅผ ์์ํ SecurityContextHolderAwareRequestWrapper ํด๋์ค๋ก HttpServletRequest ์ ๋ณด๋ฅผ ๊ฐ์ผ๋ค. SecurityContextHolderAwareRequestWrapper๋ ํํฐ ์ฒด์ธ์์ ๋ค์ ํํฐ๋ค์๊ฒ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ค - AnonymousAuthenticationFilter : ์ด ํํฐ๊ฐ ํธ์ถ๋๋ ์์ ๊น์ง ์ฌ์ฉ์ ์ ๋ณด๊ฐ ์ธ์ฆ๋์ง ์์๋ค๋ฉด, ์ธ์ฆ ํ ํฐ์ ์ฌ์ฉ์๊ฐ ์ต๋ช ์ฌ์ฉ์๋ก ๋ํ๋๋ค.
- SessionManagementFilter : ์ด ํํฐ๋ ์ธ์ฆ๋ ์ฌ์ฉ์์ ๊ด๋ ค๋ ๋ชจ๋ ์ธ์ ์ ์ถ์ ํ๋ค.
- ExceptionTranslationFilter : ์ด ํํฐ๋ ๋ณดํธ๋ ์์ฒญ์ ์ฒ๋ฆฌํ๋ ์ค์ ๋ฐ์ํ ์ ์๋ ์์ธ๋ฅผ ์์, ์ ๋ฌํ๋ค.
- FilterSecurityINterceptor : AccessDecisionManager๋ก์จ ๊ถํ ๋ถ์ฌ์ฒ๋ฆฌ๋ฅผ ์์ํ์ฌ ์ ๊ทผ ์ ์ด๋ฅผ ์ฝ๊ฒ ํด์ค๋ค.
๋ธ๋ก๊ทธ์ ์ ๋ณด
JiwonDev
JiwonDev