How To Handle session using Interceptor in Spring 3 - spring

I Want to redirect to login whenever the session goes invalid.My servelt.xml is as follows.
<mvc:interceptors>
<bean class="com.package.login.LoginIntercepter" />
</mvc:interceptors>
Interceptor :
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
HashMap details = (HashMap)session.getAttribute("details");
System.out.println("Pre-handle ::"+request.getRequestURI());
if(details!=null){
/*For Checking the Session
* -----Start-----*/
return true;
}else{
response.sendRedirect("Login");//Here Login is action Name which is Mapped in Login Controller
return false;
}
}
Login Controller :
#RequestMapping(value="/Login", method=RequestMethod.GET)
public String loginMethodWithoutaction(HttpServletRequest request,HttpServletResponse response)
{
String page="login";
HttpSession session = request.getSession();
HashMap details = (HashMap)session.getAttribute("details");
if(details!=null)
page = "redirect:/Home";
return page;
}
If Session is invalid then it has to redirect "login" page. otherwise it has to go Home Controller.But its not working.Whenever application get started, the message get printed multiple times and at the end it gives stack overflow.

Message is printing multiple times because when you redirect request is intercepted again and your session is still null because controller method is not executed yet.
You have to create a method that will set details attribute on successful login in your controller.
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession(false);
String path = request.getRequestURI().substring(request.getContextPath().length());
if(path.equals("/Login")){
return true;
}
else if(session == null || session.getAttribute("details") == null) {
request.setAttribute("emassage", "login failed");
throw new LoginFailException("login failed");
}else{
return true;
}
}
LoginFailExceptionClass :
public class LoginFailException extends Exception{
private static final long serialVersionUID = 1L;
public LoginFailException(String message) {
super(message);
}
}
In Controller handle the exception and redirect to login fail page :
#ExceptionHandler(LoginFailException.class)
public String redirectToLogin(){
return "redirect:/login";
}

I think it is your Spring configuration which is not good. Try something like that :
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<ref bean="loginIntercepter"/>
</property>
You have to declare your loginItercepter in the spring file

Related

How to know that a session is expired?

I set values to the session object in the method of a controller after success of login :
#RequestMapping(value = "/", method = RequestMethod.POST)
public ModelAndView processLogin(Model model, HttpServletRequest request, HttpSession session, #RequestParam String login, #RequestParam String pwd) {
if ( utilisateurDao.verifierLoginUser(login) ) {
if ( utilisateurDao.verifierUser(login, pwd) ) {
HashMap<String, String> criteres = new HashMap<String, String>();
criteres.put("user_login", login);
criteres.put("user_passwd", pwd);
List<Utilisateur> users = utilisateurDao.lireParCritere(criteres);
session.setAttribute("user_code", ((Utilisateur)users.get(0)).getUser_code());
session.setAttribute("menu", menuDao.afficherMenuParUtilisateur((Integer)session.getAttribute("user_code"), env, request, session));
criteres.clear();
users.clear();
criteres.put("user_code", String.valueOf(session.getAttribute("user_code")));
users = utilisateurDao.lireParCritere(criteres);
session.setAttribute("user_names", ((Utilisateur)users.get(0)).getNoms());
session.setAttribute("logout_menu", env.getProperty("menu.logout"));
return new ModelAndView("redirect:/accueil");
} else {
ModelAndView modelViewLogin = new ModelAndView("redirect:/");
modelViewLogin.addObject("e", "p").addObject("l", login);
return modelViewLogin;
}
} else {
ModelAndView modelViewLogin = new ModelAndView("redirect:/");
modelViewLogin.addObject("e", "l");
return modelViewLogin;
}
}
Then I opened the app inactive for some minutes. After that I went to the "accueil" path. Then the menu was not shown anymore ! The menu was got from session. So how to know that the session is expired and where is the convenient place to test it ?
By default in spring security session is stored in SessionRegistry.
By using SecurityContext you can get this info in your controller code.
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
If you want to be notified when session has expired or person logged out you can always register listener on SessionDestroyedEvent- documentation.
example:
#Component
public class LogoutListener implements ApplicationListener<SessionDestroyedEvent> {
#Override
public void onApplicationEvent(SessionDestroyedEvent event) {
//do your stuff here
}
}
Its also worth to refer to spring docs for that subject.
You can make a Interceptor,
#Component
public class RequestInterceptor extends HandlerInterceptorAdapter
In this interceptor you can control the HttpServletRequest
and check if obj exists into them and then you can throw to a new SessionExpiredException and catch with #ExceptionMapper (https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc)
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
if (request.getSession().getAttribute("user")==null) {
throw new SessionExpiredException();
}
return true;
}
I check like below. I think it might be help.
public boolean isUserLoggedIn(HttpServletRequest request) throws IOException {
SecurityContext securityContext = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
if(securityContext != null) {
Authentication authentication = securityContext.getAuthentication();
if(null != authentication && authentication.isAuthenticated() != true)
return false;
else
return true;
} else {
return false;
}
}

