[spring] 세션없이 Spring Security를 ​​어떻게 사용할 수 있습니까?

Amazon EC2에 상주하고 Amazon의 Elastic Load Balancer를 사용할 Spring Security로 웹 애플리케이션을 구축하고 있습니다. 안타깝게도 ELB는 고정 세션을 지원하지 않으므로 내 응용 프로그램이 세션없이 제대로 작동하는지 확인해야합니다.

지금까지 쿠키를 통해 토큰을 할당하도록 RememberMeServices를 설정했는데 제대로 작동하지만 브라우저 세션과 함께 쿠키가 만료되기를 원합니다 (예 : 브라우저가 닫힐 때).

세션없이 Spring Security를 ​​사용하고 싶은 첫 번째 사람이 아니라고 상상해야합니다 … 어떤 제안이라도?



답변

Java Config가있는 Spring Security 3 에서는 HttpSecurity.sessionManagement () 사용할 수 있습니다 .

@Override
protected void configure(final HttpSecurity http) throws Exception {
    http
        .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}


답변

Spring Securitiy 3.0에서는 훨씬 더 쉬운 것 같습니다. 네임 스페이스 구성을 사용하는 경우 다음과 같이 간단히 수행 할 수 있습니다.

<http create-session="never">
  <!-- config -->
</http>

또는 당신은 널 (null)로 SecurityContextRepository을 구성 할 수 있습니다, 아무것도 이제까지 그런 식으로 저장되지 얻을 것 뿐만 아니라 .


답변

우리는 오늘 4-5 시간 동안 동일한 문제 (사용자 지정 SecurityContextRepository를 SecurityContextPersistenceFilter에 삽입)에 대해 작업했습니다. 마침내 우리는 그것을 알아 냈습니다. 먼저 Spring Security ref의 8.3 섹션에서. doc, SecurityContextPersistenceFilter 빈 정의가 있습니다.

<bean id="securityContextPersistenceFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
    <property name='securityContextRepository'>
        <bean class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'>
            <property name='allowSessionCreation' value='false' />
        </bean>
    </property>
</bean>

그리고이 정의 뒤에는 다음과 같은 설명이 있습니다. “또는 SecurityContextRepository 인터페이스의 null 구현을 제공 할 수 있습니다. 그러면 요청 중에 세션이 이미 생성 된 경우에도 보안 컨텍스트가 저장되지 않습니다.”

사용자 지정 SecurityContextRepository를 SecurityContextPersistenceFilter에 삽입해야했습니다. 그래서 우리는 사용자 정의 impl을 사용하여 위의 bean 정의를 변경하고 보안 컨텍스트에 넣었습니다.

애플리케이션을 실행할 때 로그를 추적하고 SecurityContextPersistenceFilter가 사용자 지정 impl을 사용하지 않고 HttpSessionSecurityContextRepository를 사용하고 있음을 확인했습니다.

몇 가지 다른 작업을 시도한 후 “http”네임 스페이스의 “security-context-repository-ref”속성을 사용하여 사용자 정의 SecurityContextRepository impl을 제공해야한다는 것을 알아 냈습니다. “http”네임 스페이스를 사용하고 자신의 SecurityContextRepository impl을 삽입하려면 “security-context-repository-ref”속성을 사용해보십시오.

“http”네임 스페이스가 사용되면 별도의 SecurityContextPersistenceFilter 정의가 무시됩니다. 위에서 복사했듯이 참조 문서. 그것을 언급하지 않습니다.

제가 잘못 이해했다면 수정 해주세요.


답변

한 번 봐 가지고 SecurityContextPersistenceFilter클래스를. SecurityContextHolder이 채워지 는 방법을 정의합니다 . 기본적 HttpSessionSecurityContextRepository으로 http 세션에 보안 컨텍스트를 저장 하는 데 사용 합니다.

이 메커니즘은 custom SecurityContextRepository.

