How to implement an automatic logout in a web application? - java

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

Related

Writing Interceptor Filter for user Authentication

Creating a simple filter for trivial web application.
login
register
home
feature
if the user has not logged in using the login name "admin", or tries to access the /home resource or /feature resource, they should be routed back to the login page.
However, it appears I am running into a redirect loop problem. What is the problem with this approach. I feel it is incorrect solution approach for such a requirement.
public class LoginInterceptor extends HandlerInterceptorAdapter {
private static final Logger logger = Logger.getLogger("LoginInterceptor.class");
// to be used checking session management for user.
#Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response,
Object handler) throws Exception {
logger.log(Level.INFO, "[][][][][] +++ prehandle");
if (request.getAttribute("username") != null) {
if (request.getAttribute("username").equals("admin")) {
return true;
} else
response.sendRedirect("/sessionmanagement/login");
return false;
} else if (request.getAttribute("username") == null & !request.getRequestURI().equals("/login")) {
// return false and redirect to login.
response.sendRedirect("/sessionmanagement/login");
return false;
}
// return false and redirect to login.
response.sendRedirect("/sessionmanagement/register");
return false;
}
}
if you are using spring you can use the spring security to do that, your user can have ROLES, so you won't be checking just against the username, https://spring.io/guides/gs/securing-web/ here is a guide that you can go over it, it is a pretty straight forward , so your methods you have a annotation to check what is the authority of the user.
here another thing that can help you : Spring Security with roles and permissions
http://www.journaldev.com/8748/spring-security-role-based-access-authorization-example

Spring Security and Action Required after login

I'm trying to implement an action required screen after user is logged-in in Spring Security? I have a requirement where user has to perform to complete a form (change password, accept Terms Of Use, etc.), then once user completes that action he can use the rest of the app. I'm using Spring OAuth2 with the login screen that uses Spring Security flow.
So far I have tried to use http.formLogin().successHandler() that has custom implementation of SavedRequestAwareAuthenticationSuccessHandler, which detects if user has action required, then redirects user to the page when he can fill out the form, but the problem with that is that if user navigates away from that page, he will be logged in to the app and can use it without by skipping the form. But what I'm trying to do is to block user from establishing the session until after that Action Required form is complete. Once it is complete user should be automatically logged in (ex. if user was req. to only agree with Terms of Use, he should be logged in without entering a password second time)
Here is the code that I have so far the custom handler:
public class CustomLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
#Autowired
UserService userService;
public final static String TARGET_URL_SESSION_ATTR_NAME = "target-url";
public CustomLoginSuccessHandler(String defaultTargetUrl) {
setDefaultTargetUrl(defaultTargetUrl);
}
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
HttpSession session = request.getSession();
AuthorityUser authorityUser = (AuthorityUser)authentication.getPrincipal();
String userId = authorityUser.getUserId();
User u = userService.getById(userId);
Boolean changeRequiredDob = u.getChangeRequiredDob();
Boolean changeRequiredPwd = u.getChangeRequiredPwd();
Boolean changeRequiredTou = u.getChangeRequiredTou();
if(changeRequiredDob || changeRequiredPwd || changeRequiredTou){
String targetUrl = determineTargetUrl(request, response);
session.setAttribute(TARGET_URL_SESSION_ATTR_NAME, targetUrl);
getRedirectStrategy().sendRedirect(request, response, "/action-required");
} else {
super.onAuthenticationSuccess(request, response, authentication);
}
}
}
And then once it is successfully complete I'm redirecting user to TARGET_URL_SESSION_ATTR_NAME that was stored to the session.
It would be also helpful to know how to detect and redirect user to the action required screen during the established sessions (if user logged in and later while he is logged in admin sets action required flag on his account).
https://github.com/king-julien/spring-oauth2-customfilter Here is a working sample with Authorization and Resource Server. This Resource Server (vanilla) is a basic stateless application which will not proceed any further until you accept Terms of Service (to accept TOS, Just a do a POST on /tos end point) after authentication.
Create a filter
#Component
public class TosFilter extends OncePerRequestFilter{
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
System.out.println(request.getRequestURI());
// In realworld scenario HelloWorldController.acceptedTOS is a persisted value rather than a static variable
if(!HelloWorldController.acceptedTOS){
//response.sendRedirect("/no-tos");
request.getRequestDispatcher("error-no-tos").forward(request, response);
}
filterChain.doFilter(request,response);
}
}
Register that filter
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
TosFilter rolesFilter;
#Override
public void configure(HttpSecurity httpSecurity) throws Exception{
httpSecurity
.addFilterAfter(rolesFilter, AbstractPreAuthenticatedProcessingFilter.class)
.csrf().disable()
.authorizeRequests().anyRequest().permitAll();
}
}
Annotate your main with #EnableResourceServer.
#SpringBootApplication
#EnableResourceServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The way we solve that is to have the OAuth2 approval page be a single page application.
By default the approval page controller is WhitelabelApprovalEndpoint. We override this by defining out own OauthApproval Controller which overrides "/oauth/confirm_access", so we can add extra stuff to the model. When the approval (jsp) page is loaded, we convert some of the model properties into javascript variables (var token = '${_csrf.token}';), and start an AngularJS application. The approval page can then do whatever it wants (before showing the actual approval form), we just need to build REST endpoints for the different functionalities.
Remember to add #SessionAttributes("authorizationRequest") to the Controller
Instead of AuthenticationSuccessHandler you should use filter:
public class ActionRequirementCheckingFilter extends OncePerRequestFilter {
/* This matcher should not match static resources (js,css etc),
* url`s needed to handle the action and possibly something else,
* depending on your application */
private RequestMatcher matcher;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpSession session = request.getSession();
Boolean actionRequired = false;
/* calculate actual value for actionRequired */
if(matcher.matches(request) && actionRequired){
/* save current request info into session for later use if needed */
response.sendRedirect("/action-required");
} else {
filterChain.doFilter(request, response);
}
}
}
This approach fits all your requirments:
User wont be able to navigate away from it
User will be automatically logged-in after action is complete
It will even work for existing sessions
Only drawback is that session will be actualy created before action is completed, but unless you have a real reason not to do that (which i cant even imaging) this is negligible.
Another way of verifying user access rights during a successfully logged in session is via the filter api
https://www.mkyong.com/spring-mvc/how-to-register-a-servlet-filter-in-spring-mvc/
You can then implement the functionality needed in the doFilter() to verify your rules.