login intercepter do not work in spring

all. i was using spring4 in my project. and add and interceptor extends HandlerInterceptorAdapter, then overwrite prehandle method. but i found it does not work when i was doing spring mock test.
i have configure it in springmvc-servlet.xml , like this:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.suerpay.common.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
and here is code of LoginInteceptor:
public class LoginInterceptor extends HandlerInterceptorAdapter {
#Autowired
LoginServiceRedis loginServiceRedis;
#Autowired
UserServiceDB userServiceDB;
Logger logger = LoggerFactory.getLogger(getClass());
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
logger.info("start login interceptor");
if (isLoginRequired(handler)) {
String ticket = request.getHeader(GlobalConstants.TICKET_HEADER);
if (StringUtils.isEmpty(ticket)) {
throw new UnAuthorizedException(ResultCodeConstants.USER_NOT_LOGIN);
}
String userName = loginServiceRedis.getUserNameByTicket(ticket);
Long userId = userServiceDB.getUserIdByName(userName);
if (null == userId) {
throw new UnAuthorizedException(ResultCodeConstants.USER_NOT_LOGIN);
}
ThreadContextHolder.setCurrentUserId(userId);
}
logger.info("finish login interceptor");
return true;
}
private boolean isLoginRequired(Object handler) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
LoginRequired loginRequired = method.getAnnotation(LoginRequired.class);
if (null != loginRequired) {
return true;
}
return false;
}
}
i think i have do everything , but just can not get into breakpoint.
who can tell me why?:(

Spring security - Restricting Authenticated User redirection to Login

After login, when login url is accessed with out logging out, login page is shown, but I do not want the login Page, instead remain on the same page even when login url is accessed from address bar.
Following is my security configuration:
<form-login login-page="/loginform.do" authentication-failure-url = "/loginform.do?error=1" default-target-url="/dashBoard.do" always-use-default- target="false" />
One solution I come across is to redirect page, if the role is not 'ROLE_ANONYMOUS'
<sec:authorize ifNotGranted="ROLE_ANONYMOUS">
<% response.sendRedirect("/mainpage.jsp"); %>
</sec:authorize>
But can a similar configuration be done in security configuration file ?
I solved this with an HandlerInterceptor because I dont know a build in solution.
import org.springframework.web.util.UrlPathHelper;
...
public class PreventLoginPageForLoggedInUserInterceptor extends HandlerInterceptorAdapter {
private UrlPathHelper urlPathHelper = new UrlPathHelper();
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler)
throws Exception {
if (urlPathHelper.getLookupPathForRequest(request).startsWith("/login"))
&& isAuthenticated()) {
sendRedirect(request, response);
return false;
} else {
return true;
}
}
private void sendRedirect(HttpServletRequest request,
HttpServletResponse response) {
response.setStatus(HttpStatus.TEMPORARY_REDIRECT.value());
response.setHeader("Location", response.encodeRedirectURL(request.getContextPath() + "/"));
}
private boolean isAuthenticated() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return (authentication != null)
&& (!authentication instanceof AnonymousAuthenticationToken)
&& authentication.isAuthenticated()
}
}

Getting value of RequestParam within an Interceptor

