Spring Security ‐ Anonymous - taeyun-ham/andalos GitHub Wiki
다음은 익명 인증에 대한 설명입니다. 일반적으로 “deny-by-default” 라는 보안 관행을 채택하는 것이 좋은 보안 관습으로 간주됩니다. 이는 명시적으로 허용되는 것만을 지정하고 나머지는 모두 거부하는 방식입니다. 인증되지 않은 사용자에게 접근 가능한 것을 정의하는 것도 비슷한 상황으로, 특히 웹 애플리케이션에 해당합니다. 많은 사이트는 홈 페이지와 로그인 페이지와 같은 소수의 URL을 제외하고는 사용자가 인증을 받아야 합니다. 이 경우, 보안이 필요한 모든 리소스에 대해 접근 구성 속성을 정의하기보다는 이러한 특정 URL에 대한 접근 구성 속성을 정의하는 것이 더 쉽습니다. 다르게 말하면, 때로는 기본적으로 ROLE_SOMETHING이 필요하다고 말하는 것이 좋고, 로그인, 로그아웃, 애플리케이션의 홈 페이지와 같은 특정 예외만 허용하는 규칙이 좋을 수 있습니다. 이러한 페이지들을 필터 체인에서 완전히 생략하여 접근 제어 검사를 우회할 수도 있지만, 인증된 사용자에게 다르게 동작하는 페이지의 경우 다른 이유로 바람직하지 않을 수 있습니다.
이것이 익명 인증을 의미하는 바입니다. “익명으로 인증된” 사용자와 인증되지 않은 사용자 사이에 실질적인 개념적 차이는 없다는 점을 주목해야 합니다. 스프링 시큐리티의 익명 인증은 접근 제어 속성을 구성하는 더 편리한 방법을 제공합니다. 예를 들어, servlet API의 getCallerPrincipal과 같은 호출을 해도 SecurityContextHolder에 익명 인증 객체가 실제로 존재함에도 불구하고 여전히 null을 반환합니다.
익명 인증이 유용한 다른 상황도 있습니다. 예를 들어, 감사 인터셉터가 SecurityContextHolder를 조회하여 특정 작업을 수행한 주체를 식별할 때입니다. 클래스가 SecurityContextHolder가 항상 Authentication 객체를 포함하고 결코 null을 포함하지 않는다는 것을 알고 있으면 더 견고하게 작성할 수 있습니다.
HTTP 구성을 사용할 때 (스프링 시큐리티 3.0에서 도입) 자동으로 익명 인증 지원이 제공됩니다. 요소를 사용하여 이를 사용자 정의하거나 비활성화할 수 있습니다. 여기서 설명하는 빈을 구성하지 않는 한, 전통적인 빈 구성을 사용하는 경우가 아니면 구성할 필요가 없습니다.
익명 인증 기능을 제공하는 세 가지 클래스는 다음과 같습니다. AnonymousAuthenticationToken은 Authentication을 구현하며 익명 주체에 적용되는 GrantedAuthority 인스턴스를 저장합니다. 이에 해당하는 AnonymousAuthenticationProvider는 ProviderManager에 연쇄되어 AnonymousAuthenticationToken 인스턴스가 수용됩니다. 마지막으로, 일반적인 인증 메커니즘 뒤에 연쇄된 AnonymousAuthenticationFilter는 SecurityContextHolder에 기존 Authentication이 없는 경우 자동으로 AnonymousAuthenticationToken을 추가합니다. 필터와 인증 제공자는 다음과 같이 정의됩니다:
<bean id="anonymousAuthFilter"
class="org.springframework.security.web.authentication.AnonymousAuthenticationFilter">
<property name="key" value="foobar"/>
<property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"/>
</bean>
<bean id="anonymousAuthenticationProvider"
class="org.springframework.security.authentication.AnonymousAuthenticationProvider">
<property name="key" value="foobar"/>
</bean>이 구성에서 key는 필터와 인증 제공자 간에 공유되어, 전자에 의해 생성된 토큰이 후자에 의해 수용됩니다. 이는 익명 인증을 체계적으로 관리하여 익명 사용자도 일정 수준의 보안 권한을 부여받을 수 있게 합니다.
익명 인증은 특히 공개적으로 접근 가능한 사이트에서 게스트 사용자에게 일부 기능을 제공할 때 유용하며, 동시에 전체 시스템의 보안 체계 내에서 관리될 수 있습니다. 이러한 설정은 익명 사용자에게도 특정 역할을 부여하여 접근 제어를 좀 더 세밀하게 관리할 수 있게 해 줍니다.
키 속성의 사용은 실제 보안을 제공하는 것으로 간주되어서는 안 됩니다. 이것은 단지 기록 유지를 위한 수단에 불과합니다. AnonymousAuthenticationProvider가 포함된 ProviderManager를 공유하는 경우, 인증 클라이언트가 Authentication 객체를 구성할 수 있는 시나리오(예: RMI 호출과 같은 경우)에서 악의적인 클라이언트가 스스로 생성한 AnonymousAuthenticationToken(선택한 사용자 이름과 권한 목록 포함)을 제출할 수 있습니다. 만약 키가 추측 가능하거나 알아낼 수 있다면, 익명 제공자에 의해 토큰이 수용될 것입니다. 이것은 일반적인 사용에서는 문제가 되지 않습니다. 그러나 RMI를 사용하는 경우, HTTP 인증 메커니즘에 사용하는 것과 동일한 ProviderManager를 공유하는 대신, 익명 제공자를 생략한 맞춤형 ProviderManager를 사용해야 합니다.
userAttribute는 usernameInTheAuthenticationToken,grantedAuthority[,grantedAuthority] 형식으로 표현됩니다. 이 문법은 InMemoryDaoImpl의 userMap 속성에서 등호 기호 이후에도 동일하게 사용됩니다.
앞서 설명한 바와 같이 익명 인증의 이점은 모든 URI 패턴에 보안을 적용할 수 있다는 것입니다. 다음 예시에서 보여주듯이:
<bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
<property name="securityMetadata">
<security:filter-security-metadata-source>
<security:intercept-url pattern='/index.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>
<security:intercept-url pattern='/hello.htm' access='ROLE_ANONYMOUS,ROLE_USER'/>
<security:intercept-url pattern='/logoff.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>
<security:intercept-url pattern='/login.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>
<security:intercept-url pattern='/**' access='ROLE_USER'/>
</security:filter-security-metadata-source>" +
</property>
</bean>익명 인증 논의를 마무리하는 것은 AuthenticationTrustResolver 인터페이스와 그에 해당하는 AuthenticationTrustResolverImpl 구현입니다. 이 인터페이스는 isAnonymous(Authentication) 메소드를 제공하여 관심 있는 클래스가 이 특별한 인증 상태를 고려할 수 있도록 합니다. ExceptionTranslationFilter는 AccessDeniedException 인스턴스를 처리할 때 이 인터페이스를 사용합니다. AccessDeniedException이 발생하고 인증이 익명 타입인 경우, 필터는 403(금지) 응답을 던지는 대신 AuthenticationEntryPoint를 시작하여 원칙적으로 제대로 인증할 수 있도록 합니다. 이는 필요한 구분입니다. 그렇지 않으면 원칙적으로 항상 "인증됨"으로 간주되고 폼, 기본, 다이제스트 또는 기타 정상적인 인증 메커니즘을 통해 로그인할 기회를 결코 얻지 못할 것입니다.
우리는 종종 이전 인터셉터 구성에서 ROLE_ANONYMOUS 속성이 접근 제어를 정의할 때 실질적으로 동일한 것인 IS_AUTHENTICATED_ANONYMOUSLY로 대체되는 것을 봅니다. 이것은 인가 장에서 다루는 AuthenticatedVoter의 사용 예입니다. AuthenticatedVoter는 AuthenticationTrustResolver를 사용하여 이 특정 구성 속성을 처리하고 익명 사용자에게 접근을 허용합니다. AuthenticatedVoter 접근 방식은 익명, 기억하기, 완전히 인증된 사용자를 구분할 수 있기 때문에 더 강력합니다. 그러나 이 기능이 필요하지 않다면, 스프링 시큐리티의 표준 RoleVoter에 의해 처리되는 ROLE_ANONYMOUS를 사용할 수 있습니다.
스프링 MVC는 자체 인자 해석기를 사용하여 Principal 타입의 파라미터를 해석합니다.
이것은 다음과 같은 구조를 의미합니다:
@GetMapping("/")
public String method(Authentication authentication) {
if (authentication instanceof AnonymousAuthenticationToken) {
return "anonymous";
} else {
return "not anonymous";
}
}익명 요청일지라도 항상 "not anonymous"를 반환할 것입니다. 그 이유는 스프링 MVC가 HttpServletRequest#getPrincipal을 사용하여 파라미터를 해석하기 때문인데, 요청이 익명일 경우 이 값은 null입니다.
익명 요청에서 Authentication을 얻고 싶다면 @CurrentSecurityContext를 사용하세요:
익명 요청에 CurrentSecurityContext 사용하기
@GetMapping("/")
public String method(@CurrentSecurityContext SecurityContext context) {
return context.getAuthentication().getName();
}