2017-11-02 3 views
4

In meiner Feder-Anwendung möchte ich, dass eine SecurityContext immer eine Authentication hält. Wenn es kein reguläres UsernamePasswordAuthenticationToken ist, wird es ein PreAuthenticatedAuthenticationToken sein, der den "Systembenutzer" beschreibt. Dies hat Gründe innerhalb verschiedener Systemfunktionen, die einen Benutzer erfordern. Um eine spezielle Behandlung zu vermeiden, wenn es keinen Benutzerkontext gibt, möchte ich lediglich den Systemkontext hinzufügen. IMHO, das hat auch mit dem Grundsatz der einheitlichen Verantwortung zu tun.SecurityContext mit Standard-System-Authentifizierung/Benutzer

Um dies zu erreichen, kann ich einfach meine eigenen SecurityContextHolderStrategy und stellen Sie die es die SecurityContextHolder mit SecurityContextHolder.setStrategyName(MyStrategyClassName);

Nun zum Problem implementieren:

Der Standard SecurityContextHolderStrategy die ThreadLocalSecurityContextHolderStrategy ist. Ich bin glücklich mit dieser Strategie und wie es funktioniert. Das einzige, was ich ändern würde, ist die getContext() Methode.

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

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

zu

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; 
} 

Dies ist nicht möglich, da die ThreadLocalSecurityContextHolderStrategy Klasse ist nicht public. Natürlich kann ich einfach den Code der ThreadLocalSecurityContextHolderStrategy in meine eigene SecurityContextHolderStrategy einfügen und die getContext() Methode so implementieren, wie ich will. Aber das gibt mir das Gefühl, als wäre ich auf dem falschen Weg.

Wie konnte ich einen "Systembenutzer" Authentication als Standard für eine neue SecurityContext erreichen?

aktualisieren

oben Mein Ansatz ist offenbar keine Lösung, da es extrem invasiv ist, schafft redundanten Code und benötigt spezielle Behandlung innerhalb der Web-Filterkette. Aber es sollte mein Ziel verstehen. Ich suche nach einer Lösung, die so nahtlos wie möglich in die native Spring Security Implementierung passt. Mein Problem ist, dass ich ziemlich auf den invasiven Ansatz fixiert bin. Wie kann das gut gelöst werden? Ich kann mir nicht vorstellen, dass ich die erste Person mit dieser Anforderung bin. Oder ist das ganze Konzept insgesamt falsch?

+0

Die Zuordnung eines Authentifizierungsobjekts zum SecuruityContext für eine bestimmte Sitzung erfolgt normalerweise über die Spring-Sicherheits-HTTP-Filter. Wenn der Systembenutzer über eine Anfrage zugreift, versuchen Sie möglicherweise einen zusätzlichen Authentifizierungsfilter, der das Authentifizierungsobjekt entsprechend konfiguriert. Wenn nicht (programmatisch usw.), wäre es viel sauberer, in Refactoring-Verwendungen zu schauen, wo Sie die secruityContext.getAuthenitiation() verwenden und sich mit der Abwesenheit in der Anwendungslogik befassen, anstatt zu versuchen, das Auth-Framework-Verhalten zu massieren. – tom01

+0

Das Problem ist, dass ich Aufgaben geplant habe. Diese geplante Aufgabe wird keine 'HttpSecurity' übergeben, da sie intern ausgeführt werden. Auch die 'InheritableThreadLocalSecurityContextHolderStrategy' wird es nicht tun, da eine geplante Aufgabe vom System direkt nach einem Neustart instanziiert werden kann. –

+0

Stellen Sie sich auch einen anfänglichen Systemstart vor, bei dem einige Standardbenutzerdatensätze erstellt werden. Dies sollte auch mit einem Sicherheitskontext für Systembenutzer geschehen. –

Antwort

2

Wenn die folgende Lösung, die ziemlich glatt ist und nicht mit irgendetwas kollidiert oder interferieren. In generall habe ich zwei Situationen, in denen ich eine null Authentifizierung haben werden:

  1. Hauptsystem-Thread.
  2. Geplante Aufgabe ausführen. (Könnte mit MODE_INHERITABLETHREADLOCAL Config je nach Anwendungsfall gelöst werden, finden Sie weitere Details unten.)

Lösung auf 1

Dies läßt immer noch das Problem mit dem Hauptsystem-Thread. Dies wird sehr einfach gehandhabt, indem einfach der Kontext beim Systemstart festgelegt wird. Außerdem konfiguriere ich die SecurityContextHolder, um eine InheritableThreadLocalSecurityContextHolderStrategy zu verwenden, so dass alle untergeordneten Threads die SecurityContext erben werden. Wir machen diese Einstellung jedes Mal, wenn der Anwendungskontext aktualisiert wird. Dies ermöglicht @DirtiesContext zu verwenden, wenn Sicherheitskontext bezogene Tests laufen ..

@Component 
public class SecurityContextConfiguration { 

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

Lösung zu 2.

Als ich die SecurityContextHolder mit MODE_INHERITABLETHREADLOCAL konfiguriert haben. Ein geplanter Thread erbt seinen Elternknoten Securitycontext. In meinem Anwendungsfall ist dies nicht erwünscht, da dies Folgendes bedeuten würde: Wenn eine geplante Aufgabe mit einer Benutzeraktion initialisiert wird, würde sie unter den Benutzern SecurityContext ausgeführt. Da ich beim Systemneustart keine geplante Aufgabe verlieren möchte, behalte ich sie bei. Das würde dazu führen, dass die gleiche Aufgabe, die vorher mit den Benutzern SecurityContext initialisiert wurde, beim Neustart mit den Systemen SecurityContext initialisiert wird. Dies erzeugt eine Inkonsistenz. Dazu konfiguriere ich auch meinen Scheduler.

Ich einfach konfigurieren Sie die @Scheduled Annotation von einer DelegatingSecurityContextScheduledExecutorService ausgeführt werden, so dass ich eine SecurityContext einstellen kann.

@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; 
    } 

} 

Mit diesen beiden Konfigurationen, werde ich immer einen Systemuser Kontext, wenn der Faden nicht durch den Web-Container initialisiert wurde.

0

klingt nicht richtig ein bevölkerten Kontext innerhalb createEmptyContext() zu erstellen: o)

Wie es here angegeben, „Sobald die Anforderung authentifiziert wurde, die Authentifizierung wird in der Regel in ein Thread-local gespeichert werden SecurityContext verwaltet durch den SecurityContextHolder durch den Authentifizierungsmechanismus, der verwendet wird. ", Würde ich lieber UsernamePasswordAuthenticationFilter verlängern und attemptAuthentication überschreiben, um die PreAuthenticatedAuthenticationToken im Falle einer fehlgeschlagenen Benutzernamen Passwortverifizierung zu setzen.

bearbeiten

denke ich für systeminterne Aufgaben hängt es wie/von dem, was sie ausgeführt werden. Für Executor gibt es eine example den Kontext der Einrichtung, wie Sie oben im Thread diese Hinrichtungen ausgeführt wird beschrieben:

@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; 
} 

Die @Configuration Erstellung dieser Bean implementiert SchedulingConfigurer.

+0

Das ist wahr :) Ich habe Angst, dass die Idee mit dem 'UsernamePasswordAuthenticationFilter' nicht funktioniert. Stellen Sie sich eine persistent geplante Aufgabe innerhalb des Systems vor. Dies wird innerhalb des Systems ausgeführt, ohne jemals einen Authentifizierungsfilter aufzurufen. –

+0

Ich habe den Code auf eine akzeptable Implementierung aktualisiert. –

Verwandte Themen