I hope you can help me. I have a Spring Interceptor to authorize users based on the URL configured in #RequestMapping of controller methods and the arguments (parameters) passed to the controller. All these request parameters are configured using the #RequestParam annotation. I need to retrieve the values passed from the #RequestParam within the Interceptor so that I can use those parameters to validate if the url has been accessed by the correct user and if the user is allowed to pass in the documentId. Please let me know if this is possible. When I do request.getParameter("documentId"), I dont get anything. I have some code as below
(Controller Method)
#RequestMapping(value = "/viewDocument.html")
public ModelAndView viewDocument(#RequestParam("documentId");
Intercept class
#Override
public boolean preHandle(final HttpServletRequest req, final HttpServletResponse resp, final Object handler) throws IOException {
if (handler instanceof HandlerMethod) {
final HandlerMethod handlerMethod = (HandlerMethod) handler;
final RequestMapping requstMapping = handlerMethod.getMethodAnnotation(RequestMapping.class);
if (requstMapping != null) {
final AuthorizeRequest authorizeRequestAnnotation = handlerMethod.getMethodAnnotation(AuthorizeRequest.class);
if (authorizeRequestAnnotation != null) {
try {
checkAccess(req, requstMapping, handlerMethod);
} catch (final SecurityException e) {
resp.sendError(HttpServletResponse.SC_FORBIDDEN, "You are not allowed to perform this function");
// return false;
} catch (final Exception e) {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
// return false;
}
}
}
}
return true;
}
private void checkAccess(final HttpServletRequest req, final RequestMapping requestMapping, final HandlerMethod handlerMethod) throws SecurityException {
final Map<String, Object> arguments = Maps.newHashMap();
final RequestMethod[] methods = requestMapping.method();
final MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
for (final MethodParameter methodParameter : methodParameters) {
String parameterName = null;
final RequestParam requestParam = methodParameter.getParameterAnnotation(RequestParam.class);
if (requestParam != null) {
parameterName = requestParam.value();
arguments.put(parameterName, req.getParameter(parameterName));
}
}
final RuleValidator ruleValidator = rulesConfiguration.get(requestMapping.value()[0]);
ruleValidator.validate(arguments);
}
It is a GET method I am working with. Yes, If I remove the interceptor, documentId is sent. Below is my config for interceptors
<mvc:interceptors>
<bean class="mypackage.SecurityInterceptor" />
</mvc:interceptors>
Currently, I'm trying to achieve the same thing. So my last try:
request.getAttribute("org.springframework.web.servlet.HandlerMapping.uriTemplateVariables")
gives you parameter's value.
But I'm not sure, that this is right way.

Keep locale on Spring Security session timeout

I'm using Spring Security 3.0.2 and I have this config
<security:form-login
login-processing-url="/resources/j_spring_security_check"
login-page="/login"
authentication-failure-handler-ref="authErrorHandler" authentication-success-handler-ref="authCorrectHandler" />
<security:logout logout-success-url="/index.jsp" invalidate-session="false" />
<security:remember-me />
<security:session-management invalid-session-url="/login/sessionExpired" >
<security:concurrency-control max-sessions="1"
error-if-maximum-exceeded="true" />
</security:session-management>
When I login with a certain locale, all went well but when expiring session, Spring Security clear session and create new anonymous session with locale by default (and go to the login page as expected). The result is the user locale was LOST.
How can I keep the user locale when expiring session in Spring Security 3.0.2?
I'm using localeChangeInterceptor to set the locale, like this:
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="language" />
</bean>
and SessionLocaleResolver as locale resolver:
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="es" />
</bean>
EDIT - SOLVED FOR MY NEEDS
I've solved this finally setting a cookie in my own LocaleChangeInterceptor which extends from HandlerInterceptorAdapter, writing this in preHandle method:
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
if (localeResolver == null) {
throw new IllegalStateException("No LocaleResolver found.");
}
Cookie mylang = new Cookie("mylang", locale.getLanguage() + "_" + locale.getCountry());
mylang.setMaxAge(86400);
response.addCookie(mylang);
localeResolver.setLocale(request, response, locale);
and then in the /sessionExpired controller point, I'm getting the cookie value:
public String sessionExpired(HttpServletRequest request, HttpServletResponse response, Model model,
#CookieValue(value = "mylang", defaultValue = "es_ES") String myLang) throws Exception {
model.addAttribute("mylang", myLang);
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
StringTokenizer st = new StringTokenizer(myLang, "_");
String language = "";
String country = "";
try {
language = (String) st.nextElement();
country = (String) st.nextElement();
} catch (Exception e) {
throw new Exception("Error locale");
}
Locale locale = new Locale(language, country);
localeResolver.setLocale(request, response, locale);
return "sessionExpired";
No need to use database as temporal storage in this case.
Logically it does not look like you can use the session to keep track of the locale. The locale is set using a request param language which will be cleared with the session. When a new session is created the locale is defaulted to es as given by your localeResolver. One way I could think is to store user preferences in a DB and retrieve from there on subsequent log-ins
Also as suggested by #M. Deinum:
If you are ok with cookies use the CookieLocaleResolver which persists the choice in a cookie. Else implement your own LocaleResolver which stores/retrieves from database.
Stumbled upon this old question and just thought of sharing how I got away with my predicament using a Filter (would've preferred an Interceptor but for some reason it annoyingly ain't working for me as of this writing).
public class SessionTimeoutFilter implements Filter {
private String contextPath;
private String language;
#Override
public void init(FilterConfig config) {
contextPath = config.getInitParameter("context.path");
}
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
String path = request.getRequestURI();
if (path.equals(contextPath)) { //the index page
request.getSession(true);
chain.doFilter(request, response);
} else {
HttpSession session = request.getSession(false);
if (session != null) {
language = (String) session.getAttribute("lang");
chain.doFilter(request, response);
} else {
response.sendRedirect(contextPath + "?timeout=true&lang=" + language);
}
}
}
}
And on top of a LocaleChangeInterceptor, I had parameters in my Controller.
#GetMapping("/")
public String home(#RequestParam(value = "timeout", required = false) Boolean timeout,
#RequestParam(value = "lang", required = false) String language,
HttpServletRequest request, Model model) {
if (Strings.isNotBlank(language)) {
LocaleContextHolder.setLocale(new Locale(language));
} else {
language = LocaleContextHolder.getLocale().getLanguage();
}
if (BooleanUtils.isTrue(timeout)) {
String message = messageService.getMessage("error.timeout", null);
model.addAttribute("message", message);
}
request.getSession().setAttribute("lang", language);
return "index";
}

Resources