How to prevent the user to access the history after logout the application

I am working on a SpringMVC application I need to make a logout page. I make the page but when I click the back button on the browser I can see the user data.
How can I prevent the user to access the history after logout the application. Please tell me a solution without using Spring-security.
I am using the following handler method for logout the application.
#RequestMapping("/logout")
public String logout(HttpServletRequest request)
{
request.getSession().invalidate();
return "index";
}
Thank you
You can add below line at top of your jsp page to not to store history or cache
<%
response.setHeader("Cache-Control","no-cache,no-store,must-revalidate");//HTTP 1.1
response.setHeader("Pragma","no-cache"); //HTTP 1.0
response.setDateHeader ("Expires", 0); //prevents caching at the proxy server
%>
Stop user to go back to previous page history
You can use javascript
window.history.forward();<br>
function noBack(){<br>
window.history.forward();
}
But this is not the best approach because of various numerous reasons.
There are many other solutions
1. Invalidate the session
2. Clear the cache
See this and also this already asked question
if you are using Spring frame works then use interceptor to avoid such behavior of your application.
public class LoginInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
}
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
#Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
`

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

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

Prefered way to redirect http connection to https in Dropwizard

I'd like to redirect incoming http connections to https in Dropwizard, preferably togglable in a config file (e.g. with a YAML file, like other connection attributes).
[I've seen this question, and I'm reasonably certain that it's not a solution]
A solution I've found in several places involves hooking in a Filter that checks the schema, and if it finds "http", calls sendRedirect with a modified URL. This involves hardcoding the behavior to make this always happen though.
If I extend the HttpConnectorFactory, it seems like I could add configuration in the YAML for whether I want the redirection to happen. However, it's unclear to me how complicated it will be to add an attribute without breaking other code.
This seems like a common task; is there a standard, "preferred" way to do this? I would have expected Dropwizard to have elegant built-in support, like Jetty does, but I can't find it.
I don't know that there's a "preferred" way to do this but how about something like this (for Dropwizard 0.7.0):
void addHttpsForward(ServletContextHandler handler) {
handler.addFilter(new FilterHolder(new Filter() {
public void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
StringBuffer uri = ((HttpServletRequest) request).getRequestURL();
if (uri.toString().startsWith("http://")) {
String location = "https://" + uri.substring("http://".length());
((HttpServletResponse) response).sendRedirect(location);
} else {
chain.doFilter(request, response);
}
}
public void destroy() {}
}), "/*", EnumSet.of(DispatcherType.REQUEST));
}
#Override
public void run(ExampleConfiguration configuration, Environment environment) throws Exception {
//...
if (configuration.forwardHttps()) {
addHttpsForward(environment.getApplicationContext());
}
//...
}
You'd just need to add a boolean to your application configuration and then you could easily switch https forwarding with your YAML.
You can use the redirect bundle at
https://github.com/dropwizard-bundles/dropwizard-redirect-bundle
#Override
public void initialize(final Bootstrap<PrmCatchConfiguration> bootstrap) {
bootstrap.addBundle(new RedirectBundle(new HttpsRedirect(false)));
Above HttpsRedirect is constructed with false for allowPrivateIps which makes testing things locally possible. HttpsRedirect docs has plenty of information on this.

Categories