While using Spring's #Cacheable, how to make sure the cache does not last longer than the actual session timeout?
Suppose your caches are defined as below,
#Cacheable("cacheName1")
public Map<String, List<String>> getMethod1(){
}
#Cacheable("cacheName2")
public Map<String, List<String>> getMethod2(){
}
then call below method while the user clicks logout / session expires.
#CacheEvict(value = { "cacheName1", "cacheName2"}, allEntries = true)
public void evictAllCache(){
logger.info("All Cache Evict");
}
Extend CacheManager to handle bucket's name, e.g. #session_#name
Extend HttpSessionListener to make a clean up when session is destroyed
please find my draft sample of explicit cache per session below:
https://gist.github.com/pyanoveugen/b360622dc76136064b0215136f402837
Related
It's a common best practice to renew the HTTP session when logging in a user. This will force a new session ID, avoiding session fixation vulnerabilities.
Is there a preferred pattern for implementing this with CDI when #SessionScoped beans are involved? The difficulty is that by invalidating the current HTTP session, you'll then get a different session-scoped bean with the next request, but not until the next request.
For example, assume a session bean for storing user login information:
#Named("sessionbean")
#SessionScoped
public class SessionBean implements Serializable {
private int userId;
private String username;
private List<String> privileges;
// Accessors omitted
}
And another bean for managing the login:
#Named("loginbean")
#ViewScoped
public class LoginBean implements Serializable {
private String username;
private String password;
#Inject private SessionBean session;
#Inject private SessionManager sessionManager;
#Inject private PrivilegeManager privilegeManager;
public String doLogin() {
String destinationUrl;
if (validate(username, password)) {
FacesContext context = FacesContext.getCurrentInstance();
// force renewal of HTTP session
context.getExternalContext().invalidateSession();
// retrieve new session bean ** No longer works with CDI **
Application app = context.getApplication();
session = app.evaluateExpressionGet(context, "#{sessionbean}", SessionBean.class);
session.setUsername(username);
session.setSessionId(sessionManager.createNewSession(username));
session.setPrivileges(privilegeManager.getPrivileges(username));
destinationUrl = createLandingPageUrl();
} else {
destinationUrl = createFailureUrl("Unknown user or password");
}
return destinationUrl;
}
}
With Managed Beans this would retrieve a new SessionBean, but with CDI, the code above would just return the same SessionBean. Any recommendations or clever ideas?
The difficulty is that by invalidating the current HTTP session, you'll then get a different session-scoped bean with the next request, but not until the next request.
Then don't invalidate the session, but change the session ID. In other words, don't use HttpSession#invalidate(), but use HttpServletRequest#changeSessionId() (new since Servlet 3.1, which you should undoubtedly already be using given that you're using JSF 2.3).
In code, replace
// force renewal of HTTP session object
context.getExternalContext().invalidateSession();
by
// force renewal of HTTP session ID
((HttpServletRequest) context.getExternalContext().getRequest()).changeSessionId();
This basically changes the JSESSIONID cookie without changing the HttpSession. It's perfect for session fixation prevention.
Explicitly invalidating the session is usually only useful during logout.
I'm going to restrict this answer to be solely about CDI since I am not a security expert. I also don't know whether the general thing being asked for is a good idea or not. Regardless, here is how I think you would do what you're asking for.
Expressed in purely CDI terms, the question can be rephrased like:
I have an object that I know came from a particular Context. I know the lifecycle of objects produced by this Context. How can I properly tell the Context to invalidate the current object that it is managing, and load or create a new one?
The general approach is going to be:
#Inject a Provider<SessionBean> instead of SessionBean directly (this will let you ask CDI for the "new" object properly)
#Inject a BeanManager (so you can get the right Context that manages SessionScoped objects)
ask the BeanManager to give you the AlterableContext corresponding to the SessionScoped annotation
tell the AlterableContext to destroy the current bean's contextual instance
call Provider.get() to cause a new one to be created
So the relevant parts of your doLogin method might look like this (untested):
final AlterableContext context = (AlterableContext) this.beanManager.getContext(SessionScoped.class);
assert context != null;
final Bean<?> bean = beanManager.resolve(beanManager.getBeans(SessionBean.class));
assert bean != null;
context.destroy(bean);
final SessionBean newSessionBean = this.sessionBeanProvider.get();
assert newSessionBean != null;
I think that should work.
I have a osgi component which works with JCR (for example, CRUD).
#Component
#Service
public class SomeServiceImpl implements SomeService {
#Reference
private ResourceResolverFactory resourceResolverFactory;
private ResourceResolver resourceResolver;
#Activate
private void init() {
resourceResolver = resourceResolverFactory.getServiceResourceResolver(
Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, "myService"));
}
#Override
public void serve() {
//does something with resourceResolver
}
#Deactivate
private void dispose() {
resourceResolver.close();
}
}
It creates new instance of resourceResolver and keeps it as long as this service is alive. From time to time this service is invoked outside.
My questions are:
Is it correct approach where I once create resourceResolver and reuse it? Is it constantly?
Do I have guarantees that underlying session will not be expired?
By the way How long resourceResolver and their session lives and where can I see it?
What about concurrency? Imagine this service is invoked from serveral places parallely, Does Jackrabbit guarantee me consistency?
#Component
#Service
public class SomeServiceImpl implements SomeService {
#Reference
private SlingRepository slingRepository;
private Session session;
#Activate
private void init() {
session = slingRepository.login();
}
#Override
public void serve() {
//does something with session
}
#Deactivate
private void dispose() {
session.logout();
}
}
The same questions for another service (with session implementation).
It will be nice to see some proofs if it's possible. Maybe docs...
Thanks.
Is it correct approach where I once create resourceResolver and reuse it? Is it constantly?
No, it is not. It is perfect example of bad practice. Creation of resourceResolver is lightweight you can create as many as you need.
Note: you have to always close resourceResolver after usage but be careful and don't close it to early.
Do I have guarantees that underlying session will not be expired?
No you don't. AEM is collecting unclosed sessions after some time.
By the way How long resourceResolver and their session lives and where can I see it?
This session will became invalid after first concurrent write to the same resource. IRL big amount of changes even without conflict can fail on save.
What about concurrency? Imagine this service is invoked from serveral places parallely, Does Jackrabbit guarantee me consistency?
JCR session is supporting concurrency in scope of one session. Main assumption that will always create new session per update request.
The same questions for another service (with session implementation).
ResourceResolver is working over the Session it is just higher level of API.
I've written a custom Validation Annotation and a ConstraintValidator implementation, which uses a Spring Service (and executes a Database Query):
public class MyValidator implements ConstraintValidator<MyValidationAnnotation, String> {
private final MyService service;
public MyValidator(MyService service) {
this.service = service;
}
#Override
public void initialize(MyValidationAnnotation constraintAnnotation) {}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return service.exists(value);
}
}
It's used like this:
public class MyEntity {
#Valid
List<Foo> list;
}
public class Foo {
#MyValidationAnnotation
String id;
}
This works quite nice, but service.exists(value) is getting called for every item within the list, which is correct, but could/should be optimized.
Question:
When validating an instance of MyEntity, I'd like to cache the results of the service.exists(value) calls.
I don't want to use a static HashMap<String, Boolean>, because this would cache the results for the entire application lifetime.
Is it possible to access some kind of Constraint Validation Context, which only exists while this particular validation is running, so I can put there the cached results?
Or do you have some other solution?
Thanks in advance!
You can use Spring's cache support. There might be other parts in the application which needs caching and this can be reused. And the setup is very simple too. And it will keep your code neat and readable.
You can cache your service calls. You need to put annotation on your service methods and a little bit of configuration.
And for cache provider you can use Ehcache. You have many options like setting ttl and max number of elements that can be cached and eviction policy etc etc if needed.
Or you can implement your own cache provider if your needs are simple. And if it is web request, In this cache you may find ThreadLocal to be useful. you can do all caching for this running thread using threadlocal. When the request is processed you can clear the threadlocal cache.
I am working with Spring 4 and Hazelcast 3.2. I am trying to add a new record to existing cache with below code. somehow cache is not getting updated and at the same time I don't see any errors also. below is the code snippet for reference.
Note:- Cacheable is working fine, only cacheput is not working. Please throw light on this
#SuppressWarnings("unchecked")`enter code here`
#Transactional(readOnly = true, propagation = Propagation.REQUIRED)
#Cacheable(value="user-role-data")
public List<User> getUsersList() {
// Business Logic
List<User> users= criteriaQuery.list();
}
#SuppressWarnings("unchecked")
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
#CachePut(value = "user-role-data")
public User addUser(User user) {
return user;
}
I had the same issue and managed to solved it. The issue seemed to be tied to the transaction management.
Bascially updating the cache in the same method where you are creating or updating the new record does not work because the transaction was not committed. Here's how I solved it.
Service layer calls repo to insert user
Then go back to service layer
After the insert /update db call
In the service layer I called a refresh cache method
That returned the user data and this method has the cacheput annotation
After that it worked.
An alternative approach is you could use #CacheEvict(allEntries = true) on the method used to Save or Update or Delete the records. It will flush the existing cache.
Example:
#CacheEvict(allEntries = true)
public void saveOrUpdate(Person person)
{
personRepository.save(person);
}
A new cache will be formed with updated result the next time you call a #Cacheable method
Example:
#Cacheable // caches the result of getAllPersons() method
public List<Person> getAllPersons()
{
return personRepository.findAll();
}
I make (thanks with some users on this portal) my application that implements SessionAware.
This is my actual code :
public class UserManager extends ActionSupport implements SessionAware {
private Map<String, Object> session;
#Override
public String execute() throws Exception {
return SUCCESS;
}
public void setSession(Map<String, Object> map) {
this.session=map;
}
public String checkLogin() {
session.put("loggedOn", true);
return SUCCESS;
}
public String checkLogout() {
session.clear();
return SUCCESS;
}
}
And i check these variables on my .jsp :
<s:if test="#session['loggedOn']!=true">
DIV LOGIN
</s:if>
<s:else>
DIV LOGOUT
</s:else>
An easy piece of code.
What i'd like to know is this :
1 - the bean is (as default) request scoped. So when the request is finished it will be destroyed. But i see that, when i put a variable in the Map, it still alive on the server. How is possible? Is a variable of my Bean.
2 - Who call the setSession method? I think the servlet, due to the fact I implements that interface?
3 - I would like to detach about saving object/bean on a Session Object. I'd like to use the Bean session scoped (as for any kind of MVC framework). How can I do it on struts2?
Hope you can make clear these questions :) Cheers
1) Your bean is a struts2 action as such it is action scoped (which is a more restrictive scope than request). I say that action is a lesser scope because you can forward an action to another action in which case the previous action goes out of scope, request scoped objects however will stay in scope until the request is served. When you implement SessionAware you are provided with a reference to the SessionObject you are then putting your object into the Session object who's life span is much longer than your action. Clear your browser cache will remove the session value... you can also remove them by code be implementing SessionAware and removing the value from the map.
2 - The session already exists. You can get the session and add keys but even if you don't put anything in it, it will be there for use.
3 - You have a later question already for this topic see my answer there.