securityContext.xml아래를 참조하십시오 .

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:sec="http://www.springframework.org/schema/security"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
       http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
       http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">

    <context:annotation-config/>

    <sec:global-method-security secured-annotations="enabled" pre-post-annotations="enabled"/>

    <bean id="securityContextRepository" class="com.project.server.security.TokenSecurityContextRepository"/>

    <bean id="securityContextFilter" class="com.project.server.security.TokenSecurityContextPersistenceFilter">
        <property name="repository" ref="securityContextRepository"/>
    </bean>

    <bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
        <constructor-arg value="/login.jsp"/>
        <constructor-arg>
            <list>
                <bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
            </list>
        </constructor-arg>
    </bean>

    <bean id="formLoginFilter"
          class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="authenticationSuccessHandler">
            <bean class="com.project.server.security.TokenAuthenticationSuccessHandler">
                <property name="defaultTargetUrl" value="/index.html"/>
                <property name="passwordExpiredUrl" value="/changePassword.jsp"/>
                <property name="alwaysUseDefaultTargetUrl" value="true"/>
            </bean>
        </property>
        <property name="authenticationFailureHandler">
            <bean class="com.project.server.modules.security.CustomUrlAuthenticationFailureHandler">
                <property name="defaultFailureUrl" value="/login.jsp?failure=1"/>
            </bean>
        </property>
        <property name="filterProcessesUrl" value="/j_spring_security_check"/>
        <property name="allowSessionCreation" value="false"/>
    </bean>

    <bean id="servletApiFilter"
          class="org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter"/>

    <bean id="anonFilter" class="org.springframework.security.web.authentication.AnonymousAuthenticationFilter">
        <property name="key" value="ClientApplication"/>
        <property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"/>
    </bean>


    <bean id="exceptionTranslator" class="org.springframework.security.web.access.ExceptionTranslationFilter">
        <property name="authenticationEntryPoint">
            <bean class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
                <property name="loginFormUrl" value="/login.jsp"/>
            </bean>
        </property>
        <property name="accessDeniedHandler">
            <bean class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
                <property name="errorPage" value="/login.jsp?failure=2"/>
            </bean>
        </property>
        <property name="requestCache">
            <bean id="nullRequestCache" class="org.springframework.security.web.savedrequest.NullRequestCache"/>
        </property>
    </bean>

    <alias name="filterChainProxy" alias="springSecurityFilterChain"/>

    <bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
        <sec:filter-chain-map path-type="ant">
            <sec:filter-chain pattern="/**"
                              filters="securityContextFilter, logoutFilter, formLoginFilter,
                                        servletApiFilter, anonFilter, exceptionTranslator, filterSecurityInterceptor"/>
        </sec:filter-chain-map>
    </bean>

    <bean id="filterSecurityInterceptor"
          class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
        <property name="securityMetadataSource">
            <sec:filter-security-metadata-source use-expressions="true">
                <sec:intercept-url pattern="/staticresources/**" access="permitAll"/>
                <sec:intercept-url pattern="/index.html*" access="hasRole('USER_ROLE')"/>
                <sec:intercept-url pattern="/rpc/*" access="hasRole('USER_ROLE')"/>
                <sec:intercept-url pattern="/**" access="permitAll"/>
            </sec:filter-security-metadata-source>
        </property>
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="accessDecisionManager" ref="accessDecisionManager"/>
    </bean>

    <bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
        <property name="decisionVoters">
            <list>
                <bean class="org.springframework.security.access.vote.RoleVoter"/>
                <bean class="org.springframework.security.web.access.expression.WebExpressionVoter"/>
            </list>
        </property>
    </bean>

    <bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
        <property name="providers">
            <list>
                <bean name="authenticationProvider"
                      class="com.project.server.modules.security.oracle.StoredProcedureBasedAuthenticationProviderImpl">
                    <property name="dataSource" ref="serverDataSource"/>
                    <property name="userDetailsService" ref="userDetailsService"/>
                    <property name="auditLogin" value="true"/>
                    <property name="postAuthenticationChecks" ref="customPostAuthenticationChecks"/>
                </bean>
            </list>
        </property>
    </bean>

    <bean id="customPostAuthenticationChecks" class="com.project.server.modules.security.CustomPostAuthenticationChecks"/>

    <bean name="userDetailsService" class="com.project.server.modules.security.oracle.UserDetailsServiceImpl">
        <property name="dataSource" ref="serverDataSource"/>
    </bean>

</beans>


답변

실제로 create-session="never"완전히 무국적자가되는 것은 아닙니다. 거기에 문제가 봄 보안 문제를 관리하는 대한이.


답변

이 답변에 게시 된 수많은 솔루션으로 어려움을 겪은 후 <http>네임 스페이스 구성을 사용할 때 작동하는 것을 시도한 후 마침내 실제로 사용 사례에 맞는 접근 방식을 찾았습니다. 나는 실제로 Spring Security가 세션을 시작하지 않을 것을 요구하지 않는다 (왜냐하면 나는 애플리케이션의 다른 부분에서 세션을 사용하기 때문이다). 단지 세션에서 인증을 전혀 “기억”하지 않는다는 것 (다시 확인되어야한다) 모든 요청).

우선 위에서 설명한 “null 구현”기법을 수행하는 방법을 알아낼 수 없었습니다. securityContextRepository를 설정해야하는지 null또는 no-op 구현 으로 설정해야하는지 명확하지 않았습니다 . 전자 NullPointerException는에서 던져 지기 때문에 작동하지 않습니다 SecurityContextPersistenceFilter.doFilter(). no-op 구현의 경우 상상할 수있는 가장 간단한 방법으로 구현해 보았습니다.

public class NullSpringSecurityContextRepository implements SecurityContextRepository {

    @Override
    public SecurityContext loadContext(final HttpRequestResponseHolder requestResponseHolder_) {
        return SecurityContextHolder.createEmptyContext();
    }

