Comment obtenir l'objet Session au printemps?

88

Je suis relativement nouveau dans la sécurité Spring et Spring.

J'essayais d'écrire un programme dans lequel je devais authentifier un utilisateur côté serveur à l'aide de la sécurité Spring,

J'ai proposé ce qui suit:

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

Mon cas d'utilisation est que lorsqu'un utilisateur est authentifié, je dois placer un attribut comme:

session.setAttribute("userObject", myUserObject);

myUserObject est un objet d'une certaine classe auquel je peux accéder à travers le code de mon serveur à travers plusieurs demandes d'utilisateurs.

Salvin Francis
la source

Réponses:

147

Votre ami ici est 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
}

Celui-ci sera alimenté par le servlet de distribution Spring mvc standard, mais si vous utilisez un autre framework Web, vous devez ajouter org.springframework.web.filter.RequestContextFilterun filtre dans votre web.xmlpour gérer le support.

EDIT : juste comme problème secondaire, qu'est-ce que vous essayez réellement de faire, je ne suis pas sûr que vous devriez avoir besoin d'accéder à HttpSessionla retieveUserméthode de a UserDetailsService. Spring Security placera l'objet UserDetails dans la session pour vous de toute façon. Il peut être récupéré en accédant au 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
la source
1
@premier point: j'ai essayé d'utiliser RequestContextHolder, cela m'a donné une erreur: Aucune requête liée au thread n'a été trouvée: faites-vous référence à ... utilisez RequestContextListener ou RequestContextFilter pour exposer la requête actuelle. Je n'ai pas essayé de filtres je suppose, je vais essayer cela et vous faire savoir si cela fonctionne. @second point: en fait c'est un objet personnalisé, donc je n'ai pas préféré UserDetails si possible, voyez mon autre question sur un sujet similaire: stackoverflow.com/questions/1629273/…
Salvin Francis
2
si vous n'utilisez pas le servlet de répartition, vous devez configurer: RequestContextFilter
Gareth Davis
1
désolé mon erreur ... adapté le code de mon propre projet qui utilise getRequest, la réponse est mise à jour
Gareth Davis
1
non, cela semble à peu près correct ... essayez d'arrêter votre code dans un débogueur et de vérifier que RequestContextFilter est dans la pile d'appels
Gareth Davis
1
J'ai trouvé que RequestContextFilter ne fonctionnait pas pour moi alors que RequestContextListener fonctionnait ...<listener><listener-class>org.springframework.web.context.request.RequestContextListener</listener-class></listener>
Josh
33

Puisque vous utilisez Spring, restez fidèle à Spring, ne le piratez pas vous-même comme les autres postures.

Le manuel Spring dit:

Vous ne devez pas interagir directement avec HttpSession pour des raisons de sécurité. Il n'y a tout simplement aucune justification à le faire - utilisez toujours SecurityContextHolder à la place.

La meilleure pratique suggérée pour accéder à la session est:

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

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

La clé ici est que Spring et Spring Security font toutes sortes de choses intéressantes pour vous, comme la prévention de la fixation de session. Ces choses supposent que vous utilisez le framework Spring tel qu'il a été conçu pour être utilisé. Donc, dans votre servlet, rendez-le conscient du contexte et accédez à la session comme dans l'exemple ci-dessus.

Si vous avez juste besoin de cacher des données dans la portée de session, essayez de créer un bean à portée de session comme cet exemple et laissez autowire faire sa magie. :)

Joseph Lust
la source
5

j'ai fait mes propres ustensiles. c'est pratique. :)

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
la source
4

En effet, vous pouvez accéder aux informations de la session même lorsque la session est en cours de destruction sur un HttpSessionLisener en faisant:

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

ou vous pouvez également accéder aux informations partout où vous avez l'objet HttpSession disponible comme:

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

le dernier en supposant que vous avez quelque chose comme:

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

J'essaye avec le code suivant et je travaille 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
la source
4
Veuillez ajouter une explication avec votre code. Ici, le simple fait de lancer du code ou un lien sur une question à quelqu'un n'est pas vraiment une réponse.
Rémi
1

Si tout ce dont vous avez besoin, ce sont des détails sur l'utilisateur, pour Spring Version 4.x, vous pouvez utiliser @AuthenticationPrincipalet @EnableWebSecurityétiqueter fourni par Spring comme indiqué ci-dessous.

Classe de configuration de sécurité:

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

Méthode du contrôleur:

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

Dans mon scénario, j'ai injecté la HttpSession dans la classe CustomAuthenticationProvider comme ceci

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
la source
0
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
attr.getSessionId();
Ebrahim Kh
la source
5
Pourriez-vous s'il vous plaît ajouter des informations supplémentaires à votre réponse sur la façon dont cela résout le problème par rapport aux autres réponses existantes?
slfan