ChamomileGuides 3.0.4 Help

시큐리티(세션) 모듈 커스터마이징 가이드

캐모마일 시큐리티에서 설정한 시큐리티 관련 빈들을 커스터마이징 하여 사용하고 싶다면 다음 가이드를 읽고 해당 빈의 이름과 같은 이름의 빈을 등록하여 오버라이드를 진행한다.

SecurityFilterChain

Spring Security는 HTTP 요청을 처리하기 위해 다양한 필터들의 체인을 사용하며, 이 메소드는 그 체인을 구성한다.

@Bean public SecurityFilterChain filterChain(HttpSecurity http,AuthenticationManager authManager,AuthenticationProvider chamomileAuthenticationProvider , RoleHierarchyService roleHierarchyService, AuthenticationSuccessHandler authenticationSuccessHandler, AuthenticationFailureHandler authenticationFailureHandler) throws Exception { http.httpBasic(); configureRememberMe(http); configureSessionManagement(http); addAuthenticationProvider(http, chamomileAuthenticationProvider); addSecurityInterceptorFilter(http, authManager, roleHierarchyService); configureFormLogin(http, authenticationSuccessHandler, authenticationFailureHandler); configureLogoutHandling(http); configureExceptionHandling(http); return http.build(); }

http.httpBasic()
HTTP 인증을 활성화한다. 사용자 이름과 비밀번호를 통한 간단한 인증 방법이다.

http.rememberMe()
rememberMe() 메소드는 HTTP 요청에 'Remember Me' 기능을 활성화한다.

configureSessionManagement()
동시세션 제어를 한다.

addAuthenticationProvider()
커스텀 AuthenticationProvider인 chamomileAuthenticationProvider를 Spring Security에 추가한다.

