Spring에서 세션 객체를 어떻게 얻습니까?


88

저는 Spring 과 Spring 보안에 비교적 익숙하지 않습니다.

Spring 보안을 사용하여 서버 끝에서 사용자를 인증해야하는 프로그램을 작성하려고했습니다.

나는 다음을 생각 해냈다.

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

내 사용 사례는 사용자가 인증 될 때 다음과 같은 속성을 배치해야한다는 것입니다.

session.setAttribute("userObject", myUserObject);

myUserObject는 여러 사용자 요청에서 서버 코드 전체에 액세스 할 수있는 일부 클래스의 개체입니다.

답변:


147

여기 당신의 친구는 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
}

이것은 표준 spring mvc 디스패치 서블릿에 의해 채워지지만 다른 웹 프레임 워크를 사용하는 경우 홀더를 관리 org.springframework.web.filter.RequestContextFilter하기 web.xml위해 필터로 추가 해야합니다.

편집 : 당신이 실제로 무엇을하려고 측면의 문제로, 나는 확실히 당신이 액세스가 필요합니다 아니에요 HttpSession에서 retieveUser의 방법을 UserDetailsService. Spring 보안은 UserDetails 객체를 세션에 넣습니다. 에 액세스하여 검색 할 수 있습니다 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
@ 첫 번째 포인트 : RequestContextHolder를 사용해 보았습니다. 오류가 발생했습니다. 스레드 바운드 요청을 찾을 수 없음 : 참조 중이신가요? 현재 요청을 노출하려면 RequestContextListener 또는 RequestContextFilter를 사용하세요. 나는 내가 생각하는 필터를 시도하지 않았고, 그것을 시도하고 그것이 작동하는지 알려줄 것입니다. @second point : 실제로는 사용자 정의 개체이므로 가능한 경우 UserDetails를 선호하지 않았습니다. 비슷한 주제에 대한 다른 질문을 참조하십시오. stackoverflow.com/questions/1629273/…
Salvin Francis

2
디스패치 서블릿을 사용하지 않는 경우 다음을 구성해야합니다. RequestContextFilter
Gareth Davis

1
미안 내 실수는 ... 답이 업데이트, 대해 GetRequest를 사용하여 내 자신의 프로젝트에서 코드를 적용
가레스 데이비스

1
아니 그것에 대해 외모 바로 ... 디버거에서 코드를 중지하고 RequestContextFilter이 호출 스택에 있는지 확인해보십시오
가레스 데이비스

1
나는 것을 발견 RequestContextFilter는 반면, 나를 위해 작동하지 않았다 RequestContextListener이 했다 ...<listener><listener-class>org.springframework.web.context.request.RequestContextListener</listener-class></listener>
조쉬

33

Spring을 사용하고 있으므로 Spring을 고수하고 다른 게시물 위치처럼 직접 해킹하지 마십시오.

봄 설명서는 말한다 :

보안을 위해 HttpSession과 직접 상호 작용해서는 안됩니다. 그렇게하는 이유는 간단하지 않습니다. 대신 항상 SecurityContextHolder를 사용하십시오.

세션 액세스를위한 권장 모범 사례는 다음과 같습니다.

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

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

여기서 핵심은 Spring과 Spring Security가 Session Fixation Prevention과 같은 모든 종류의 훌륭한 기능을 수행한다는 것입니다. 이러한 것들은 사용하도록 설계된 Spring 프레임 워크를 사용하고 있다고 가정합니다. 따라서 서블릿에서 위의 예와 같이 컨텍스트를 인식하고 세션에 액세스하십시오.

세션 범위에 일부 데이터를 숨길 필요가 있다면 이 예제 와 같이 세션 범위 빈을 만들어 autowire가 마법을 수행하도록하십시오. :)


5

나는 내 자신의 유틸리티를 만들었습니다. 편리합니다. :)

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

실제로 다음을 수행하여 HttpSessionLisener에서 세션이 소멸되는 경우에도 세션의 정보에 액세스 할 수 있습니다.

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

또는 다음과 같이 HttpSession 개체를 사용할 수있는 모든 위치에서 정보에 액세스 할 수도 있습니다.

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

마지막으로 다음과 같은 것이 있다고 가정합니다.

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

3

나는 다음 코드로 시도하고 훌륭하게 작동합니다.

    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
코드와 함께 설명을 추가하십시오. 이 주변에서 누군가 질문에 코드 나 링크를 던지는 것은 실제로 답이 아닙니다.
Rémi

1

당신이 필요로하는 모든 사용자의 세부 사항을 경우를 위해 봄 버전 4.x의 사용 수 @AuthenticationPrincipal@EnableWebSecurity다음과 같이 태그는 Spring에 의해 제공했다.

보안 구성 클래스 :

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

컨트롤러 방법 :

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

1

내 시나리오에서는 다음과 같이 CustomAuthenticationProvider 클래스에 HttpSession을 삽입했습니다.

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
다른 기존 답변과 비교하여 문제가 어떻게 해결되는지에 대한 답변에 몇 가지 추가 정보를 추가해 주시겠습니까?
slfan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.