95

I am relatively new to Spring and Spring security.

I was attempting to write a program where I needed to authenticate a user at the server end using Spring security,

I came up with the following:

public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider{
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken)
                    throws AuthenticationException
    {
        System.out.println("Method invoked : additionalAuthenticationChecks isAuthenticated ? :"+usernamePasswordAuthenticationToken.isAuthenticated());
    }

    @Override
    protected UserDetails retrieveUser(String username,UsernamePasswordAuthenticationToken authentication) throws AuthenticationException 
    {
        System.out.println("Method invoked : retrieveUser");
        //so far so good, i can authenticate user here, and throw exception if not authenticated!!
        //THIS IS WHERE I WANT TO ACCESS SESSION OBJECT
    }
}

My usecase is that when a user is authenticated, I need to place an attribute like:

session.setAttribute("userObject", myUserObject);

myUserObject is an object of some class that I can access throughout my server code across multiple user requests.

Andremoniy
  • 34,031
  • 20
  • 135
  • 241
Salvin Francis
  • 4,117
  • 5
  • 35
  • 43

8 Answers8

160

Your friend here is org.springframework.web.context.request.RequestContextHolder

// example usage
public static HttpSession session() {
    ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
    return attr.getRequest().getSession(true); // true == allow create
}

This will be populated by the standard spring mvc dispatch servlet, but if you are using a different web framework you have add org.springframework.web.filter.RequestContextFilter as a filter in your web.xml to manage the holder.

EDIT: just as a side issue what are you actually trying to do, I'm not sure you should need access to the HttpSession in the retieveUser method of a UserDetailsService. Spring security will put the UserDetails object in the session for you any how. It can be retrieved by accessing the SecurityContextHolder:

public static UserDetails currentUserDetails(){
    SecurityContext securityContext = SecurityContextHolder.getContext();
    Authentication authentication = securityContext.getAuthentication();
    if (authentication != null) {
        Object principal = authentication.getPrincipal();
        return principal instanceof UserDetails ? (UserDetails) principal : null;
    }
    return null;
}
Brett Ryan
  • 26,937
  • 30
  • 128
  • 163
Gareth Davis
  • 27,701
  • 12
  • 73
  • 106
  • 1
    @first point: I tried using RequestContextHolder, it gave me an error: No thread-bound request found:Are you referring to ... use RequestContextListener or RequestContextFilter to expose the current request. I didnt try filters i guess, will try that and let u know if it works. @second point: actually its a custom object hence i didnt prefer UserDetails if possible do see my other question on similar topic: http://stackoverflow.com/questions/1629273/is-it-possible-to-send-more-data-in-form-based-authentication-in-spring – Salvin Francis Oct 27 '09 at 09:07
  • 2
    if you are not using the dispatch servlet you need to configure: RequestContextFilter – Gareth Davis Oct 27 '09 at 09:30
  • 1. attr.getSession(true); >> is protected method and is not accessible in CustomAuthenticationProvider 2. this is wht i did in my web.xml: requestContextFilter org.springframework.web.filter.RequestContextFilter requestContextFilter /* still the same error occurs. – Salvin Francis Oct 27 '09 at 09:44
  • 1
    sorry my mistake... adapted the code from my own project which uses getRequest, the answer is updated – Gareth Davis Oct 27 '09 at 10:30
  • ok, but my point 2 is still prevalent: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request. – Salvin Francis Oct 27 '09 at 10:44
  • Is this wrong ? requestContextFilter org.springframework.web.filter.RequestContextFilter requestContextFilter /* – Salvin Francis Oct 27 '09 at 10:49
  • 1
    nope that looks about right... try stopping your code in a debugger and checking that RequestContextFilter is in the call stack – Gareth Davis Oct 27 '09 at 11:12
  • thanks mate, it worked, turns out that i had placed this after the DelegatingFilterProxy filter in web.xml I didnt know that the order of filters did matter here. a new lesson learnt, thanks for the effort. – Salvin Francis Oct 27 '09 at 13:00
  • 1
    I found that **RequestContextFilter** didn't work for me whereas **RequestContextListener** did... `org.springframework.web.context.request.RequestContextListener` – Josh Aug 10 '11 at 14:28
  • Just make sure to add RequestContextListener in your web.xml org.springframework.web.context.request.RequestContextListener in case of using RequestContextHolder.currentRequestAttributes(); – objectone Dec 11 '13 at 23:13
  • Thumbs up to both your solutions. An enhancement to the `currentUserDetails` could be made to instead return the username which could also be an `Authentication` object or a string of the username itself, this could be tested and return just the username instead of the whole `UserDetails` making it a little more universal. – Brett Ryan Apr 08 '15 at 05:40
  • It's a bug prone solution. – Alex78191 May 22 '18 at 22:31
37

Since you're using Spring, stick with Spring, don't hack it yourself like the other post posits.

The Spring manual says:

You shouldn't interact directly with the HttpSession for security purposes. There is simply no justification for doing so - always use the SecurityContextHolder instead.

The suggested best practice for accessing the session is:

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

if (principal instanceof UserDetails) {
  String username = ((UserDetails)principal).getUsername();
} else {
  String username = principal.toString();
}

The key here is that Spring and Spring Security do all sorts of great stuff for you like Session Fixation Prevention. These things assume that you're using the Spring framework as it was designed to be used. So, in your servlet, make it context aware and access the session like the above example.

If you just need to stash some data in the session scope, try creating some session scoped bean like this example and let autowire do its magic. :)

Community
  • 1
  • 1
Joseph Lust
  • 19,340
  • 7
  • 85
  • 83
6

i made my own utils. it is handy. :)

package samples.utils;

import java.util.Arrays;
import java.util.Collection;
import java.util.Locale;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.MessageSource;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.ui.context.Theme;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.ThemeResolver;
import org.springframework.web.servlet.support.RequestContextUtils;


/**
 * SpringMVC通用工具
 * 
 * @author 应卓(yingzhor@gmail.com)
 *
 */
