Bagaimana cara mendapatkan Objek Sesi di Musim Semi?

88

Saya relatif baru dalam keamanan Musim Semi dan Musim Semi.

Saya mencoba untuk menulis program di mana saya perlu mengautentikasi pengguna di ujung server menggunakan keamanan Spring,

Saya datang dengan yang berikut:

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

Kasus penggunaan saya adalah ketika pengguna diautentikasi, saya perlu menempatkan atribut seperti:

session.setAttribute("userObject", myUserObject);

myUserObject adalah objek dari beberapa kelas yang dapat saya akses di seluruh kode server saya di beberapa permintaan pengguna.

Salvin Francis
sumber

Jawaban:

147

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

Ini akan diisi oleh servlet pengiriman mvc pegas standar, tetapi jika Anda menggunakan kerangka kerja web yang berbeda, Anda telah menambahkan org.springframework.web.filter.RequestContextFiltersebagai filter web.xmluntuk mengelola dudukan.

EDIT : hanya sebagai masalah sampingan apa yang sebenarnya Anda coba lakukan, saya tidak yakin Anda harus memerlukan akses ke HttpSessiondalam retieveUsermetode a UserDetailsService. Keamanan musim semi akan menempatkan objek UserDetails di sesi untuk Anda dengan caranya. Itu dapat diambil dengan mengakses 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
sumber
1
@ poin pertama: Saya mencoba menggunakan RequestContextHolder, itu memberi saya kesalahan: Tidak ada permintaan terikat utas yang ditemukan: Apakah Anda merujuk ke ... gunakan RequestContextListener atau RequestContextFilter untuk mengekspos permintaan saat ini. Saya tidak mencoba filter, saya kira, akan mencobanya dan memberi tahu Anda jika berhasil. @ poin kedua: sebenarnya itu adalah objek khusus maka saya tidak lebih suka UserDetails jika memungkinkan lihat pertanyaan saya yang lain tentang topik serupa: stackoverflow.com/questions/1629273/…
Salvin Francis
2
jika Anda tidak menggunakan servlet pengiriman, Anda perlu mengkonfigurasi: RequestContextFilter
Gareth Davis
1
maaf kesalahan saya ... mengadaptasi kode dari proyek saya sendiri yang menggunakan getRequest, jawabannya diperbarui
Gareth Davis
1
tidak terlihat benar ... coba hentikan kode Anda di debugger dan periksa bahwa RequestContextFilter ada di tumpukan panggilan
Gareth Davis
1
Saya menemukan bahwa RequestContextFilter tidak berfungsi untuk saya sedangkan RequestContextListener berhasil ...<listener><listener-class>org.springframework.web.context.request.RequestContextListener</listener-class></listener>
Josh
33

Karena Anda menggunakan Spring, tetap gunakan Spring, jangan meretasnya sendiri seperti pos lainnya.

The pengguna Musim Semi mengatakan:

Anda tidak boleh berinteraksi langsung dengan HttpSession untuk tujuan keamanan. Tidak ada justifikasi untuk melakukannya - selalu gunakan SecurityContextHolder sebagai gantinya.

Praktik terbaik yang disarankan untuk mengakses sesi ini adalah:

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

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

Kuncinya di sini adalah Keamanan Musim Semi dan Musim Semi melakukan segala macam hal hebat untuk Anda seperti Pencegahan Fiksasi Sesi. Hal-hal ini mengasumsikan bahwa Anda menggunakan framework Spring sebagaimana yang dirancang untuk digunakan. Jadi, di servlet Anda, buatlah context aware dan akses sesi seperti contoh di atas.

Jika Anda hanya perlu menyimpan beberapa data dalam cakupan sesi, coba buat kacang cakupan sesi seperti contoh ini dan biarkan autowire melakukan keajaibannya. :)

Joseph Lust
sumber
5

saya membuat utilitas saya sendiri. itu berguna. :)

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

Memang Anda bisa mengakses informasi dari sesi bahkan ketika sesi sedang dihancurkan di HttpSessionLisener dengan melakukan:

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

atau Anda juga dapat mengakses informasi di mana pun Anda memiliki objek HttpSession yang tersedia seperti:

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

asumsi terakhir Anda memiliki sesuatu seperti:

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

Saya mencoba dengan kode berikutnya dan bekerja dengan sangat baik

    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
sumber
4
Harap tambahkan penjelasan dengan kode Anda. Di sini, hanya melempar kode atau tautan ke pertanyaan seseorang bukanlah jawaban.
Rémi
2

Jika yang Anda butuhkan hanyalah detail Pengguna, untuk Spring Version 4.x Anda dapat menggunakan @AuthenticationPrincipaldan @EnableWebSecuritytag yang disediakan oleh Spring seperti gambar di bawah ini.

Kelas Konfigurasi Keamanan:

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

Metode pengontrol:

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

Dalam skenario saya, saya telah menyuntikkan HttpSession ke kelas CustomAuthenticationProvider seperti ini

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
sumber
0
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
attr.getSessionId();
Ebrahim Kh
sumber
5
Bisakah Anda menambahkan beberapa informasi tambahan ke jawaban Anda tentang bagaimana ini memecahkan masalah dibandingkan dengan jawaban lain yang ada?
slfan