2017-11-02 11 views
4

스프링 응용 프로그램에서 SecurityContext은 항상 Authentication을 보유하고 싶습니다. 일반 UsernamePasswordAuthenticationToken이 아닌 경우 "시스템 사용자"를 나타내는 PreAuthenticatedAuthenticationToken이됩니다. 이것은 사용자를 필요로하는 다른 시스템 기능 내에서 이유가 있습니다. 사용자 컨텍스트가없는 경우 특별한 대우를 피하기 위해 시스템 컨텍스트를 추가하기 만하면됩니다. IMHO, 이것은 또한 단일 책임 원칙과 관련이 있습니다.기본 시스템 인증/사용자 인 SecurityContext

이를 달성하기 위해, 나는 단순히 SecurityContextHolderStrategy 내 자신을 구현하고 문제를 지금 SecurityContextHolder.setStrategyName(MyStrategyClassName);

으로 SecurityContextHolder로 설정할 수 있습니다 :

기본 SecurityContextHolderStrategyThreadLocalSecurityContextHolderStrategy입니다. 나는이 전략과 그것이 어떻게 작동하는지에 만족합니다. 내가 바꿀 유일한 것은 getContext() 방법입니다. ThreadLocalSecurityContextHolderStrategy 클래스 하지 public 같이

public SecurityContext getContext() { 
    SecurityContext ctx = CONTEXT_HOLDER.get(); 

    if (ctx == null) { 
     ctx = createEmptyContext(); 
     CONTEXT_HOLDER.set(ctx); 
    } 
    return ctx; 
} 

public SecurityContext getContext() { 
    SecurityContext ctx = CONTEXT_HOLDER.get(); 

    if (ctx == null) { 
     ctx = createEmptyContext(); 
     Authentication authentication = new PreAuthenticatedAuthenticationToken("system", null); 
     authentication.setAuthenticated(true); 
     ctx.setAuthentication(authentication); 
     CONTEXT_HOLDER.set(ctx); 
    } 
    return ctx; 
} 

에이 하지 수 있습니다. 물론 ThreadLocalSecurityContextHolderStrategy의 코드를 내 SecurityContextHolderStrategy에 붙여 넣기하고 원하는 방식으로 getContext() 메서드를 구현할 수 있습니다. 그러나 이것은 내가 잘못된 길 위에있을 수 있다는 느낌을줍니다.

SecurityContext에 대한 기본 설정으로 "시스템 사용자"Authentication을 어떻게 얻을 수 있습니까?

업데이트

내 접근 방식은 위가 매우 침습적이기 때문에, 분명히 해결책이 아니다 중복 코드를 생성하고 웹 필터 체인 내에서 특별한 치료를 필요로한다. 그러나 그것은 나의 목표를 이해해야한다. 원시 스프링 보안 구현에 가능한 한 매끄럽게 맞는 솔루션을 찾고 있습니다. 내 문제는 내가 침략적 접근법에 상당히 고정되어 있다는 것입니다. 이 문제를 어떻게 해결할 수 있을까요? 내가이 요구 조건을 가진 첫 번째 사람이라고 상상할 수는 없습니다. 아니면 전체 개념이 완전히 잘못 되었습니까?

+0

주어진 세션에 대한 인증 객체에 대한 SecruityContext와의 연관은 대개 Spring Security http 필터에 의해 수행됩니다. 시스템 사용자가 요청을 통해 액세스하는 경우 인증 객체를 적절하게 구성하는 추가 인증 필터를 사용해보십시오. 그렇지 않은 경우 (프로그래밍 방식 등) Autru 프레임 워크 비헤이비어를 마사지하려고 시도하는 대신 secruityContext.getAuthenitication()을 사용하는 곳의 리팩토링 용도를 조사하고 애플리케이션 로직에서 부재를 처리하는 것이 훨씬 더 명확합니다. – tom01

+0

문제는 예약 된 작업이 있다는 것입니다. 예약 된 작업은 내부적으로 실행되는 'HttpSecurity'를 전달하지 않습니다. 또한 'InheritableThreadLocalSecurityContextHolderStrategy'는 재부팅 후 시스템에서 예약 된 작업을 직접 인스턴스화 할 수 있으므로이를 수행하지 않습니다. –

+0

또한 몇 가지 기본 사용자 레코드가 생성되는 초기 시스템 시작을 상상해보십시오. 이것은 또한 시스템 사용자 보안 컨텍스트에서 발생해야합니다. –

답변

2

다음과 같은 해결책이 있는데, 이는 매우 매끄럽고 충돌하거나 간섭하지 않습니다.

  1. 홈페이지 시스템 스레드 : generall에서 나는 내가 null 인증을해야합니다 두 가지 상황이있다.
  2. 예약 된 작업을 실행 중입니다. (MODE_INHERITABLETHREADLOCAL 설정은 사용 사례에 따라 해결 될 수, 자세한 내용은 아래를 참조하십시오.)

