I have configured the Shiro global timeout with Spring in my webapp, so if my web page(client) doesn't have any request during the past 30 minutes(just instance), the session of the client will timeout and page redirect to a login page. This is already ok. My problem is as following:
The web page has a ajax request in the background, which will request the server at set intervals. And every time it requests will clear the timeout counter of the session in Shiro, so the client session will never timeout!
Is it possible to configure the Shiro to make some specific urls will not clear or refresh the session timeout???
It's really hard to title the problem, also for searching. But I think there always are some people have the same requirement! Anyone has any idea, please tell me. Thank you very much~
My part configuration is as follows,
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="filters">
<map>
<entry key="ssl" value-ref="sslFilter"/>
<entry key="login" value-ref="userLoginFilter"/>
<entry key="nosessi" value-ref="unSessionFilter"/>
</map>
</property>
<property name="securityManager" ref="securityManager"/>
<property name="filterChainDefinitions">
<value>
/alarms/current-alarm-states = nosessi
/js/** = anon
/css/** = anon
/images/** = anon
/login = anon,ssl
/login/** = anon,ssl
/** = login,ssl
</value>
</property>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realms">
<list>
<ref bean="userRealm"/>
</list>
</property>
<property name="sessionManager" ref="sessionManager"/>
</bean>
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="sessionListeners">
<list>
<ref bean="sessionListener"/>
<ref bean="tsSessionListener"/>
</list>
</property>
<!-- 3 minutes: 180000 -->
<property name="globalSessionTimeout" value="180000"/>
<property name="sessionIdCookie.name" value="MY_SESSIONID"/>
</bean>
You can't configure this in Shiro. The timeout has nothing to do with shiro, it is a servlet container configuration.
The user session is one single object in the server and as long as your requests send the session cookie (JSESSIONID most of the time) and the servlet container can find the session object (and thus it hasn't timed out yet), the timeout will be reset.
You will have to create some Filter yourself to keep track of a timeout. For each request you can create a session timer using a session listener (http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpSessionListener.html). You could use a Timer object for this for example. Each time a request comes in, the filter gets the session timer and resets it, except on those url's you do not want that to happen.
You could also look in the source code of Vaadin, as they have a similar setup and have somehow dealt with it: https://vaadin.com/book/-/page/application.lifecycle.html#application.lifecycle.ui-expiration
I debug the shiro, track the behavior of session. And I found that the last accessed time was updated in class ShiroFilterFactoryBean, after that, Most Filters will check the request time with lastAccessTime of session except anon filter.
Based on this, I figured out a solution. Extend the ShiroFilterFactoryBean and override the method of updating session access time which will not update for the special url. Besides, the special has to use anon filter.
public class MyShiroFilterFactoryBean extends ShiroFilterFactoryBean {
#Override
protected AbstractShiroFilter createInstance() throws Exception {
SecurityManager securityManager = this.getSecurityManager();
String manager1;
if(securityManager == null) {
manager1 = "SecurityManager property must be set.";
throw new BeanInitializationException(manager1);
} else if(!(securityManager instanceof WebSecurityManager)) {
manager1 = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(manager1);
} else {
FilterChainManager manager = this.createFilterChainManager();
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
return new NmsShiroFilterFactoryBean.SpringShiroFilter((WebSecurityManager)securityManager, chainResolver);
}
}
#Override
public Class getObjectType() {
return NmsShiroFilterFactoryBean.SpringShiroFilter.class;
}
private static final class SpringShiroFilter extends AbstractShiroFilter {
protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
if(webSecurityManager == null) {
throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
} else {
this.setSecurityManager(webSecurityManager);
if(resolver != null) {
this.setFilterChainResolver(resolver);
}
}
}
#Override
protected void updateSessionLastAccessTime(ServletRequest request, ServletResponse response) {
if(request instanceof HttpServletRequest) {
String requestURI = ((HttpServletRequest) request).getRequestURI();
if(requestURI.equals("/alarms/current-alarm-states")) { // no update the last access time of session
return;
}
}
super.updateSessionLastAccessTime(request, response);
}
}
}
Related
I am trying to use Spring's ReloadableResourceBundleMessageSource for LocalValidatorFactoryBean so that when I update an error message it should reflect without requiring the server to be restarted. I am using Spring 4.1.4, hibernate-validator 4.3.2.Final.
Below are the code details -
context.xml -
<mvc:annotation-driven validator="validator" />
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>file:../conf/fileapplication</value> <!-- Messages here will override the below properties file-->
<value>/WEB-INF/application</value>
</list>
</property>
<property name="cacheSeconds" value="10"></property> <!-- Will check for refresh every 10 seconds -->
</bean>
<bean name="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource">
<ref bean="messageSource"/>
</property>
</bean>
Model -
import org.hibernate.validator.constraints.NotBlank;
public class InputForm {
#NotBlank ( message = "{required.string.blank}")
String requiredString;
Controller -
#RequestMapping(value = "/check/string", method = RequestMethod.POST)
public String checkString(
#ModelAttribute("formModel") #Valid InputForm inputForm ,
BindingResult result, Model model, HttpServletResponse response,
HttpServletRequest request) {
if (result.hasErrors()) {
model.addAttribute("formModel", inputForm);
return "userInput";
}
// Do some backend validation with String
result.reject("string.not.valid",
"String is Invalid");
model.addAttribute("formModel", inputForm);
return "userInput";
}
application.properties (in /WEB_INF/ folder)
required.string.blank=Please enter the required string.
string.not.valid=Please enter a valid string.
fileapplication.properties (in /conf/ folder. Will override above file)
required.string.blank=You did not enter the required string. #Does not reflect when I change here
string.not.valid=You did not enter a valid string. #Reflects when I change here
Now the problem I am facing is, when I update "string.not.valid" in fileapplication.properties it reflects at runtime and I see the updated message. But when I update "required.string.blank" in fileapplication.properties it does not reflect at runtime.
Note that the overriding part is working fine for both messages upon application start up. But the "reloading" part is not working fine for "required.string.blank".
This is what I figured out based on my research - We need to create our own MessageInterpolator and add it as dependency to the validator instead of message source. Because when we add a messageSource as dependency, it is cached by default by the validator and any message reloads spring does won't take effect in the validator's cached instance of messageSource.
Below are the details:
In context.xml, add the custom MessageInterpolator as dependency to LocalValidatorFactoryBean instead of messageSource:
<mvc:annotation-driven validator="validator" />
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>file:../conf/fileapplication</value> <!-- Messages here will override the below properties file-->
<value>/WEB-INF/application</value>
</list>
</property>
<property name="cacheSeconds" value="10"></property> <!-- Will check for refresh every 10 seconds -->
</bean>
<bean name="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="messageInterpolator">
<ref bean="messageInterpolator"/>
</property>
</bean>
<bean name="messageInterpolator"
class="com.my.org.support.MyCustomResourceBundleMessageInterpolator">
<constructor-arg ref="messageSource" />
</bean>
Create your custom MessageInterpolator by extending Hibernate's org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.
public class MyCustomResourceBundleMessageInterpolator extends
ResourceBundleMessageInterpolator {
public MyCustomResourceBundleMessageInterpolator(MessageSource messageSource)
{
// Passing false for the second argument
// in the super() constructor avoids the messages being cached.
super(new MessageSourceResourceBundleLocator(messageSource), false);
}
}
Model, Controller and properties file can be same as in the question.
< ?xml version="1.0" encoding="UTF-8"?>
< beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
< security:global-method-security secured-annotations="enabled" />
< security:http>
< security:intercept-url pattern="/index*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
< security:intercept-url pattern="/login123" access="ROLE_ADMIN" />
< security:intercept-url pattern="/employee1" access="ROLE_EMPLOYEE"/>
< security:intercept-url pattern="/emppreviewshow" access="ROLE_EMPLOYEE"/>
< security:access-denied-handler error-page="/login"/>
<security:form-login login-page="/login" default-target-url="/index"
authentication-failure-url="/fail2login"
username-parameter="username"
password-parameter="j_password" />
<security:session-management invalid-session-url="/logout" session-fixation-protection="newSession" >
<security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</security:session-management>
<security:logout logout-success-url="/logout" delete-cookies="JSESSIONID" invalidate-session="true"/>
</security:http>
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" >
<constructor-arg name="strength" value="255" />
</bean>
<security:authentication-manager>
<security:authentication-provider>
<security:jdbc-user-service data-source-ref="dataSource"
users-by-username-query=
"select username,password, enabled from USER_MASTER where username=?"
authorities-by-username-query=
"select username,USER_ROLE from USER_ROLE where username =? " />
<security:password-encoder ref="passwordEncoder" />
</security:authentication-provider>
</security:authentication-manager>
when i am clicking the logout and when clicking the back button of browser still it is showing the old pages.I want the same login url to be shown when back button is clicked in browser.
you can check the session is active in your all the methods of the controller classes.ie.,request mapped classes,methods.if the session is active then return the page.otherwise redirect to login page.
Welcome to the world of client vs server! Invalidating a session is an on server operation. Assuming that the session id is passed in a cookie, it means that next request containing that cookie will not be member of the previous session, and so you will activate all the "please login first" machinery.
But under normal conditions, hitting the back button on a browser does not send a new request but just display the last page from the local cache. Thus it is a client only operation.
As an application developper, there is little that you can do. You could try to use javascript to hide back button, catch it, or clean the cache. But if I were you, I won't dare to think about that: you are likely to fall in browser compatibiliy problem, for something that you should not care about. What user reads locally is its own problem. If he/she made a printed copy of a page, you would not take a lighter to burn it when the session is over. The cached pages are the same: a local copy. That's the reason why on explicit disconnection you often see a message asking to close the browser window. Itsi the only way for the user to be sure not to read offline copies if he/she click on the back button.
i cannot use the invalidate-session. i just add the "authentication-success-handler-ref" . and set a session inside there.after login the session is set to true.and after logout the sesison is set to false.
this is the code:
Securuty-context.xml
<bean id="customAuthenticationSuccessHandler" class="org.dewbyte.corebank.utility.CustomAuthenticationSuccessHandler"/>
root-context.xml
<bean id="LogoutSuccessHandler" class="org.dewbyte.corebank.utility.LogoutSuccessHandler" />
CustomAuthenticationSuccessHandler class
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler{
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
request.getSession().setAttribute("loginStatus", "true");
String targetUrl = "/dashboard";
redirectStrategy.sendRedirect(request, response, targetUrl);
}
public RedirectStrategy getRedirectStrategy() {
return redirectStrategy;
}
public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
this.redirectStrategy = redirectStrategy;
}
}
LogoutSuccessHandler class
public class LogoutSuccessHandler implements org.springframework.security.web.authentication.logout.LogoutSuccessHandler{
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
public RedirectStrategy getRedirectStrategy() {
return redirectStrategy;
}
public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
this.redirectStrategy = redirectStrategy;
}
#Override
public void onLogoutSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
request.getSession().setAttribute("loginStatus", "false");
String targetUrl = "/";
redirectStrategy.sendRedirect(request, response, targetUrl);
}
}
check the session is true or false in every methods in the controller classes.
Controller class
if (request.getSession().getAttribute("loginStatus").equals("true"))
{
return home;
}
else
return login;
I am currently using Spring MVC 3.x,
and using the freemarker view resolver.
Recently i have been wondering about the execution time that it takes for a view to translate into html before getting sent back as a response. I would like to do tunings if things are slow in this area, which is why i need some numbers.
In plain freemarker mode, i can actually do the simple System.currentTimeMillis() between these to find out the execution time :
long start = System.currentTimeMillis();
// this could be slow or fast depending on the caching used
Template temp = cfg.getTemplate(ftlName);
...
temp.process(model, myWriter); // depends on the writer
System.out.printf("done in %s ms", System.currentTimeMillis() - start);
But how do i do this when with spring mvc's freemaker view rendering ?
You might consider extending org.springframework.web.servlet.view.freemarker.FreeMarkerView and configuring FreeMarkerViewResolver with your custom logging view implementation.
Logging view implementation could look like this:
public class LoggingFreeMarkerView extends FreeMarkerView {
private static final transient Log log = LogFactory.getLog(LoggingFreeMarkerView.class);
#Override
protected void doRender(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
long start = System.currentTimeMillis();
super.doRender(model, request, response);
log.debug("Freemarker rendered " + request.getRequestURI() + " in " + (System.currentTimeMillis() - start) + " ms");
}
}
And wire the view resolver with new class:
<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver" autowire="no">
<property name="viewClass" value="com.example.LoggingFreeMarkerView" />
<property name="cache" value="false" /> <!-- cache disabled for performance monitoring -->
<property name="prefix" value="" />
<property name="suffix" value=".ftl" />
<property name="contentType" value="text/html;charset=utf-8" />
<property name="exposeRequestAttributes" value="true" />
<property name="requestContextAttribute" value="base" />
</bean>
You are going to calculate just on server side merging template with data,Main problem is when freemarker executing on page ,As you know freemarker built on top of jsp page so you should bring code to jsp side to calculate execution time,
As my experience according to data size load time in freemarker is different.
if else condition also is too slow compare to jstl!
I can recommend thymeleaf for spring that allowing templates to be working prototypes on not xml style .
I am learning the spring mvc and when i try to use the ConversionService ,i encounterrd 500
#RequestMapping("/handle81")
public String handle81(#RequestParam("user")User user,ModelMap modelMap) {
System.out.println(user);
modelMap.put("user", user);
return "/user/success";
}
this is the handler method ,i've put the #RequestMapping("/user") at the class
and the converter
public class StringToUserConverter implements Converter<String, User> {
public User convert(String source) {
System.out.println(source);
User user=new User();
String[] item=source.split(":");
user.setUserName(item[0]);
user.setPassword(item[1]);
user.setName(item[2]);
return user;
}
}
<mvc:annotation-driven conversion-service="conversionService" />
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.baobaotao.domain.StringToUserConverter" />
</list>
</property>
</bean>
so when i browse
http://localhost:8080/spring-mvc/user/handle81.html?user=asdf:asdf:fdas
it gets 500 and prints nothing at the console(i use maven-jetty to do the test)
thx for helping~
I think your Request URL may not be not matching. You specify "/handle81" in the annotation, but are requesting "/handle81.html".
It's hard to tell, without further information, whether the problem is matching & dispatching the request to the handler; or in the conversion.
Try another handler with the parameter of type String, and see whether you can call that successfully. At least you'll then know where the problem is.
And what is the exception stack-trace? Why didn't you post it? That's your most important clue & you should always post the ex message & top few lines/ where it was thrown, when you ask a question. It should be in either the application or Tomcat/ other server logs.
Hi I used Internal resource view resolver and my structure of jsp are like
jsp/adm, jsp/icon, jsp/iload, like
and my annotation based controller will return string value based on condition
my problem is jsp uder sub-folder is not resoled but it is under jsp folder is working
could any one please help me in this
he is my code:`
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
my controller code is
#RequestMapping("/icrl")
public String search() throws Exception
{
setMenuName(CommonConstants.ICRL);
return "pgiCRL";
}
#RequestMapping("/searchCodes")
public String searchCodes() throws Exception
{
String key=getSearchKey();
String query=getQuery();
Map<String, Object> searchKeys = new HashMap<String, Object>();
ArrayList<String> list=new ArrayList<String>();
if(query!=null||!query.isEmpty()){
searchKeys.put(CommonConstants.DIAGICD9, getDiaICD9());
searchKeys.put(CommonConstants.DIAGICD10, getDiaICD10());
searchKeys.put(CommonConstants.DIAGNOSIS, getDiagnosis());
searchKeys.put(CommonConstants.PROCEDURE, getProcedure());
searchKeys.put(CommonConstants.SURGICAL, getSurgical());
searchKeys.put(CommonConstants.SURGICAL9, getSurICD9());
searchKeys.put(CommonConstants.SURGICAL10, getSurICD10());
searchKeys.put(CommonConstants.REVENUE, getRevenue());
list= (ArrayList<String>) iCRLService.getSearchCodeList(query,searchKeys);
}
setSuggestions(list);
return CommonConstants.SUCCESS;
}
my view is depending on condition it may be success page and failure page so i need to fix the return value in controller because that is dynamic.
Thanks in advance
You need to define the subfolder name in the returning string value.
For Example, if your page "pgiCRL" is in admin subfolder then return "admin/pgiCRL".