addSecurityInterceptorFilter()

  • 보호되는 자원을 RDB로 처리하기 위한 FilterSecurityInterceptor이다.
    모든 요청에 대해 권한이 적절한지 검사를 수행한다.

  • authManager
    AuthenticationManager는 사용자 인증을 처리하는 구성요소이다. 이 매니저는 인증 과정을 관리한다.

  • accessDecisionManager(roleHierarchyService)
    accessDecisionManager메소드는 접근 결정을 내리는 데 필요한 로직을 구현한다. 이 메소드는RoleHierarchyService`를 사용하여 역할 계층 구조에 따라 접근 결정을 내릴 수 있다.

  • reloadableFilterInvocationSecurityMetadataSource()
    이 메소드는 FilterInvocationSecurityMetadataSource의 구현체를 제공한다. 이 구현체는 각 HTTP 요청에 필요한 보안 메타데이터(예: 요청에 필요한 권한)를 결정한다.

configureFormLogin()

  • form 기반 인증 관련 메서드

  • http.formLogin()
    HttpSecurityformLogin() 메소드를 호출하여 폼 로그인을 활성화한다.

  • loginProcessingUrl(...)
    로그인 처리를 위한 URL을 설정한다. 사용자가 로그인 폼을 제출할 때 이 URL로 요청을 보낸다. securityProperties.getLoginProcessingUrl()을 통해 이 URL을 가져온다.

  • usernameParameter(...), .passwordParameter(...): 사용자 이름과 비밀번호 필드의 이름을 설정한다. 폼에서 사용되는 입력 필드의 이름을 지정한다.

  • successHandler(...), .failureHandler(...): 로그인 성공 및 실패 처리를 위한 핸들러를 설정한다. 로그인 성공 시 authenticationSuccessHandler가, 실패 시 authenticationFailureHandler가 호출된다.

configureLogoutHandling()

  • 로그아웃 구성을 작성하기 위한 메서드

  • http.logout()
    HttpSecuritylogout() 메소드를 호출하여 로그아웃 기능을 활성화한다. 이 메소드는 로그아웃 관련 설정을 정의할 수 있는 LogoutConfigurer 객체를 반환한다.

  • .logoutUrl(...)

    • 로그아웃을 처리할 URL을 설정한다. 이 URL은 사용자가 로그아웃 요청을 보낼 때 사용된다.

    • this.securityProperties.getLogoutProcessingUrl()를 통해 로그아웃 처리 URL을 가져온다.

    • 로그아웃 요청을 받을 URL을 지정하여, 사용자가 해당 URL로 요청을 보내 로그아웃을 수행할 수 있도록 설정한다.

configureExceptionHandling()

  • http.exceptionHandling().accessDeniedPage(page)

    • HttpSecurityexceptionHandling() 메소드를 사용하여 예외 처리 설정을 시작한다.

    • accessDeniedPage(page) 메소드로 사용자가 접근할 수 없는 리소스에 접근하려고 시도할 때 보여줄 페이지를 설정한다..

AuthenticationManager

  • 인증요청에 대한 처리 메서드

  • AuthenticationManager는 Spring Security의 핵심 인터페이스 중 하나로, 인증(Authentication) 과정을 관리한다. 이 인터페이스는 사용자의 인증 정보(아이디와 비밀번호 등)를 받아 유효성을 검증하는 역할을 한다.

@Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); }

AuthenticationProvider

Spring Security에서 인증 메커니즘을 추상화하는 인터페이스.

사용자가 제공한 인증 정보(예: 아이디와 비밀번호)를 검증하고, 해당 사용자에 대한 인증 결과를 반환하는 역할을 한다.

ChamomileAuthenticationProvider는 AuthenticationProvider 인터페이스이다.이 클래스는 특정 인증 로직(예: 사용자 인증, 권한 검사 등)을 구현하고 있다. 사용자정보, 사용자권한, 사용자그룹권한, 계정만료, 계정잠김, 사용여부, 비밀번호 확인 등에서 사용된다.

@Bean @ConditionalOnMissingBean(AuthenticationProvider.class) public AuthenticationProvider getDaoAuthProvider() { return new ChamomileAuthenticationProvider(); }

WebSecurityCustomizer

  • Spring Security에서 웹 보안 설정을 커스터마이징는 데 사용하는 인터페이스이다.

  • web.ignoring()은 특정 요청에 대해 Spring Security의 보안 필터를 적용하지 않도록 설정한다.

    • chmm.security.ignorePatterns에 설정된 URL 패턴을 무시한다.

    • HttpSecurity 보다 우선 실행 된다. (filterChain 이전)

@Bean public WebSecurityCustomizer webSecurityCustomizer() { return web -> { Optional.ofNullable(securityProperties.getIgnorePatterns()) .ifPresent(patterns -> web.ignoring().antMatchers(patterns)); }; }

RememberMe

이 기능은 사용자가 로그인 상태를 유지할 수 있게 한다.

private void configureRememberMe(HttpSecurity http) { Optional.ofNullable(securityProperties.getAutoConfig().get("rememberMe")) .filter("true"::equals) .ifPresent(rememberMe -> { try { http.rememberMe() .rememberMeCookieName("custom-cookie") .rememberMeParameter("remember-me") .tokenValiditySeconds(Integer.parseInt(securityProperties.getAutoConfig().get("rememberMeValiditySeconds"))); } catch (Exception e) { throw new ChamomileException(ChamomileExceptionCode.Unauthorized); } }); }
  • rememberMeCookieName("custom-cookie"):

    • 'Remember Me' 쿠키의 이름을 "custom-cookie"로 설정한다.

    • rememberMeParameter("remember-me"):

      • 'Remember Me' 파라미터의 이름을 "remember-me"로 설정한다. 이는 폼 제출 시 사용된다.

    • .tokenValiditySeconds(...):

      • 'Remember Me' 토큰의 유효 기간을 설정한다. 여기서는 securityProperties에서 "rememberMeValiditySeconds" 값을 가져와 해당 기간을 초 단위로 설정한다.

PasswordEncoder

  • 이 메서드는 PasswordEncoder 인터페이스의 인스턴스를 반환한다.

  • PasswordEncoder는 Spring Security에서 사용하는 인터페이스로, 비밀번호를 안전하게 저장하고 검증하는 데 사용된다.

  • 반환되는 객체는 MultiplePasswordEncoder라는 사용자 정의 클래스의 인스턴스이다. 여러 종류의 비밀번호 인코딩 방식을 지원하도록 설계된다.

@Bean @ConditionalOnMissingBean({PasswordEncoder.class}) public PasswordEncoder passwordEncoder() { return new MultiplePasswordEncoder(this.securityProperties.getDefaultPasswordEncoder(), this.securityProperties.getPasswordEncoderList()); }

JdbcUserDetailsService

  • JdbcUserDetailsService는 이 인터페이스의 구현체 중 하나로, 데이터베이스를 사용하여 사용자 정보를 로드하는데 사용된다.

  • chamomile에서 제공하는 테이블 형태의 구조로 사용자 정보를 제공한다.

    • dataSource : RDB에 연결하기 위한 데이터소스를 설정한다.

    • usersByUsernameQuery (optional) : 입력받은 사용자 아이디로 사용자를 조회하는 쿼리를 설정한다.

      • 쿼리의 결과는 기본적으로 6개의 항목을 조회하고 있다.

      • userId(아이디), userPassword(비밀번호), enabled(활성화여부), accountNonExpired(계정이만료되지 않았는지), accountNonLocked(계정이 잠겨있지 않았는지), credentialsNonExpired(비밀번호가 만료되지 않았는지): 순서는 변경되어서는 안된다.

      • 예) select userId, userPassword, enabled, accountNonExpired, accountNonLocked, credentialsNonExpired from where > ? and userId = ?

    • authoritiesByUsernameQuery (optional) : 입력받은 사용자 아이디에 부여 된 권한 목록을 조회하는 쿼리를 설정한다.

      • 쿼리의 결과는 기본적으로 2개의 항목을 조회하고 있다.

      • userId(아이디), roleId(권한): 순서가 변경되어서는 안된다.

    • groupAuthoritiesByUsernameQuery (optional) : 사용자가 속한 그룹의 권한 목록을 조회하는 쿼리를 설정한다.

      • 쿼리의 결과는 기본적으로 3개의 항목을 조회하고 있다.

      • userId(아이디), groupId(그룹), roleId(권한): 순서가 변경되어서는 안된다.

@Bean public JdbcUserDetailsService jdbcUserDetailsService(DataSource dataSource) throws Exception { JdbcUserDetailsService jdbcUserDetailsService = new JdbcUserDetailsService(); jdbcUserDetailsService.setDataSource(dataSource); return jdbcUserDetailsService; }

AccessDecisionManager

  • 리소스에 대한 접근 여부를 결정하기 위한 판단주체이다.

  • 설정 된 AccessDecisionManager는 등록 된 voter 중 하나라도 리소스에 대한 접근을 허용하게 되는 경우 접근하도록 결정한다.

  • 2개의 AccessDecisionVoter가 설정되어 있으며, expression 기반의 처리와 권한계층을 포함하는 Role Voter가 설정되어져 있다.

@Bean public AccessDecisionManager accessDecisionManager(RoleHierarchyService RoleHierarchyService) { WebExpressionVoter webExpressionVoter = new WebExpressionVoter(); webExpressionVoter.setExpressionHandler(this.webSecurityExpressionHandler(RoleHierarchyService)); RoleHierarchyVoter roleHierarchyVoter = new RoleHierarchyVoter(this.reloadableRoleHierarchy(RoleHierarchyService)); roleHierarchyVoter.setRolePrefix(""); List<AccessDecisionVoter<?>> decisionVoters = Arrays.asList(webExpressionVoter, roleHierarchyVoter); return new AffirmativeBased(decisionVoters); }
  • WebExpressionVoter 객체는 Spring Security의 표현식 기반 접근 제어(예: hasRole('ROLE_USER'))를 평가하는 데 사용된다.

  • webSecurityExpressionHandler 메서드는 RoleHierarchyService를 사용하여 역할 계층을 지원한다.

  • RoleHierarchyVoter 역할 계층을 고려하여 접근 결정을 내리는 데 사용한다.

  • AffirmativeBased AffirmativeBased는 제공된 투표자(decisionVoters) 목록 중 하나라도 접근을 승인하면, 접근을 허용하는 결정 전략을 사용한다.

ReloadableFilterInvocationSecurityMetadataSource

  • 리소스(주로 URL)에 대한 보안 메타데이터를 관리하는 컴포넌트이다

  • 필터 체인에서 URL 기반의 보안 메타데이터를 동적으로 관리해준다.

  • 보안 메타데이터를 변경할 필요가 있을 때 애플리케이션을 재시작 하지 않고도 변경 사항을 적용할 수 있게 해준다.

@Bean public ReloadableFilterInvocationSecurityMetadataSource reloadableFilterInvocationSecurityMetadataSource() { ReloadableFilterInvocationSecurityMetadataSource reloadableFilterInvocationSecurityMetadataSource = new ReloadableFilterInvocationSecurityMetadataSource(); reloadableFilterInvocationSecurityMetadataSource.setSecuredUrlResourceService(securedUrlResourceService(dataSource)); return reloadableFilterInvocationSecurityMetadataSource; }

SecuredUrlResourceService

  • URL 리소스에 대한 권한정보를 제공하는 서비스

  • dataSource : RDB의 데이터소스를 설정한다.

  • rolesQuery : URL 리소스에 대한 권한 정보를 조회하는 쿼리를 설정한다.

    • 기본적으로 url(리소스 url 정보), authority(권한)을 조회하게 되어 있다.

    • 순서와 명칭이 동일해야 한다.

@Bean public SecuredUrlResourceService securedUrlResourceService(DataSource dataSource) { SecuredUrlResourceService securedUrlResourceService = new SecuredUrlResourceService(); securedUrlResourceService.setDataSource(dataSource); return securedUrlResourceService; }

ReloadableRoleHierarchy

  • 권한의 계층정보를 제공한다.

  • 역할 계층(Role Hierarchy)을 관리하는 컴포넌트로, 역할 간의 관계를 정의하고 동적으로 업데이트할 수 있게 한다

  • 상위 역할이 하위 역할의 권한을 상속받는 구조를 만들 수 있다.

@Bean public ReloadableRoleHierarchy reloadableRoleHierarchy(RoleHierarchyService RoleHierarchyService) { ReloadableRoleHierarchy reloadableRoleHierarchy = new ReloadableRoleHierarchy(); reloadableRoleHierarchy.setRoleHierarchyService(RoleHierarchyService); return reloadableRoleHierarchy; }

jdbcRoleHierarchyService

  • 권한의 계층 정보를 제공하는 서비스

  • dataSource : RDB의 데이터소스를 설정한다.

  • hierarchicalRolesQuery : 권한의 계층 정보를 조회하는 쿼리를 설정한다.

    • 기본적으로 parent(상위 권한), child(하위 권한)을 조회하게 되어 있다.

    • 순서와 명칭이 동일해야 한다.

@Bean @ConditionalOnMissingBean({RoleHierarchyService.class}) public JdbcRoleHierarchyService jdbcRoleHierarchyService(DataSource dataSource) { JdbcRoleHierarchyService roleHierarchyService = new JdbcRoleHierarchyService(); roleHierarchyService.setDataSource(dataSource); return roleHierarchyService; }

FilterSecurityInterceptor

  • 보호되는 자원을 RDB로 처리하기 위한 FilterSecurityInterceptor이다.

    • 모든 요청에 대해 권한이 적절한지 검사를 수행한다.

    • 인증과 권한 부여의 중심 역할을 하며, 각 HTTP 요청이 보안 정책에 부합하는지를 검사한다

  • authenticationManager : 추가 인증이 필요한경우 사용하게 될 인증 처리 주체

    • 사용자의 인증 요청을 처리하는 역할

  • accessDecisionManager : 리소스에 대한 접근 여부를 판단하게 될 AccessDecisionManager를 설정한다.

    • 요청된 리소스에 대한 접근을 승인할 것인지를 결정

  • securityMetadataSource : RDB로 관리되는 보호되는 자원(리소스)를 제공하는 메타데이터소스

    • 보호되는 리소스(예: URL)에 대한 보안 메타데이터를 제공

  • rejectPublicInvocations : 권한을 부여하지 않은 리소스에 대한 접근 시 오류 발생 여부 설정

public FilterSecurityInterceptor filterSecurityInterceptor(AuthenticationManager authenticationManager, AccessDecisionManager accessDecisionManager, ReloadableFilterInvocationSecurityMetadataSource reloadableFilterInvocationSecurityMetadataSource) { FilterSecurityInterceptor customFilterSecurityInterceptor = new FilterSecurityInterceptor(); //인증 처리 주체로서, 사용자의 인증 요청을 처리하는 AuthenticationManager를 설정 customFilterSecurityInterceptor.setAuthenticationManager(authenticationManager); //RDB로 관리되는 보호되는 자원(리소스)를 제공하는 메타데이터소스 customFilterSecurityInterceptor.setSecurityMetadataSource(reloadableFilterInvocationSecurityMetadataSource); //리소스에 대한 접근 여부를 판단하게 될 AccessDecisionManager를 설정 //주어진 인증과 요청한 URL리소스에 대해 사용자가 접근할 권한이 있는지 결정 customFilterSecurityInterceptor.setAccessDecisionManager(accessDecisionManager); //권한을 부여하지 않은 리소스에 대한 접근 시 오류 발생 여부 설정 customFilterSecurityInterceptor.setRejectPublicInvocations(true); return customFilterSecurityInterceptor; }

CustomAuthenticationProvider

Provider의 경우 프로젝트 상황에 맞게 수정이 가능하다.

프로젝트 내에 특정 패키지에 아래 CustomAuthenticationProvider.java를 생성해준다.

@Configuration public class CustomAuthenticationProvider implements AuthenticationProvider { @Autowired private JdbcUserDetailsService jdbcUserDetailsService; @Autowired private JdbcLoginService jdbcLoginService; @Autowired private PasswordEncoder passwordEncoder; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { try { String loginId = authentication.getName(); String password = (String) authentication.getCredentials(); LoggingUtils.userAccessLogging(loginId, UserAccessActionType.LOGIN_ATTEMPT); // 입력받은 userID로 사용자 정보(UserDetails)를 조회하여 반환한다. (사용자정보, 사용자권한 사용자그룹권한) DefaultUser userDetails = (DefaultUser) jdbcUserDetailsService.loadUserByUsername(loginId); checkCredentials(password, userDetails); checkAccountStatus(userDetails); LoggingUtils.userAccessLogging(loginId, UserAccessActionType.LOGIN_SUCCESS); return new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()); } catch (ChamomileException ex) { throw new AuthenticationServiceException(ex.getMessage(), new ChamomileException(((ChamomileException) ex).getChamomileCode(), ex.getMessage())); } catch (Exception ex) { throw new AuthenticationServiceException(ex.getMessage()); } } @Override public boolean supports(Class<?> authentication) { return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); } }

AuthenticationSuccessHandler

로그인 성공 후 로직에 대한 핸들링을 커스터마이징 하고싶다면 해당 클래스를 프로젝트에 생성하여 빈을 등록하면 된다.

동일한 이름의 클래스로 생성해야 한다.

SampleLoginSuccessHandler는 AuthenticationProvide 에서 계정 유효성 검사 성공시 후 처리 역활을 한다.

프로젝트 내에 특정 패키지에 아래 SampleLoginSuccessHandler는.java를 생성해준다.

@Configuration public class SampleLoginSuccessHandler implements AuthenticationSuccessHandler { private static final Logger LOGGER = LoggerFactory.getLogger(SampleLoginSuccessHandler.class); private final ObjectMapper objectMapper; public SampleLoginSuccessHandler() { this.objectMapper = new ObjectMapper(); } @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { String userID = authentication.getName(); LOGGER.info("Account that succeeded authentication : {}", userID); //락카운트 초기화 등 필요한 기능 추가 Map<String, Object> map = new HashMap<>(); map.put("success", true); String jsonString = objectMapper.writeValueAsString(map); try (OutputStream out = response.getOutputStream()) { out.write(jsonString.getBytes()); } } }

AuthenticationFailureHandler

로그인 실패 후 로직에 대한 핸들링을 커스터마이징 하고싶다면 해당 클래스를 프로젝트에 생성하여 빈을 등록하면 된다.

동일한 이름의 클래스로 생성해야 한다.

SampleLoginFailureHandler는 AuthenticationProvider 에서 계정 유효성 검사 실패시 후 처리 역활을 한다.

프로젝트 내에 특정 패키지에 아래 AuthenticationFailureHandler.java를 생성해준다.

@Configuration public class SampleLoginFailureHandler implements AuthenticationFailureHandler { private static final Logger LOGGER = LoggerFactory.getLogger(SampleLoginFailureHandler.class); @Autowired private JdbcLoginService jdbcLoginService; @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { String userID = request.getParameter("username"); try { jdbcLoginService.lockProcessAccount(userID, exception); } catch (Exception e) { throw new RuntimeException(e); } Map<String, Object> responseMap = new HashMap<>(); responseMap.put("success", false); String jsonResponse = new ObjectMapper().writeValueAsString(responseMap); response.getOutputStream().write(jsonResponse.getBytes()); } }
Last modified: 10 1월 2025