Close session of the user using primefaces and Hibernate [duplicate] - java

What is the best possible way to invalidate session within a JSF 2.0 application? I know JSF itself does not handle session. So far I could find
private void reset() {
HttpSession session = (HttpSession) FacesContext.getCurrentInstance()
.getExternalContext().getSession(false);
session.invalidate();
}
Is this method correct? Is there a way without touching the
ServletAPI?
Consider a scenario wherein a #SessionScoped UserBean handles the
login-logout of a user. I have this method in the same bean. Now
when I call the reset() method after I'm done with necessary DB
updates, what will happen to my current session scoped bean? since
even the bean itself is stored in HttpSession?

Firstly, is this method correct? Is there a way without touching the ServletAPI?
You can use ExternalContext#invalidateSession() to invalidate the session without the need to grab the Servlet API.
#ManagedBean
#SessionScoped
public class UserManager {
private User current;
public String logout() {
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
return "/home.xhtml?faces-redirect=true";
}
// ...
}
what will happen to my current session scoped bean? since even the bean itself is stored in HttpSession?
It will still be accessible in the current response, but it will not be there anymore in the next request. Thus it's important that a redirect (a new request) is fired after invalidate, otherwise you're still displaying data from the old session. A redirect can be done by adding faces-redirect=true to the outcome, as I did in the above example. Another way of sending a redirect is using ExternalContext#redirect().
public void logout() throws IOException {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.invalidateSession();
ec.redirect(ec.getRequestContextPath() + "/home.xhtml");
}
Its use is however questionable in this context as using a navigation outcome is simpler.

public void logout() {
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
}

Frontend code is:
<h:form>
<h:commandLink action="#{userManager.logout()}">
<span>Close your session</span>
</h:commandLink>
</h:form>
Backend code is:
public String logout() {
HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
if (session != null) {
session.invalidate();
}
return "/login.xhtml?faces-redirect=true";
}

Related

Autowiring HttpSession give different object than HttpServletRequest

I use Spring Security, and I found strange behavior of framework while login. Spring Security WebAuthenticationDetails has parameter sessionId which is getting from HTTP request, and it all should be good, but in fact REST request gives me another session id. If I will autowire HttpSession and then get session id from it, I will get Spring-like id. So it seems that I have two ids for one user. Is it correct? Or I missed something?
EDITED:
For example this class will gave some session id
public class AuthenticationEventListener implements ApplicationListener<AbstractAuthenticationEvent> {
#Autowired
HttpSession httpSession;
#Override
public void onApplicationEvent(AbstractAuthenticationEvent event) {
if (event instanceof AuthenticationSuccessEvent) {
LoggedUser loggedUser = (LoggedUser) event.getAuthentication().getPrincipal();
loggedUser.initSessionParams(event.getAuthentication());
String sessionId = httpSession.getId();
}
}
}
and this method will give another one:
#RequestMapping(value = "/chart")
public Map getTestStatusesChart(HttpServletRequest request) {
String sessionId= request.getSession(false).getId();
return null;
}
So the answer is next: with condition of security Spring change session id by default. To prevent such behavior you need to disable session-fixation-protection in Spring Security config. more info by link

Inject data into session from JUnit test

