Come ottengo l'oggetto Session in primavera?


88

Sono relativamente nuovo alla primavera sicurezza di e Spring.

Stavo tentando di scrivere un programma in cui avevo bisogno di autenticare un utente sul lato server utilizzando la sicurezza Spring,

Mi è venuto in mente quanto segue:

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

Il mio caso d'uso è che quando un utente viene autenticato, devo inserire un attributo come:

session.setAttribute("userObject", myUserObject);

myUserObject è un oggetto di una classe a cui posso accedere tramite il codice del mio server su più richieste dell'utente.

Risposte:


147

Il tuo amico è qui 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
}

Questo sarà popolato dal servlet standard di invio spring mvc, ma se stai usando un framework web diverso devi aggiungere org.springframework.web.filter.RequestContextFiltercome filtro nel tuo web.xmlper gestire il titolare.

EDIT : proprio come una questione secondaria che cosa stai realmente cercando di fare, io non sono sicuro che avrei bisogno di accedere al HttpSessionnel retieveUsermetodo di un UserDetailsService. La sicurezza di Spring inserirà l'oggetto UserDetails nella sessione in qualsiasi modo. Può essere recuperato accedendo 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;
}

1
@ primo punto: ho provato a utilizzare RequestContextHolder, mi ha dato un errore: nessuna richiesta associata a thread trovata: ti riferisci a ... usa RequestContextListener o RequestContextFilter per esporre la richiesta corrente. Non ho provato i filtri, immagino, lo proverò e ti farò sapere se funziona. @second point: in realtà è un oggetto personalizzato, quindi non ho preferito UserDetails, se possibile, vedi la mia altra domanda su un argomento simile: stackoverflow.com/questions/1629273/…
Salvin Francis

2
se non stai usando il servlet di spedizione devi configurare: RequestContextFilter
Gareth Davis

1
scusate il mio errore ... adattato il codice dal mio progetto che usa getRequest, la risposta è aggiornata
Gareth Davis

1
no sembra giusto ... prova a fermare il tuo codice in un debugger e controlla che RequestContextFilter sia nello stack di chiamate
Gareth Davis

1
Ho scoperto che RequestContextFilter non ha funzionato per me mentre RequestContextListener ha fatto ...<listener><listener-class>org.springframework.web.context.request.RequestContextListener</listener-class></listener>
Josh

33

Dato che stai usando Spring, resta con Spring, non hackerarlo da solo come postulato negli altri post.

Il manuale di primavera dice:

Non dovresti interagire direttamente con HttpSession per motivi di sicurezza. Semplicemente non c'è giustificazione per farlo: usa sempre invece SecurityContextHolder.

La migliore pratica suggerita per accedere alla sessione è:

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

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

La chiave qui è che Spring e Spring Security fanno ogni sorta di cose fantastiche per te come la prevenzione della fissazione della sessione. Queste cose presuppongono che tu stia utilizzando il framework Spring così come è stato progettato per essere utilizzato. Quindi, nel tuo servlet, rendilo consapevole del contesto e accedi alla sessione come nell'esempio precedente.

Se hai solo bisogno di immagazzinare alcuni dati nell'ambito della sessione, prova a creare un bean con ambito di sessione come questo esempio e lascia che autowire faccia la sua magia. :)


5

ho fatto le mie utils. è utile. :)

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

}

4

Infatti puoi accedere alle informazioni dalla sessione anche quando la sessione viene distrutta su un HttpSessionLisener facendo:

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

oppure puoi anche accedere alle informazioni ovunque tu abbia l'oggetto HttpSession disponibile come:

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

l'ultimo assumendo che tu abbia qualcosa come:

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

3

Provo con il codice successivo e lavoro alla grande

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

4
Aggiungi una spiegazione con il tuo codice. Da queste parti solo lanciare codice o link alla domanda di qualcuno non è davvero una risposta.
Rémi

1

Se tutto ciò di cui hai bisogno sono i dettagli dell'utente, per la versione Spring 4.x puoi usare @AuthenticationPrincipale@EnableWebSecurity tag fornito entro la primavera come illustrato di seguito.

Classe di configurazione della sicurezza:

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

Metodo controller:

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

1

Nel mio scenario, ho inserito HttpSession nella classe CustomAuthenticationProvider in questo modo

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

0
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
attr.getSessionId();

5
Potresti aggiungere alcune informazioni aggiuntive alla tua risposta su come questo risolve il problema rispetto alle altre risposte esistenti?
slfan
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.