public final class WebContextHolder {

    private static final Logger LOGGER = LoggerFactory.getLogger(WebContextHolder.class);

    private static WebContextHolder INSTANCE = new WebContextHolder();

    public WebContextHolder get() {
        return INSTANCE;
    }

    private WebContextHolder() {
        super();
    }

    // --------------------------------------------------------------------------------------------------------------

    public HttpServletRequest getRequest() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        return attributes.getRequest();
    }

    public HttpSession getSession() {
        return getSession(true);
    }

    public HttpSession getSession(boolean create) {
        return getRequest().getSession(create);
    }

    public String getSessionId() {
        return getSession().getId();
    }

    public ServletContext getServletContext() {
        return getSession().getServletContext();    // servlet2.3
    }

    public Locale getLocale() {
        return RequestContextUtils.getLocale(getRequest());
    }

    public Theme getTheme() {
        return RequestContextUtils.getTheme(getRequest());
    }

    public ApplicationContext getApplicationContext() {
        return WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    }

    public ApplicationEventPublisher getApplicationEventPublisher() {
        return (ApplicationEventPublisher) getApplicationContext();
    }

    public LocaleResolver getLocaleResolver() {
        return RequestContextUtils.getLocaleResolver(getRequest());
    }

    public ThemeResolver getThemeResolver() {
        return RequestContextUtils.getThemeResolver(getRequest());
    }

    public ResourceLoader getResourceLoader() {
        return (ResourceLoader) getApplicationContext();
    }

    public ResourcePatternResolver getResourcePatternResolver() {
        return (ResourcePatternResolver) getApplicationContext();
    }

    public MessageSource getMessageSource() {
        return (MessageSource) getApplicationContext();
    }

    public ConversionService getConversionService() {
        return getBeanFromApplicationContext(ConversionService.class);
    }

    public DataSource getDataSource() {
        return getBeanFromApplicationContext(DataSource.class);
    }

    public Collection<String> getActiveProfiles() {
        return Arrays.asList(getApplicationContext().getEnvironment().getActiveProfiles());
    }

    public ClassLoader getBeanClassLoader() {
        return ClassUtils.getDefaultClassLoader();
    }

    private <T> T getBeanFromApplicationContext(Class<T> requiredType) {
        try {
            return getApplicationContext().getBean(requiredType);
        } catch (NoUniqueBeanDefinitionException e) {
            LOGGER.error(e.getMessage(), e);
            throw e;
        } catch (NoSuchBeanDefinitionException e) {
            LOGGER.warn(e.getMessage());
            return null;
        }
    }

}
Zhuo YING
  • 972
  • 3
  • 11
  • 19
4

Indeed you can access the information from the session even when the session is being destroyed on an HttpSessionLisener by doing:

public void sessionDestroyed(HttpSessionEvent hse) {
    SecurityContextImpl sci = (SecurityContextImpl) hse.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
    // be sure to check is not null since for users who just get into the home page but never get authenticated it will be
    if (sci != null) {
        UserDetails cud = (UserDetails) sci.getAuthentication().getPrincipal();
        // do whatever you need here with the UserDetails
    }
 }

or you could also access the information anywhere you have the HttpSession object available like:

SecurityContextImpl sci = (SecurityContextImpl) session().getAttribute("SPRING_SECURITY_CONTEXT");

the last assuming you have something like:

HttpSession sesssion = ...; // can come from request.getSession(false);
OscarG
  • 385
  • 5
  • 11
4

In my scenario, I've injected the HttpSession into the CustomAuthenticationProvider class like this

public class CustomAuthenticationProvider extends  AbstractUserDetailsAuthenticationProvider{

    @Autowired 
    private HttpSession httpSession;

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken)
             throws AuthenticationException
    {
        System.out.println("Method invoked : additionalAuthenticationChecks isAuthenticated ? :"+usernamePasswordAuthenticationToken.isAuthenticated());
    }

    @Override
    protected UserDetails retrieveUser(String username,UsernamePasswordAuthenticationToken authentication) throws AuthenticationException 
    {
        System.out.println("Method invoked : retrieveUser");
        //so far so good, i can authenticate user here, and throw exception 
if not authenticated!!
        //THIS IS WHERE I WANT TO ACCESS SESSION OBJECT
        httpSession.setAttribute("userObject", myUserObject);
    }
}
Kinglee
  • 53
  • 1
  • 8
Gopi
  • 620
  • 8
  • 16
3

I try with next code and work excellent

    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.ModelMap;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;

    /**
     * Created by jaime on 14/01/15.
     */

    @Controller
    public class obteinUserSession {
        @RequestMapping(value = "/loginds", method = RequestMethod.GET)
        public String UserSession(ModelMap modelMap) {
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            String name = auth.getName();
            modelMap.addAttribute("username", name);
            return "hellos " + name;
        }
jaimeRambo
  • 79
  • 1
  • 1
  • 5
  • 4
    Please add an explanation with your code. Around here just throwing code or link at someone question isn't really an answer. – Rémi Jan 14 '15 at 21:19
2

If all that you need is details of User, for Spring Version 4.x you can use @AuthenticationPrincipal and @EnableWebSecurity tag provided by Spring as shown below.

Security Configuration Class:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
   ...
}

Controller method:

@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@AuthenticationPrincipal User user) {
    ...
}
pranjal thakur
  • 312
  • 2
  • 13
0
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
attr.getSessionId();
  • 5
    Could you please add some additional information to your answer on how this solves the problem in comparison to the other existing answers? – slfan May 11 '19 at 11:09