    @Override
    public void saveContext(final SecurityContext context_, final HttpServletRequest request_,
            final HttpServletResponse response_) {
    }

    @Override
    public boolean containsContext(final HttpServletRequest request_) {
        return false;
    }

}

이것은 유형 ClassCastException과 관련된 이상한 점 때문에 내 응용 프로그램에서 작동하지 않습니다 response_.

(단순히 세션에 컨텍스트를 저장하지 않음으로써) 작동하는 구현을 찾았다 고 가정하더라도 <http>구성에 의해 구축 된 필터에이를 주입하는 방법에 대한 문제가 여전히 있습니다. 문서 에 따라 단순히 SECURITY_CONTEXT_FILTER위치 에서 필터를 교체 할 수는 없습니다 . 내가 커버 아래에 만들어진 것에 연결하는 유일한 방법 은 못생긴 콩 을 작성하는 것입니다 .SecurityContextPersistenceFilterApplicationContextAware

public class SpringSecuritySessionDisabler implements ApplicationContextAware {

    private final Logger logger = LoggerFactory.getLogger(SpringSecuritySessionDisabler.class);

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(final ApplicationContext applicationContext_) throws BeansException {
        applicationContext = applicationContext_;
    }

    public void disableSpringSecuritySessions() {
        final Map<String, FilterChainProxy> filterChainProxies = applicationContext
                .getBeansOfType(FilterChainProxy.class);
        for (final Entry<String, FilterChainProxy> filterChainProxyBeanEntry : filterChainProxies.entrySet()) {
            for (final Entry<String, List<Filter>> filterChainMapEntry : filterChainProxyBeanEntry.getValue()
                    .getFilterChainMap().entrySet()) {
                final List<Filter> filterList = filterChainMapEntry.getValue();
                if (filterList.size() > 0) {
                    for (final Filter filter : filterList) {
                        if (filter instanceof SecurityContextPersistenceFilter) {
                            logger.info(
                                    "Found SecurityContextPersistenceFilter, mapped to URL '{}' in the FilterChainProxy bean named '{}', setting its securityContextRepository to the null implementation to disable caching of authentication",
                                    filterChainMapEntry.getKey(), filterChainProxyBeanEntry.getKey());
                            ((SecurityContextPersistenceFilter) filter).setSecurityContextRepository(
                             new NullSpringSecurityContextRepository());
                        }
                    }
                }

            }
        }
    }
}

어쨌든 실제로 작동하는 솔루션에 대해 매우 hackish하지만. 일을 할 때 찾는 Filter세션 항목을 삭제하는 a 를 사용하십시오 HttpSessionSecurityContextRepository.

public class SpringSecuritySessionDeletingFilter extends GenericFilterBean implements Filter {

    @Override
    public void doFilter(final ServletRequest request_, final ServletResponse response_, final FilterChain chain_)
            throws IOException, ServletException {
        final HttpServletRequest servletRequest = (HttpServletRequest) request_;
        final HttpSession session = servletRequest.getSession();
        if (session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY) != null) {
            session.removeAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
        }

        chain_.doFilter(request_, response_);
    }
}

그런 다음 구성에서 :

<bean id="springSecuritySessionDeletingFilter"
    class="SpringSecuritySessionDeletingFilter" />

<sec:http auto-config="false" create-session="never"
    entry-point-ref="authEntryPoint">
    <sec:intercept-url pattern="/**"
        access="IS_AUTHENTICATED_REMEMBERED" />
    <sec:intercept-url pattern="/static/**" filters="none" />
    <sec:custom-filter ref="myLoginFilterChain"
        position="FORM_LOGIN_FILTER" />

    <sec:custom-filter ref="springSecuritySessionDeletingFilter"
        before="SECURITY_CONTEXT_FILTER" />
</sec:http>


답변

참고 : “세션 만들기”가 아니라 “세션 만들기”입니다.

세션 생성

HTTP 세션이 생성되는 열의를 제어합니다.

설정하지 않으면 기본값은 “ifRequired”입니다. 다른 옵션은 “항상”및 “사용 안 함”입니다.

이 속성의 설정은 HttpSessionContextIntegrationFilter의 allowSessionCreation 및 forceEagerSessionCreation 속성에 영향을줍니다. allowSessionCreation은이 속성이 “never”로 설정되지 않는 한 항상 true입니다. forceEagerSessionCreation은 “always”로 설정되지 않은 경우 “false”입니다.

따라서 기본 구성은 세션 생성을 허용하지만 강제하지는 않습니다. 예외는 동시 세션 제어가 활성화 된 경우, 여기에있는 설정에 관계없이 forceEagerSessionCreation이 true로 설정 될 때입니다. “never”를 사용하면 HttpSessionContextIntegrationFilter를 초기화하는 동안 예외가 발생합니다.

세션 사용에 대한 자세한 내용은 HttpSessionSecurityContextRepository javadoc에 좋은 문서가 있습니다.