I need to run a JUnit vs Spring MVC test case in which preconditions include that certain data is present in the HTTP Session. Most important is that I cannot wire a session-scoped bean: I must access httpServletContext.getSession().
Before showing code, let me explain. The controller I need to test assumes that a certain datum is stored in session, otherwise throws an exception. And that is the correct behaviour for now, because that controller is never invoked without a session and the session is always initialized with application data at login time. And obviously the controller is under security.
In my test, I just need to test whether this controller returns either a redirection or a 404 not found according to the request parameters.
I thought building my test case such as
#Autowired
private HttpServletRequest httpServletRequest;
#Autowired
private ModuleManager moduleManager;
#Autowired
private WebApplicationContext webApplicationContext;
private MenuItem rootMenu;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception
{
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
// No asserzioni
.build();
rootMenu = moduleManager.getRootMenu()
.clone();
httpServletRequest.getSession()
.setAttribute(MenuItem.SESSION_KEY, rootMenu);
assertNotNull(rootMenu.getDescendant(existingSelectedMenu));
assertNull(rootMenu.getDescendant(notExistingMenu));
}
#Test
public void testNavigate() throws Exception
{
mockMvc.perform(get("/common/navigate?target=" + existingSelectedMenu))
.andExpect(status().is3xxRedirection());
assertNotSelected(rootMenu, existingSelectedMenu);
mockMvc.perform(get("/common/navigate?target=" + notExistingMenu))
.andExpect(status().is4xxClientError());
}
Part of the code is truly self-explaining. Anyway I expect /common/navigate to use the value I stored in the session. Like this
#RequestMapping(value = "/common/navigate",
method = RequestMethod.GET)
public String navigate(#RequestParam("target") String target) throws NotFoundException
{
MenuItem rootMenu = (MenuItem) httpServletRequest.getSession()
.getAttribute(MenuItem.SESSION_KEY);
if (rootMenu == null)
throw new RuntimeException("Menu not found in session"); //Never happens
MenuItem menuItem = rootMenu.getAndSelect(target);
if (menuItem == null)
throw new NotFoundException(MenuItem.class, target); //Expected
return "redirect:" + menuItem.getUrl();
}
Now guess. What happens when I run my code?
RuntimeException is thrown in the line I commented as the menu object is not found in the session
Obviously the question is implicit now, but I will still write it: how do I inject data into the Session object so that controllers under test will have them available as precondition?
Found the solution by myself now.
The problem is that the session itself must be mocked too. Spring provides a MockHttpSession class that does the trick. It can be pre-populated with all pre-conditions, but must be passed to every MockMvc request so that the mock will wire the session to the (mocked) servlet context.
Following code initializes the session
mockHttpSession = new MockHttpSession(webApplicationContext.getServletContext());
mockHttpSession.setAttribute(MenuItem.SESSION_KEY, rootMenu);
Following performs the request with mocked session wired to it
mockMvc.perform(get("/common/navigate?target=" + existingSelectedMenu).session(mockHttpSession))
.andExpect(status().is3xxRedirection());

Fetching session attributes from static methods

I have used below codes for fetching session attributes from session util class(contains static methods). Want to know that below code could be right approach in multithreaded environment or is there any better other ways for fetching sessions from util.
public static HttpSession getSession(){
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpSession session = attr.getRequest().getSession();
return session;
}
public static String getEmailIDFromSession(){
return (String)getSession().getAttribute("USER_EMAIL");
}
No, it's OK. And it is really thread safe. But from other side you should understand that it will be available only within HTTP Request Thread and from Spring MVC environment.
From other side, if you want to get that attribute from your #Controller or #Service you always can inject session there:
#Controller
class MyController {
#Autowired
private HttpSession session;
}

How to implement an automatic logout in a web application?

Our web application runs with JBoss 7.1.1 and Java (JPA2 and RichFaces4). At the moment the problem is that the application just gets stuck if the user is logged in but doesn't do anything inside the application for some time (probably caused due to session timeout). Then the user has to load the web application again, which doesn't look very professional.
Can you give me a hint, how an automatic logout could be implemented using the mentioned technologies?
[UPDATE]
I tried a lot of possibilities but none of them work properly. My thought is to implement a SessionTimeoutListener which knows when the session gets expired:
#Logged
#WebListener
#Named
public class SessionTimeoutListener implements HttpSessionListener
{
#Inject
private Logger logger;
#Override
public void sessionCreated(HttpSessionEvent event)
{
// not used.
}
#Override
public void sessionDestroyed(HttpSessionEvent event)
{
logger.info("Session destroyed.");
ApplicationContext.setAutoLoggedOut(true);
}
}
This works. But then all the problems appear: I cannot redirect from it because FacesContext is null in there. I cannot fire push events because I get some exceptions like NameNotFoundException or similar (I tried a lot but it seemed that firing events doesn't work out of this too). Then I tried a4j:poll on ApplicationContext.isAutoLoggedOut() but this doesn't work either, because if I execute poll events, the session would never expire. I always come to a dead end. If I could redirect somehow from SessionTimeoutListener, this would be the solution.
[POSSIBLE SOLUTION]
I'm now satisfied with a logout which is executed when I click on any button inside the view after session is expired. This current solution is only rudimentary and not applicable for production yet, but this solution works and I will build on it:
I use the upper SessionTimeoutListener. Moreover I use a PhaseListener which is being invoked after the SessionTimeoutListener, so if the session expires, the SessionTimeoutListener will be invoked and destroys the session but the SessionTimeoutPhaseListener still has the FacesContext. So I can redirect there to logout page:
public class SessionTimeoutPhaseListener implements PhaseListener
{
private static final long serialVersionUID = -8603272654541248512L;
#Override
public void beforePhase(PhaseEvent event)
{
//not used.
}
#Override
public void afterPhase(PhaseEvent event)
{
FacesContext facesContext = event.getFacesContext();
if (ApplicationContext.isAutoLoggedOut())
{
ApplicationContext.setAutoLoggedOut(false);
try
{
facesContext.getExternalContext().redirect("./logout.xhtml");
}
catch (IOException e)
{
}
}
}
#Override
public PhaseId getPhaseId()
{
return PhaseId.RESTORE_VIEW;
}
}
The ApplicationContext is a class with #ApplicationScoped which stores the boolean variables, but this has to be changed, because it would affect every user who currently works with the application. I think about some "ThreadLocal context" to solve that. I still have to distinguish between auto logout and manual logout. The listener is invoked in both cases. Although this solution works at the moment, the redirection in PhaseListener is also tricky, because it would be invoked over and over again by JSF (which causes redirection loop errors in browser), if I wouldn't set "autoLoggedOut" to false.... as I said, only rudimentary, but using the PhaseListener is probably the only suitable solution.
There are two ways that you can do this:
1 Use pull mechanism
Every 5 seconds or so, keep asking server if session is valid or not, if server says yes valid, sleep for specified period of time, if server says invalid, show message to user, and logout.
2 Use push mechanism
Use a4j:push, so in your server you have a TimerTask which runs says every 5 seconds to check if session is valid or not, if it sees session is valid, sleep, if invalid, send message to user and then logout.
Looks like RichFaces4 online demo is not working, though check this link
I am not sure if RichFaces has something like Primefaces Idle Monitor
Another solution is to use Custom HttpSessionListener
If you want to handle the ViewExpiredException, you could add the following setting to your web.xml
<error-page>
<exception-type>javax.faces.application.ViewExpiredException</exception-type>
<location>/login.jsf</location>
</error-page>
Better yet, use a PhaseListener tio check if the User is still available in the Session.
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("User")
If not - navigate to the login page.
FacesContext.getCurrentInstance().getApplication().getNavigationHandler().handleNavigation(FacesContext.getCurrentInstance(), null, "login");
You have to configure the session timeout in your web.xml, the value is in minutes and can't be less than 1:
<session-config>
<!-- The application will have 15 minutes for session timeout -->
<session-timeout>15</session-timeout>
</session-config>
You should add a filter that will check if the session has expired:
<filter>
<filter-name>SessionTimeoutFilter</filter-name>
<filter-class>edu.home.TimeoutFilter</filter-class>
</filter>
The filter will look like this:
public class TimeoutFilter implements Filter {
private String startPage = "index.xhtml";
public void init(FilterConfig filterConfig) throws ServletException {}
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse)) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
if (!isValidSession(httpServletRequest)) {
String timeoutUrl = httpServletRequest.getContextPath() + "/"
+ startPage;
//redirects to the first page
httpServletResponse.sendRedirect(timeoutUrl);
}
filterChain.doFilter(request, response);
}
}
private boolean isValidSession(HttpServletRequest httpServletRequest) {
return (httpServletRequest.getRequestedSessionId() != null) &&
httpServletRequest.isRequestedSessionIdValid();
}
}
Since it is web, you could use some javascript:
var timeout;
var timeoutTime = 294000;
$().ready(function() {
$(".loading").bind("ajaxSend", function() {
timeout = setTimeout("TimedOut()", timeoutTime);
}
});
function TimedOut() {
//here I do an async call to test if a user is timed out
var isTimedOut = yourMethodToTest();
if(isTimedOut) { //do whatever you need to do here;
alert("session timed out");
}
else { //restart the checker
timeout = setTimeout("TimedOut()",timeoutTime);
}
}

How to implement Logout feature using Spring Web Mvc

I am new to Spring Web MVC..
Can I get some example or online link that shows me how to implement logout feature using spring web mvc ?
I don't want to use the in built feature of spring security (i.e. ACEGI)..
Thanks in advance...
The trick with the session invalidation doesn't work. It seems the Spring authentication buffers the session ID somewhere and accept the COOKIE even, if the session was invalidated.
Another solution is to clear the Spring security context manually:
public void manualLogout() {
SecurityContextHolder.getContext().setAuthentication(null);
}
Here is the code, how to log in user manually (if somebody needs):
public void doManualLogin(HttpServletRequest request, String u, String p) {
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(u, p);
token.setDetails(new WebAuthenticationDetails(request));
Authentication auth = authenticationProvider.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(auth);
}
where the authenticationProvider is the bean from you spring configuration which implements
org.springframework.security.authentication.AuthenticationProvider
You only have to invalidate the session and the user is logged out. This is directly supported by the servlet api: HttpSession.invalidate(). You can write one controller that does only call invalidate.
class Logout implements Controller{
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response){
ModelAndView view = //?;
request.getSession().invalidate();
return view;
}
}
#Controller
public class LogoutController {
#RequestMapping(value="/logout",method = RequestMethod.GET)
public String logout(HttpServletRequest request){
HttpSession httpSession = request.getSession();
httpSession.invalidate();
return "redirect:/";
}
}
Please use above code to implement logout filter

Categories