Wie bekomme ich das Sitzungsobjekt im Frühjahr?

88

Ich bin relativ neu in Spring und Spring Security.

Ich habe versucht, ein Programm zu schreiben, in dem ich einen Benutzer am Server mit Spring Security authentifizieren musste.

Ich habe mir Folgendes ausgedacht:

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

Mein Anwendungsfall ist, dass ich bei der Authentifizierung eines Benutzers ein Attribut wie das folgende platzieren muss:

session.setAttribute("userObject", myUserObject);

myUserObject ist ein Objekt einer Klasse, auf die ich über mehrere Benutzeranforderungen hinweg im gesamten Servercode zugreifen kann.

Salvin Francis
quelle

Antworten:

140

Dein Freund hier ist 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
}

Dies wird vom Standard-Feder-MVC-Versandservlet ausgefüllt. Wenn Sie jedoch ein anderes Webframework verwenden, müssen Sie es org.springframework.web.filter.RequestContextFilterals Filter hinzufügen web.xml, um den Halter zu verwalten.

EDIT : Nur als Nebenproblem, was versuchen Sie eigentlich zu tun, ich bin nicht sicher, ob Sie Zugriff auf die HttpSessionin der retieveUserMethode von a benötigen sollten UserDetailsService. Spring Security fügt das UserDetails-Objekt für Sie in die Sitzung ein. Sie können es abrufen, indem Sie auf Folgendes zugreifen 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
quelle
1
@erster Punkt: Ich habe versucht, RequestContextHolder zu verwenden. Es wurde ein Fehler angezeigt: Keine threadgebundene Anforderung gefunden: Verweisen Sie auf ... Verwenden Sie RequestContextListener oder RequestContextFilter, um die aktuelle Anforderung verfügbar zu machen. Ich habe keine Filter ausprobiert, denke ich, werde das versuchen und dich wissen lassen, ob es funktioniert. @ zweiter Punkt: Eigentlich ist es ein benutzerdefiniertes Objekt, daher habe ich UserDetails nicht bevorzugt, wenn möglich. Siehe meine andere Frage zu einem ähnlichen Thema: stackoverflow.com/questions/1629273/…
Salvin Francis
2
Wenn Sie das Dispatch-Servlet nicht verwenden, müssen Sie Folgendes konfigurieren: RequestContextFilter
Gareth Davis
1
Entschuldigung, mein Fehler ... hat den Code aus meinem eigenen Projekt angepasst, das getRequest verwendet. Die Antwort wird aktualisiert
Gareth Davis
1
Nein, das sieht ungefähr richtig aus ... versuchen Sie, Ihren Code in einem Debugger zu stoppen und zu überprüfen, ob RequestContextFilter im Aufrufstapel ist
Gareth Davis
1
Ich fand, dass RequestContextFilter für mich nicht funktionierte, während RequestContextListener ...<listener><listener-class>org.springframework.web.context.request.RequestContextListener</listener-class></listener>
Josh
33

Da Sie Spring verwenden, bleiben Sie bei Spring und hacken Sie es nicht selbst wie die anderen Post-Positionen.

Das Spring-Handbuch sagt:

Sie sollten aus Sicherheitsgründen nicht direkt mit der HttpSession interagieren. Es gibt einfach keine Rechtfertigung dafür - verwenden Sie stattdessen immer den SecurityContextHolder.

Die empfohlene Best Practice für den Zugriff auf die Sitzung lautet:

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

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

Der Schlüssel hier ist, dass Spring und Spring Security alle möglichen großartigen Dinge für Sie tun, wie z. B. die Verhinderung von Sitzungsfixierungen. Diese Dinge setzen voraus, dass Sie das Spring-Framework so verwenden, wie es verwendet werden soll. Machen Sie es in Ihrem Servlet kontextbezogen und greifen Sie wie im obigen Beispiel auf die Sitzung zu.

Wenn Sie nur einige Daten im Sitzungsbereich speichern müssen, versuchen Sie, eine Bean mit Sitzungsbereich wie in diesem Beispiel zu erstellen, und lassen Sie Autowire seine Magie entfalten. :) :)

Joseph Lust
quelle
5

Ich habe meine eigenen Utensilien gemacht. es ist praktisch. :) :)

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
quelle
4

In der Tat können Sie auf die Informationen aus der Sitzung zugreifen, selbst wenn die Sitzung auf einem HttpSessionLisener zerstört wird, indem Sie Folgendes tun:

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

Sie können auch überall dort auf die Informationen zugreifen, wo das HttpSession-Objekt verfügbar ist, z.

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

die letzte Annahme, dass Sie etwas haben wie:

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

Ich versuche es mit dem nächsten Code und arbeite hervorragend

    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
quelle
4
Bitte fügen Sie eine Erklärung mit Ihrem Code hinzu. Nur Code oder Link auf eine Frage zu werfen, ist hier keine wirkliche Antwort.
Rémi
1

Wenn Sie nur Details zum Benutzer benötigen, können Sie für Spring Version 4.x die von Spring bereitgestellten Tags verwenden @AuthenticationPrincipalund @EnableWebSecuritykennzeichnen, wie unten gezeigt.

Sicherheitskonfigurationsklasse:

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

Controller-Methode:

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

In meinem Szenario habe ich die HttpSession wie folgt in die CustomAuthenticationProvider-Klasse eingefügt

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
quelle
0
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
attr.getSessionId();
Ebrahim Kh
quelle
5
Könnten Sie Ihrer Antwort bitte einige zusätzliche Informationen hinzufügen, wie dies das Problem im Vergleich zu den anderen vorhandenen Antworten löst?
Slfan