솔루션

1에이 여전히 메인 시스템 스레드 문제를 떠난다. 이것은 시스템 시작시 컨텍스트를 설정하기 만하면 쉽게 처리 할 수 ​​있습니다. 또한 SecurityContextHolderInheritableThreadLocalSecurityContextHolderStrategy을 사용하도록 구성하면 모든 하위 스레드가 SecurityContext을 상속합니다. 응용 프로그램 컨텍스트가 새로 고침 될 때마다이 설정이 적용됩니다. 이 보안 컨텍스트 관련 테스트를 실행할 때 @DirtiesContext을 사용할 수 있습니다 ..

@Component 
public class SecurityContextConfiguration { 

    @EventListener 
    public void setupSecurityContext(ContextRefreshedEvent event) { 
    SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL); 
    SecurityContextHolder.getContext().setAuthentication(new SystemAuthentication()); 
    } 
} 

솔루션

2에 내가 MODE_INHERITABLETHREADLOCAL와 SecurityContextHolder를 구성한다. 예정된 스레드는 그의 부모 인 Securitycontext을 inheriet합니다. 내 유스 케이스에서는 다음과 같은 의미가 있기 때문에 바람직하지 않습니다. 예약 된 작업이 사용자 작업으로 초기화되면 사용자 SecurityContext에서 실행됩니다. 시스템을 다시 부팅 할 때 예약 된 작업을 풀고 싶지는 않지만이를 유지할 것입니다. 어떤 경우 사용자 SecurityContext으로 초기화되기 전과 동일한 작업이 재부팅시 시스템 SecurityContext으로 초기화되지 않습니다. 이것은 inconsitence를 생성합니다. 그래서 나는 나의 스케줄러를 구성한다.

DelegatingSecurityContextScheduledExecutorService으로 @Scheduled 주석을 구성하여 SecurityContext을 설정할 수 있습니다. 스레드가 웹 컨테이너에 의해 초기화되지 않은 경우이 두 가지 구성으로

@EnableScheduling 
@Configuration 
public class SystemAwareSchedulerConfiguration implements SchedulingConfigurer { 

    @Override 
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { 
    taskRegistrar.setScheduler(taskExecutor()); 
    } 

    @Bean 
    public ScheduledExecutorService taskExecutor() { 
    ScheduledExecutorService delegateExecutor = Executors.newSingleThreadScheduledExecutor(); 
    SecurityContext schedulerContext = createSchedulerSecurityContext(); 
    return new DelegatingSecurityContextScheduledExecutorService(delegateExecutor, schedulerContext); 
    } 

    private SecurityContext createSchedulerSecurityContext() { 
    SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); 
    securityContext.setAuthentication(new SystemAuthentication()); 
    return securityContext; 
    } 

} 

, 난 항상 SystemUser 컨텍스트를해야합니다 수 있습니다.

0

createEmptyContext() 내에서 인구 컨텍스트를 만들 오른쪽 소리하지 않습니다 : 그것은 here을 언급함에 따라 요청이 인증되면, 인증은 일반적으로을 저장됩니다 ", O)

을 스레드 로컬 SecurityContextHolder에 의해 관리되고있는 인증 메커니즘에 의해 SecurityContextHolder가 관리되고 있습니다. "라는 메시지가 나타나면 UsernamePasswordAuthenticationFilter을 확장하고 attemptAuthentication을 덮어 쓰면 사용자 이름 암호 확인에 실패한 경우 PreAuthenticatedAuthenticationToken을 설정할 수 있습니다.

편집

나는 그것을 따라 시스템 내부 작업을 위해 생각하는 방법/그들이 실행 무엇에 의해.Executor를 들어 , 당신이 실행을 실행하는 스레드에서 전술 한 바와 같이 컨텍스트를 설정 example가 :

@Bean 
public Executor taskExecutor() { 
    ScheduledExecutorService delegateExecutor = Executors.newSingleThreadScheduledExecutor(); 
    SecurityContext schedulerContext = createSchedulerSecurityContext(); 
    return new DelegatingSecurityContextScheduledExecutorService(delegateExecutor, schedulerContext); 
} 

private SecurityContext createSchedulerSecurityContext() { 
    SecurityContext context = SecurityContextHolder.createEmptyContext(); 

    Authentication authentication = new PreAuthenticatedAuthenticationToken("system", null); 
    authentication.setAuthenticated(true); 
    context.setAuthentication(authentication); 

    return context; 
} 

@Configuration이 빈을 생성 SchedulingConfigurer을 구현합니다.

+0

그건 사실이야 :) 나는'UsernamePasswordAuthenticationFilter '아이디어가 작동하지 않을 까봐 걱정이다. 시스템 내에서 지속적으로 계획된 작업을 생각해보십시오. 이것은 인증 필터를 호출하지 않고 시스템 내에서 실행됩니다. –

+0

코드를 수용 가능한 구현으로 업데이트했습니다. –