In my spring application, I would like that a SecurityContext
always holds an Authentication
. If it's not a regular UsernamePasswordAuthenticationToken
, it will be a PreAuthenticatedAuthenticationToken
describing the "system user." This has reasons within different system function which requires a user. To avoid a special treatment if there is no user context, I merely want to add the system context. IMHO, this has also to do with the single responsibility principle.
To achieve this, I can simply implement my own SecurityContextHolderStrategy
and set the it to the SecurityContextHolder
with SecurityContextHolder.setStrategyName(MyStrategyClassName);
Now to the problem:
The default SecurityContextHolderStrategy
is the ThreadLocalSecurityContextHolderStrategy
. I'm happy with this strategy and how it works. The only thing which I would change is the getContext()
method.
public SecurityContext getContext() {
SecurityContext ctx = CONTEXT_HOLDER.get();
if (ctx == null) {
ctx = createEmptyContext();
CONTEXT_HOLDER.set(ctx);
}
return ctx;
}
to
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;
}
This is not possible as the ThreadLocalSecurityContextHolderStrategy
class is not public
. Of course I can simply copy paste the code of the ThreadLocalSecurityContextHolderStrategy
into my own SecurityContextHolderStrategy
and implement the getContext()
method the way I want. But this gives me the feeling as I might be on the wrong path.
How could I achieve a "system user" Authentication
as default for a new SecurityContext
?
Update
My approach above is apparently not a solution as it is extremely invasive, creates redundant code and needs special treatment within the web filter chain. But it should give an understanding of my goal. I'm looking for a solution, which fits as seamless as possible to the native spring security implementation. My problem is that I'm quite fixed on the invasive approach. How can this solve nicely? I cannot imagine that I'm the first person with this requirement. Or is the whole concept altogether wrong?
If got the following solution, which is quite slick and doesn't collide or interfere with anything. In generall I have two situations where I'll have a
null
authentication:MODE_INHERITABLETHREADLOCAL
config depending on use case, more details see below.)Solution to 1.
This still leaves the problem with the main system thread. This is very easily handled by just setting the context on system start up. Also I configure the
SecurityContextHolder
to use aInheritableThreadLocalSecurityContextHolderStrategy
so all child threads will inherit theSecurityContext
. We make this setting everytime the application context refreshes. This allows to use@DirtiesContext
when running security context related tests..Solution to 2.
As I have configured the
SecurityContextHolder
with MODE_INHERITABLETHREADLOCAL. A scheduled thread will inheriet his parentSecuritycontext
. In my use case this is not wanted as this would mean the following: If a scheduled task gets initialized dua a user action, it would run under the usersSecurityContext
. As I do not want to loose a scheduled task on system reboot, I'll persist them. Which would lead to that the same task which was before initialized with the usersSecurityContext
, will get intitialize with the systemsSecurityContext
on reboot. This generates an inconsitence. Therefor I configure my scheduler too.I simply configure the
@Scheduled
annotation to be executed by aDelegatingSecurityContextScheduledExecutorService
allowing me to set aSecurityContext
.With this two configurations, I'll always will have a SystemUser context if the thread wasn't initialized by the web container.
Doesn't sound right to create a populated context within
createEmptyContext()
:o)As it is stated here, "Once the request has been authenticated, the Authentication will usually be stored in a thread-local SecurityContext managed by the SecurityContextHolder by the authentication mechanism which is being used.", I'd rather extend
UsernamePasswordAuthenticationFilter
and overwriteattemptAuthentication
to set thePreAuthenticatedAuthenticationToken
in case of a failed username password verification.Edit
I think for system-internal tasks it depends how/by what they are executed. For
Executor
, there is an example setting up the context as you described above in the thread running these executions:The
@Configuration
creating this bean implementsSchedulingConfigurer
.