¿Cómo obtengo el objeto de sesión en Spring?

88

Soy relativamente nuevo en Spring y Spring Security.

Estaba intentando escribir un programa en el que necesitaba autenticar a un usuario en el extremo del servidor utilizando la seguridad de Spring,

Se me ocurrió lo siguiente:

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

Mi caso de uso es que cuando un usuario está autenticado, necesito colocar un atributo como:

session.setAttribute("userObject", myUserObject);

myUserObject es un objeto de alguna clase al que puedo acceder a través del código de mi servidor a través de múltiples solicitudes de usuario.

Salvin Francis
fuente

Respuestas:

147

Tu amigo aqui es 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
}

Esto se completará con el servlet de envío estándar de Spring MVC, pero si está utilizando un marco web diferente, debe agregarlo org.springframework.web.filter.RequestContextFiltercomo un filtro web.xmlpara administrar el titular.

EDITAR : así como una cuestión secundaria ¿qué estás tratando de hacer realidad, no estoy seguro de que debería obtener acceso al programa HttpSessionen el retieveUsermétodo de un UserDetailsService. Spring Security colocará el objeto UserDetails en la sesión de cualquier manera. Se puede recuperar accediendo a 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;
}
Gareth Davis
fuente
1
@primer punto: Intenté usar RequestContextHolder, me dio un error: No se encontró una solicitud vinculada a un hilo: ¿Se refiere a ... use RequestContextListener o RequestContextFilter para exponer la solicitud actual. No probé los filtros, supongo, lo intentaré y te haré saber si funciona. @segundo punto: en realidad es un objeto personalizado, por lo tanto, no preferí UserDetails si es posible, vea mi otra pregunta sobre un tema similar: stackoverflow.com/questions/1629273/…
Salvin Francis
2
si no está utilizando el servlet de despacho, debe configurar: RequestContextFilter
Gareth Davis
1
perdón mi error ... adapté el código de mi propio proyecto que usa getRequest, la respuesta está actualizada
Gareth Davis
1
no, eso parece correcto ... intente detener su código en un depurador y verifique que RequestContextFilter esté en la pila de llamadas
Gareth Davis
1
Descubrí que RequestContextFilter no funcionó para mí, mientras que RequestContextListener sí ...<listener><listener-class>org.springframework.web.context.request.RequestContextListener</listener-class></listener>
Josh
33

Ya que está usando Spring, quédese con Spring, no lo piratee usted mismo como las otras publicaciones.

El manual de Spring dice:

No debe interactuar directamente con HttpSession por motivos de seguridad. Simplemente no hay justificación para hacerlo; siempre use SecurityContextHolder en su lugar.

La mejor práctica sugerida para acceder a la sesión es:

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

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

La clave aquí es que Spring y Spring Security hacen todo tipo de cosas excelentes para usted, como Prevención de fijación de sesiones. Estas cosas suponen que está utilizando el marco Spring tal como fue diseñado para su uso. Entonces, en su servlet, hágalo consciente del contexto y acceda a la sesión como en el ejemplo anterior.

Si solo necesita guardar algunos datos en el alcance de la sesión, intente crear un bean con alcance de sesión como este ejemplo y deje que autowire haga su magia. :)

Joseph Lust
fuente
5

hice mis propias utilidades. es útil. :)

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 应卓([email protected])
 *
 */
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
fuente
4

De hecho, puede acceder a la información de la sesión incluso cuando la sesión se está destruyendo en un HttpSessionLisener haciendo:

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

o también puede acceder a la información en cualquier lugar donde tenga el objeto HttpSession disponible como:

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

el último asumiendo que tienes algo como:

HttpSession sesssion = ...; // can come from request.getSession(false);
OscarG
fuente
3

Intento con el siguiente código y trabajo excelente

    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
fuente
4
Agregue una explicación con su código. Por aquí, simplemente arrojar un código o un enlace a la pregunta de alguien no es realmente una respuesta.
Rémi
1

Si todo lo que necesita son los detalles del usuario, para Spring Version 4.x puede usar @AuthenticationPrincipaly@EnableWebSecurity la etiqueta proporcionada por el resorte como se muestra a continuación.

Clase de configuración de seguridad:

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

Método de controlador:

@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@AuthenticationPrincipal User user) {
    ...
}
pranjal thakur
fuente
1

En mi escenario, he inyectado HttpSession en la clase CustomAuthenticationProvider de esta manera

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);
    }
}
Gopi
fuente
0
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
attr.getSessionId();
Ebrahim Kh
fuente
5
¿Podría agregar información adicional a su respuesta sobre cómo esto resuelve el problema en comparación con las otras respuestas existentes?
slfan