spring security current user in thread - spring

hi i have some problems when use spring security in thread scope
System.out.println(((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getId());
new Thread(() -> System.out.println(((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getId())).start();
this two lines should give me current user id
the first line work as expected
the second line give me NullPointerException as there is no current user it is null value
i found this problem as i want to save many rows to the song table and it hava #CreatedBy user and this will ask for current user in thread and will fail as this will give null value for current user

If you want spawned threads to inherit SecurityContext of the parent thread, you should set MODE_INHERITABLETHREADLOCAL strategy.
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL)
There was an issue, when using this with thread pools. This seems to be fixed.

you can transfer the SecurityContext from one Thread to another
Runnable originalRunnable = new Runnable() {
public void run() {
// invoke secured service
}
};
SecurityContext context = SecurityContextHolder.getContext();
DelegatingSecurityContextRunnable wrappedRunnable =
new DelegatingSecurityContextRunnable(originalRunnable, context);
new Thread(wrappedRunnable).start();
See Concurrency Support
https://docs.spring.io/spring-security/reference/features/integrations/concurrency.html

If you want all your child threads to inherit SecurityContextHolder from the ThreadLocal you can use a method annotated with #PostConstruct to set it globally. Now your child threads will have access to the same SecurityContextHolder.
#PostConstruct
void setGlobalSecurityContext() {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
Cheers

Related

Activiti Escalation Listener Configuration

I am using activiti 5.18.
Behind the scenes : There are few task which are getting routed though a workflow. Some of these tasks are eligible for escalation. I have written my escalation listener as follows.
#Component
public class EscalationTimerListener implements ExecutionListener {
#Autowired
ExceptionWorkflowService exceptionWorkflowService;
#Override
public void notify(DelegateExecution execution) throws Exception {
//Process the escalated tasks here
this.exceptionWorkflowService.escalateWorkflowTask(execution);
}
}
Now when I start my tomcat server activiti framework internally calls the listener even before my entire spring context is loaded. Hence exceptionWorkflowService is null (since spring hasn't inejcted it yet!) and my code breaks.
Note : this scenario only occurs if my server isn't running at the escalation time of tasks and I start/restart my server post this time. If my server is already running during escalation time then the process runs smoothly. Because when server started it had injected the service and my listener has triggered later.
I have tried delaying activiti configuration using #DependsOn annotation so that it loads after ExceptionWorkflowService is initialized as below.
#Bean
#DependsOn({ "dataSource", "transactionManager","exceptionWorkflowService" })
public SpringProcessEngineConfiguration getConfiguration() {
final SpringProcessEngineConfiguration config = new SpringProcessEngineConfiguration();
config.setAsyncExecutorActivate(true);
config.setJobExecutorActivate(true);
config.setDataSource(this.dataSource);
config.setTransactionManager(this.transactionManager);
config.setDatabaseSchemaUpdate(this.schemaUpdate);
config.setHistory(this.history);
config.setTransactionsExternallyManaged(this.transactionsExternallyManaged);
config.setDatabaseType(this.dbType);
// Async Job Executor
final DefaultAsyncJobExecutor asyncExecutor = new DefaultAsyncJobExecutor();
asyncExecutor.setCorePoolSize(2);
asyncExecutor.setMaxPoolSize(50);
asyncExecutor.setQueueSize(100);
config.setAsyncExecutor(asyncExecutor);
return config;
}
But this gives circular reference error.
I have also tried adding a bean to SpringProcessEngineConfiguration as below.
Map<Object, Object> beanObjectMap = new HashMap<>();
beanObjectMap.put("exceptionWorkflowService", new ExceptionWorkflowServiceImpl());
config.setBeans(beanObjectMap);
and the access the same in my listener as :
Map<Object, Object> registeredBeans = Context.getProcessEngineConfiguration().getBeans();
ExceptionWorkflowService exceptionWorkflowService = (ExceptionWorkflowService) registeredBeans.get("exceptionWorkflowService");
exceptionWorkflowService.escalateWorkflowTask(execution);
This works but my repository has been autowired into my service which hasn't been initialized yet! So it again throws error in service layer :)
So is there a way that I can trigger escalation listeners only after my entire spring context is loaded?
Have you tried binding the class to ApplicationListener?
Not sure if it will work, but equally I'm not sure why your listener code is actually being executed on startup.
Try to set the implementation type of listeners using Java class or delegate expression and then in the class implement JavaDelegate instead of ExecutionListener.

Is Security Context Holder Thread Safe

I am using spring security. I was wondering if in a filter I set
SecurityContextHolder.getcontext().setAuthentication(null)
for one particular user who has at that instance suppose 100 api calls, would each of these calls securitycontextholder's authentication become null?
SecurityContextHolder is threadsafe by default. So using this statement SecurityContextHolder.getcontext().setAuthentication(null) would actually be unsetting authentication on a per-thread basis.
If you see the implementation of SecurityContextHolder.getcontext(), you will find that getContext() is actually returning a thread safe object:
final class ThreadLocalSecurityContextHolderStrategy implements
SecurityContextHolderStrategy {
// ~ Static fields/initializers
// =====================================================================================
//.....
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<SecurityContext>();
public SecurityContext getContext() {
SecurityContext ctx = contextHolder.get();
if (ctx == null) {
ctx = createEmptyContext();
contextHolder.set(ctx);
}
return ctx;
}
}
As you can see in above code, contextHolder is actually the ThreadLocal object, which are by default thread safe variables.
So by changing values using SecurityContextHolder.getcontext().setAuthentication(null) will not effect authentication object of other threads(Note: Each & every web request are handled by separate threads).

Tomcat Hazelcast Session Store Session Attributes Vanishing

I am trying to setup a Tomcat cluster on AWS and since AWS does not support IP multicasting, one of the option is tomcat clustering using DB
That is well understood, however, due to performance penalties related to DB calls, I am currently considering Hazelcast as the session store. The current Hazelcast filter approach does not work out for me as there are other filters on the web app and they are somewhat interfering and a better and cleaner approach would be to configure the PersistenceManager with a custom store implementation and configure the same on the tomcat/conf context.xml, the configuration section is provided below:
<Manager className="org.apache.catalina.session.PersistentManager"
distributable="true"
maxActiveSessions="-1"
maxIdleBackup="2"
maxIdleSwap="5"
processingTime="1000"
saveOnRestart="true"
maxInactiveInterval="1200">
<Store className="com.hm.vigil.platform.session.HC_SessionStore"/>
</Manager>
The sessions are being saved in the Hazelcast instance and the trace from tomcat is below:
---------------------------------------------------------------------------------------
HC_SessionStore == Saving Session ID == C19A496F2BB9E6A4A55E70865261FC9F SESSION == StandardSession[
C19A496F2BB9E6A4A55E70865261FC9F]
SESSION ATTRIBUTE :: USER_IDENTIFIER :: 50
SESSION ATTRIBUTE :: APPLICATION_IDENTIFIER :: APPLICATION_1
SESSION ATTRIBUTE :: USER_EMAIL :: x#y.com
SESSION ATTRIBUTE :: USER_ROLES :: [PLATFORM_ADMIN, CLIENT_ADMIN, PEN_TESTER, USER]
SESSION ATTRIBUTE :: CLIENT_IDENTIFIER :: 1
---------------------------------------------------------------------------------------
03-Nov-2015 15:12:02.562 FINE [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.ca
talina.session.PersistentManagerBase.processExpires End expire sessions PersistentManager processing
Time 75 expired sessions: 0
03-Nov-2015 15:12:02.563 FINE [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.ca
talina.session.PersistentManagerBase.processExpires Start expire sessions PersistentManager at 14465
43722563 sessioncount 0
03-Nov-2015 15:12:02.577 FINE [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.ca
talina.session.PersistentManagerBase.processExpires End expire sessions PersistentManager processing
Time 14 expired sessions: 0
The above trace if from the 'save' method as overridden by the store implementation, the code is provided below:
#Override
public void save(Session session) throws IOException {
//System.out.println("HC_SessionStore == Saving Session ID == "+session.getId()+" SESSION == "+session);
try{
String sessionId=session.getId();
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(session);
oos.close();
byte[] serializedSession=baos.toByteArray();
sessionStore.put(sessionId,serializedSession);
sessionCounter++;
System.out.println("---------------------------------------------------------------------------------------");
System.out.println("HC_SessionStore == Saving Session ID == "+sessionId+" SESSION == "+session);
Enumeration<String> attributeNames=((StandardSession)session).getAttributeNames();
while(attributeNames.hasMoreElements()){
String attributeName=attributeNames.nextElement();
System.out.println("SESSION ATTRIBUTE :: "+attributeName+" :: "+((StandardSession)session).getAttribute(attributeName));
}//while closing
System.out.println("---------------------------------------------------------------------------------------");
}catch(Exception e){throw new IOException(e);}
}//save closing
Where the 'sessionStore' is a Hazelcast distributed Map.
The corresponding 'load' method of the store is as follows:
#Override
public Session load(String sessionId) throws ClassNotFoundException, IOException {
Session session=null;
try{
byte[] serializedSession=(byte[])sessionStore.get(sessionId);
ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(serializedSession));
//Read the saved session from serialized state
//StandardSession session_=new StandardSession(manager);
StandardSession session_=(StandardSession)ois.readObject();
session_.setManager(manager);
ois.close();
//Initialize the transient properties of the session
ois=new ObjectInputStream(new ByteArrayInputStream(serializedSession));
session_.readObjectData(ois);
session=session_;
ois.close();
System.out.println("===========================================================");
System.out.println("HC_SessionStore == Loading Session ID == "+sessionId+" SESSION == "+session);
Enumeration<String> attributeNames=session_.getAttributeNames();
while(attributeNames.hasMoreElements()){
String attributeName=attributeNames.nextElement();
System.out.println("SESSION ATTRIBUTE :: "+attributeName+" :: "+session_.getAttribute(attributeName));
}//while closing
System.out.println("===========================================================");
}catch(Exception e){throw new IOException(e);}
return session;
}//load closing
Now, one of the most intriguing thing is that while the 'store' method gets called at the default interval of 60 seconds, the 'load' method is never called with the net impact that any session attributes that are saved is lost after a while, which is most unusual. Technically any new session attributes that are bound to the session will be saved in the Hazelcast once the 'save' method is called and the manager is configured to swap out every 5 seconds.
However, the session attribute is lost (the new one), the old ones are still there. But whatever it is the 'load' method is not called (at least I don't see the trace).
Some help on this will be really appreciated.
Hope this helps someone, the problem is actually in the following code sections:
public void save(Session session) throws IOException method:
String sessionId=session.getId();
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(session);
oos.close();
byte[] serializedSession=baos.toByteArray();
sessionStore.put(sessionId,serializedSession);
public Session load(String sessionId) throws ClassNotFoundException, IOException method:
byte[] serializedSession=(byte[])sessionStore.get(sessionId);
ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(serializedSession));
//Read the saved session from serialized state
//StandardSession session_=new StandardSession(manager);
StandardSession session_=(StandardSession)ois.readObject();
session_.setManager(manager);
ois.close();
//Initialize the transient properties of the session
ois=new ObjectInputStream(new ByteArrayInputStream(serializedSession));
session_.readObjectData(ois);
session=session_;
ois.close();
If you notice, the session is summarily serialized and saved to the Hazelcast, that is not a problem by itself.
Now if we look at the Tomcat code for StandardSession, we see that it contains a number of transient properties that will not be serialized. So during deserialization these properties must be given values, which is done in the 'load' method, however, it is done wrongly, first it deserializes the session from the ObjectInputStream 'readObjectData' method to initialize the transient properties. In the StandardSession, 'readObjectData' calls 'doReadObject' a protected method to reinitialize the transient properties, which in turn expects that the object input stream provided is a series of objects. In our case, however, it is the entire serialized object and not the series of objects that it expects.
In fact, after enabling fine level logging on Tomcat only this exception is seen, not otherwise.
The workaround is simple, StandardSession has a method 'writeObjectData' method, which internally calls a protected method 'doWriteObject', which writes the session state in a series of objects to the output stream, reading this serialized bytes solves the problem.

Spring AsyncTask: update jsf view component

I have a long run job must run in background and after it finished I want to update jsf view component.
I used SimpleAsyncTaskExecutor to do the work. It works good but when comming to updating ui then I am getting NullPointerException.
Here is my code
SimpleAsyncTaskExecutor tasks = new SimpleAsyncTaskExecutor();
tasks.submitListenable(new Callable<String>() {
#Override
public String call() throws Exception {
//Do long time taking job in approximately 16 seconds
doTheBigJob();
//then update view component by it's id
FacesContext.getCurrentInstance().getPartialViewContext().getRenderIds().add(myComponentId);
return "";
}
});
Not: When the time is short (like 2 seconds), no NullPointerException occurs
Thanks in advence.
FacesContext.getCurrentInstance() returns null because it tries to get the context from thread local variable. But because the executing thread was not initialized by JSF (which is done by javax.faces.webapp.FacesServlet) but created by executor then the thread local variable is null.
I have no idea why NullPointerException does not occur sometimes. By default SimpleAsyncTaskExecutor creates new thread each time unless you specify a thread pool. When I recreated the example it happened every time. Maybe it does but is not logged properly...
To solve your problem you need to resort to polling. For instance you can use property of backing bean to indicate that job was finished.
#Named("someBean")
#SessionScoped
public class SomeBean {
private volatile boolean jobDone = false;
public String execute() {
SimpleAsyncTaskExecutor tasks = new SimpleAsyncTaskExecutor();
tasks.submitListenable(new Callable<String>() {
public String call() throws Exception {
//Do long time taking job in approximately 16 seconds
doTheBigJob();
jobDone = true
return "";
}
});
return null;
}
public boolean isJobDone() {
return jobDone;
}
}
On your page you enter component which is rendered when jobDone==true. For instance:
<h:outputText id="jobDoneText" rendered="#{someBean.jobDone}" value="Job finished"/>
Then using polling and AJAX you update your current page.
In pure JSF the only way to do polling is to use combination of JavaScript and JSF AJAX requests.
Alternatively you can use Primefaces component p:poll to poll for changes.
<p:poll interval="1" update="jobDoneText" />
More about polling in JSF can be found in answers to the following question: JSF, refresh periodically a component with ajax?

How do I safely detect if Session Scope exists in a service?

I have service that is called both from Quartz (w/o a session scope), and interactively from a Web Flow (w/ a session scope).
When called from the Web Flow, there may be some optional flags set and I would like to safely check for them, IF THEY EXIST.
I'm using session scope because the functions I'm calling are far down a call chain, and passing the options, or scope all the way down will touch a lot of code.
I'm wondering if there is something like:
if(someObject.session?.myFlag)
where "session" refers to the session scope if called from a web flow, or null if called from Quartz.
Thanks in advance!
As you seem to already know, accessing the session scope from a service is not something to be encouraged, because the HTTP session should only be used within the web layer (GSPs, controllers, filters, etc.).
Now you didn't hear this from me, but you can access the current session from anywhere like this:
def session = org.codehaus.groovy.grails.web.util.WebUtils.
retrieveGrailsWebRequest().session
Once you have access to the session you can check if attributes exist or retrieve them using the usual HttpSession API.
I would go with a Filter and ThreadLocal approach.
Create a class with a static ThreadLocal variable which holds a reference to an instance of the class. This instance can then be referenced from anywhere from the executing thread and it will provide access to your variables and flags. This way you are not directly referencing to HTTP session API in your service.
Finally create a Filter in which you set up the ThreadLocal value before executing the rest of the chain. Remember to clear the state of the value after the thread is complete.
class MyExecutionContext {
private static ThreadLocal instance = new ThreadLocal<MyExecutionContext>()
private HttpSession session
private ServletRequest request
// set the state for current thread
// you can add request here too, if you want/need
public static void setContext(ServletRequest req, HttpSession s) {
stateInstance.set(new MyExecutionContext(req, s))
}
// get the state of current thread
public static getContext() {
return instance.get()
}
// clear the current state
public static void clearContext() {
stateInstance.remove()
}
// private constructor
private MyExecutionContext(ServletRequest req, HttpSession s) {
request = req
session = s
}
// now the actual methods to query any kinds of things you need
// from session (or request if you gave it in the constructor)
public String getSomething() {
(String) session?.getAttribute("somethingInSession")
}
public String getSomethingElse() {
(String) request?.getAttribute("somethingInRequest")
}
}
class ContextFilter extends Filter {
public void doFilter(...) {
try {
MyExecutionContext.setContext(request, request.getSession(false))
chain.doFilter(req, res)
} finally {
// make sure you clear the state
MyExecutionContext.clearContext()
}
}
}
// usage in your service
class YourService {
def method() {
if (MyExecutionContext?.context?.something) {
// value exists in session
}
}
}

Resources