I want to override moqui session timeout on transition execution.
moqui default session configured in the web.xml like this
<session-config>
<!-- session timeout in minutes; note that this may be overridden with webapp.session-config.#timeout in the Moqui Conf XML file -->
<session-timeout>600</session-timeout>
<cookie-config><http-only>true</http-only><comment>__SAME_SITE_LAX__</comment></cookie-config>
<tracking-mode>COOKIE</tracking-mode>
</session-config>
But I want to change the session-timeout value when the transition is executed on the page,
when method sessionCreated in class MoquiSessionListener is executed, the value of session timeout set in this method
#Override void sessionCreated(HttpSessionEvent event) {
HttpSession session = event.session
ExecutionContextFactoryImpl ecfi = (ExecutionContextFactoryImpl) Moqui.getExecutionContextFactory()
String moquiWebappName = session.servletContext.getInitParameter("moqui-name")
ExecutionContextFactoryImpl.WebappInfo wi = ecfi?.getWebappInfo(moquiWebappName)
if (wi?.sessionTimeoutSeconds != null) session.setMaxInactiveInterval(wi.sessionTimeoutSeconds)
}
also, this class(MoquiSessionListener) has been added to MoquiDefaultConf.xml as listener
<!-- Moqui Session Listener (necessary to handle expired sessions, etc) -->
<listener class="org.moqui.impl.webapp.MoquiSessionListener"/>
is there a way to change the value of the session timeout when moqui framework is running and transition or service-call is executed?
Related
I have an eclipse plugin which uses jetty server with ProxyServlet. Basically, the implementation is the following:
ServletHolder proxyServletHolder = new ServletHolder(new SubClassOfProxyServlet());
proxyServletHolder.setAsyncSupported(true);
ServletHandler proxyServletHandler = new ServletHandler();
proxyServletHandler.addServletWithMapping(proxyServletHolder, "/mapping/url");
After that I add proxy handler to the handler list and set this list to the server:
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] {
. // Other Handlers
.
proxyServletHandler,
.
.
.
new DefaultHandler()
});
server.setHandler(handlers);
Everything worked like a charm against jetty 8 but after migration to jetty 9 I get the following error:
Caused by: java.lang.IllegalStateException: No server executor for proxy
at org.eclipse.jetty.proxy.ProxyServlet.createHttpClient(ProxyServlet.java:279)
at org.eclipse.jetty.proxy.ProxyServlet.init(ProxyServlet.java:123)
... 24 more
Has the mechanism of working with ProxyServer changed? Am I missing something?
You need to update your SubClassOfProxyServlet class to include the various configurations that are now being passed from the Server to the Proxy which are then in turn used by the internal HttpClient
The particular error means you are not passing along the Executor properly.
You have 2 choices for the Executor specific piece (there might be more things for you to configure after this is addressed)
Set the init-parameter maxThreads to a valid integer value.
or Create an Executor, and set it in the servlet context attributes at ServletContext.setAttribute("org.eclipse.jetty.server.Executor", myExecutor) on application deployment / startup. - You could probably do this as well in your SubClassOfProxyServlet.init(ServletConfig config) method.
I was able to get it working via the maxThreads method mentioned above, setting it on creation. Applying this to the original example would result in this:
ServletHolder proxyServletHolder = new ServletHolder(new SubClassOfProxyServlet());
proxyServletHolder.setAsyncSupported(true);
proxyServletHolder.setInitParameter("maxThreads", "2");
ServletHandler proxyServletHandler = new ServletHandler();
proxyServletHandler.addServletWithMapping(proxyServletHolder, "/mapping/url");
Here is an example of how you can add a servlet to a list of handlers:
private void addWebApp(String contextPath, String resourceBase, Server server) {
WebAppContext webAppContext = new WebAppContext();
// webAppContext.setDescriptor(webapp + "/WEB-INF/web.xml");
webAppContext.setResourceBase(resourceBase);
webAppContext.setContextPath(contextPath);
webAppContext.setParentLoaderPriority(true);
webAppContext.setWelcomeFiles(new String[] {"index.html"});
webAppContext.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false");
webAppContext.setInitParameter("org.eclipse.jetty.servlet.Default.useFileMappedBuffer", "false");
final ServletHolder servletHolder =new ServletHolder();
servletHolder.setAsyncSupported(ContentBasedProxyServlet.class);
servletHolder.setAsyncSupported(true);
webAppContext.addServlet(servletHolder, "/*");
HandlerList handlers = (HandlerList) server.getHandler();
handlers.addHandler(webAppContext);
}
In addition you can put maxThreads to web.xml as well:
<servlet>
<servlet-name>proxy</servlet-name>
<servlet-class>example.MyProxyServlet</servlet-class>
<init-param>
<param-name>maxThreads</param-name>
<param-value>5</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
I'm building a grails app that has the spring-security-core 1.2.7.3 plugin as well as spring-security-ui 0.2 plugin, and would like to obtain a list of ALL the users that are currently logged in (ie have a currently active session). Users can login either through a login controller (daoAuthenticationProvider) or automatically through a rememberMe cookie.
I have implemented the code below, using ConcurrentSessionControlStrategy to create a sessionRegistry:
in /conf/spring/resources.groovy:
import org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy
import org.springframework.security.web.session.ConcurrentSessionFilter
import org.springframework.security.core.session.SessionRegistryImpl
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy
beans = {
userDetailsService(lablore.MyUserDetailsService)
sessionRegistry(SessionRegistryImpl)
sessionAuthenticationStrategy(ConcurrentSessionControlStrategy, sessionRegistry) {
maximumSessions = -1
}
concurrentSessionFilter(ConcurrentSessionFilter){
sessionRegistry = sessionRegistry
expiredUrl = '/login/concurrentSession'
}
}
In /plugins/spring-security-core/conf/DefaultSecurityConfig.groovy
useHttpSessionEventPublisher = true
In the controller:
controller{
def sessionRegistry
action(){
def loggedInUsers = sessionRegistry.getAllPrincipals()
}
}
It works well for
-users that login through the login page
-users that logout through a 'logout' link
-users who's session expires
HOWEVER, it does NOT work for users that authenticate automatically with a rememberMe cookie. It doesn't see that they have a newly created session.
If I understand correctly, this is because the RememberMeAuthenticationFilter is 'further up' in the filter chain compared to the ConcurrentSessionFilter, which is the one running the sessionRegistry? Or, I messed something up with my configurations....
Any help on how to get this to work would be great !
Thanks!!
The ConcurrentSessionControlStrategy is deprecated,
Use the ConcurrentSessionControlAuthenticationStrategy instead
Alternatively,
You can implement the HttpSessionListener interface which has the sessionCreated(HttpSessionEvent event) and sessionDestroyed(HttpSessionEvent event) methods, But you have to add the class you used
Implementations of this interface are notified of changes to the list of active sessions in a web application. To receive notification events, the implementation class must be configured in the deployment descriptor for the web application.
You can either add the implementation class to your deployment descriptor like so(i.e you web.xml file)
<listener>
<listener-class>com.hazelcast.web.SessionListener</listener-class>
</listener>
or by using the WebXmlConfig plugin in grails
Your implementation class could look like below, see Online users with Spring Security also
class WebSessionListener implements HttpSessionListener{
sessionCreated(HttpSessionEvent se){
//Checked if user has logged in Here and keep record
HttpSession webSession = se.getSession();
}
sessionDestroyed(HttpSessionEvent se){
//Checked if user has logged in Here and keep record
HttpSession webSession = se.getSession();
}
}
I'm developing a .NET MVC 4 application that needs to show a message for "Session expiration" to the user if he performs some action after the session timeout.
I'm using forms authentication with a very long timeout and a sesion with a shorter timeout, so I can know if the session expired or it's a new visitor. This is my Web.config section for that:
<authentication mode="Forms">
<forms loginUrl="~/Login" timeout="10080" defaultUrl="~/Index" />
</authentication>
<sessionState mode="InProc" timeout="20" />
As I said, that's only to differentiate a new visitor from a session timeout, and not necessary for the application itself. It can be changed if the same result can achieved with something else.
With that in the Web.config, I can do the following in the Global.asax:
protected void Session_Start(object sender, EventArgs e)
{
//if it's a new session AND the user is authenticated, then it's a session timeout
if (Request.IsAuthenticated)
{
if (Request.RequestContext.HttpContext.Request.IsAjaxRequest())
{
//...
//Ajax handling code
//...
//The redirect is so I can return a JsonResult with a standard object with the message
Response.Redirect("/GetJson?some_parameters, true);
}
else
{
Response.Redirect("/SessionExpired", true);
}
}
}
All that works great for the private sections of the website that require login. The problem is that if a user is logged, wait for the session timeout, and then try to open a "public" page, one that doesn't require login, he still gets the "Session expired" message. That's what I want to avoid. The "Session expire" message should only be showed to the user if he's trying to access a page that requires log in.
I've tried to set a flag in Session_Start and redirect in a later event of Global.asax, but it didn't work because I had some variable accessibility issues.
I thought of trying to check if the current action has the [AllowAnonymous] attribute so I can skip the redirect, but I couldn't figure out how.
Some answers that I read gave me the impression that my "Session expire" logic should be in a custom attribute but I don't know if that's the right approach.
My question would be, what's the best way of accomplish the "Session expire" for only "private" pages in .Net MVC 4?
As you say, the ideal is to use an attribute. To be more exact this attribute is named "Action Filter".
You can create an ActionFilter implementing an interface, or inheriting an existing one.
In your case I think the easiest way to get your result is to inherit AuthorizeAttribute, and override its OnAuthorization method.
In this method you can access the HttpContext as a property of the filterContext parameter: filterContext.HttpContext. You can check the session, authorization, and check if it's an ajax request using this property. If you need to redirect, you must do it by setting a property of filterContext parameter:
filterContext.Result = new RedirectResult("...url to redirect to...");
You can use any overload of RedirectResult or RedirectToRouteResult constructor.
If you leave the Result property untouched, nothing special will happen.
The you can use this custom attribute instead of the original authorization attribute.
I have a web application running on Tomcat 7.0.14 and I'm using LDAP for user authentication. The problem is that when a user logs in after an inactive period the following warning comes out. The inactive period doesn't have to be long, as only few minutes is enough. However, the user is able to log in despite of the warning. From the users' point of view the application behaves normally, but Tomcat log reveals the warning below.
Jun 6, 2012 9:41:19 AM org.apache.catalina.realm.JNDIRealm authenticate
WARNING: Exception performing authentication
javax.naming.CommunicationException [Root exception is java.io.IOException: connection closed]; remaining name ''
at com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:157)
at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2685)
at com.sun.jndi.ldap.LdapCtx.ensureOpen(LdapCtx.java:2593)
at com.sun.jndi.ldap.LdapCtx.ensureOpen(LdapCtx.java:2567)
at com.sun.jndi.ldap.LdapCtx.doSearch(LdapCtx.java:1932)
at com.sun.jndi.ldap.LdapCtx.doSearchOnce(LdapCtx.java:1924)
at com.sun.jndi.ldap.LdapCtx.c_getAttributes(LdapCtx.java:1317)
at com.sun.jndi.toolkit.ctx.ComponentDirContext.p_getAttributes(ComponentDirContext.java:231)
at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.getAttributes(PartialCompositeDirContext.java:139)
at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.getAttributes(PartialCompositeDirContext.java:127)
at javax.naming.directory.InitialDirContext.getAttributes(InitialDirContext.java:140)
at org.apache.catalina.realm.JNDIRealm.bindAsUser(JNDIRealm.java:1621)
at org.apache.catalina.realm.JNDIRealm.checkCredentials(JNDIRealm.java:1480)
at org.apache.catalina.realm.JNDIRealm.authenticate(JNDIRealm.java:1131)
at org.apache.catalina.realm.JNDIRealm.authenticate(JNDIRealm.java:1016)
at org.apache.catalina.authenticator.FormAuthenticator.authenticate(FormAuthenticator.java:282)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:440)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:563)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:399)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:317)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:204)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:311)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:636)
Caused by: java.io.IOException: connection closed
at com.sun.jndi.ldap.LdapClient.ensureOpen(LdapClient.java:1576)
at com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:155)
... 27 more
The LDAP configuration is in the application's context.xml file:
<Realm className="org.apache.catalina.realm.JNDIRealm"
connectionURL="ldaps://ldap-company.com"
userPattern="uid={0},dc=company,dc=com"
roleBase="ou=groups,o=company"
roleName="uid"
roleSearch="uniqueMember={0}"
roleSubtree="true" />
I've found posts about this problem from several forums, but no one seems to have figured out the solution.
I was able to figure out the reason for the warning and also a way to get rid of it.
The reason for the warning was that the LDAP server is closing all the connections that have been idle for more than 5 minutes. The LDAP server admin told me that it's recommended to close the connection immediately after each login request, because the number of available handles is limited. Tomcat's JNDIRealm, however, doesn't offer a way to configure this, so I resolved the problem by extending the JNDIRealm class and overriding the authenticate(..) method. All that needs to be done is to close the connection to the LDAP server after each authentication request and the warnings are gone.
Note that the package needs to be the same as JNDIRealm class, because otherwise it's not possible to access the context variable.
package org.apache.catalina.realm;
import java.security.Principal;
public class CustomJNDIRealm extends JNDIRealm {
#Override
public Principal authenticate(String username, String credentials) {
Principal principal = super.authenticate(username, credentials);
if (context != null) {
close(context);
}
return principal;
}
}
Generated jar needs to be put under Tomcat's lib folder and change the className in the application's context.xml to org.apache.catalina.realm.CustomJNDIRealm. Then just restart Tomcat and that's it.
<Realm className="org.apache.catalina.realm.CustomJNDIRealm"
connectionURL="ldaps://ldap-company.com"
userPattern="uid={0},dc=company,dc=com"
roleBase="ou=groups,o=company"
roleName="uid"
roleSearch="uniqueMember={0}"
roleSubtree="true" />
I am answering, because this is a current research topic for me, as we currently extend the JNDIRealm for our needs.
The realm will retry after the warning, so the suggested patch is just beautifying the logfile. Later versions of tomcat (7.0.45 iirc) will beautify the logmessage to make clear, that there is a retry attempt done.
If you want to have the realm doing authentication with a fresh connection every time, it should be sufficient to use this class (I have not tested this implementation but will if our realm is done):
package org.apache.catalina.realm;
import java.security.Principal;
public class CustomJNDIRealm extends JNDIRealm {
#Override
public Principal authenticate(String username, String credentials) {
Principal principal = null;
DirContext context = null;
try {
context = open();
principal = super.authenticate(context, username, credentials);
}
catch(Throwable t) {
// handle errors
principal = null;
}
finally {
close(context); // JNDIRealm close() takes care of null context
}
return principal;
}
#Override
protected DirContext open() throws NamingException {
// do no longer use the instance variable for context caching
DirContext context = null;
try {
// Ensure that we have a directory context available
context = new InitialDirContext(getDirectoryContextEnvironment());
} catch (Exception e) {
connectionAttempt = 1;
// log the first exception.
containerLog.warn(sm.getString("jndiRealm.exception"), e);
// Try connecting to the alternate url.
context = new InitialDirContext(getDirectoryContextEnvironment());
} finally {
// reset it in case the connection times out.
// the primary may come back.
connectionAttempt = 0;
}
return (context);
}
}
The LDAP server is disconnecting idle connections that have been idle, that is, no requests transmitted, after a certain period of time.
basically adding a keepaliveTimeout to override connection timeout which was around 5 minutes resolved the issue in my scenario i.e. keepaliveTimeout ="-1" attribute to connector element in server.xml file
keepAliveTimeout="-1"
here is my case.
when first time user land to site, they will pointed to login page. (lest say http://ex.com/) and when they successfully login, they'll see the other page with the same url (http://ex.com/
but, when they open the site on other tab (http://ex.com) they will pointed back to login page.
how to implement this case in my site with spring security ?
its easy to do when deal with conventional servlet. i just need to have 2 method (doGet for showing login page, and doPost for authenticating user and if its valid it will call another view).
here is my configuration :
<security:http auto-config="true">
<security:intercept-url pattern="/login*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<security:form-login login-page="/login"
login-processing-url="/loginProcess"
default-target-url="/login"
authentication-failure-url="/login?login_error=1" />
<security:logout logout-url="/logout" logout-success-url="/logoutSuccess" />
</security:http>
** Edited (remove unrelated answer)
It appears you need to add a concurrent session management using Spring Security. See the following link: http://static.springsource.org/spring-security/site/docs/3.1.x/reference/session-mgmt.html
You can inject the SessionRegistry and see if the principal is already logged-in. If he is, call the expireNow()
Or you can implement a filter on or before SessionManagementFilter in the FilterChainProxy:
The SessionManagementFilter checks the contents of the SecurityContextRepository against the current contents of the SecurityContextHolder to determine whether a user has been authenticated during the current request, typically by a non-interactive authentication mechanism, such as pre-authentication or remember-me [19]. If the repository contains a security context, the filter does nothing. If it doesn't, and the thread-local SecurityContext contains a (non-anonymous) Authentication object, the filter assumes they have been authenticated by a previous filter in the stack. It will then invoke the configured SessionAuthenticationStrategy.
- http://static.springsource.org/spring-security/site/docs/3.1.x/reference/session-mgmt.html
I think your configuration has a problem
<security:http auto-config="true">
<security:intercept-url pattern="/login*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<security:form-login login-page="/login"
login-processing-url="/loginProcess"
default-target-url="<home-page-url. ex: /home>"
authentication-failure-url="/login?login_error=1" />
<security:logout logout-url="/logout" logout-success-url="/logoutSuccess" />
</security:http>
The default-target-url should point to the default page to which the application has to redirect after a successful login.
EDITED
After going through the required posted again, I think the approach is to make the controller handling /login request to handle both cases
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;
#Controller
public class AppsController {
#RequestMapping("/login")
public ModelAndView view(HttpServletRequest request,
HttpServletResponse response) {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
User user = authentication != null
&& authentication.getPrincipal() instanceof User ? (User) authentication
.getPrincipal() : null;
return user == null ? getLoginModelAndView() : getHomeModelAndView();
}
private ModelAndView getHomeModelAndView() {
return null;
}
private ModelAndView getLoginModelAndView() {
return null;
}
}
If there is no authenticated user present in the session the controller will return the log-in page, but once the user is logged-in then it will return a different page.
Spring security will cache the logged used to the user session and it can be retrieved using the SecurityContextHolder.