복붙노트

[SPRING] 세션없이 Spring Security를 ​​사용하려면 어떻게해야합니까?

SPRING

세션없이 Spring Security를 ​​사용하려면 어떻게해야합니까?

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

지금까지 쿠키를 통해 토큰을 할당하도록 RememberMeServices를 설정했지만 제대로 작동하지만 브라우저가 닫힐 때 쿠키가 만료되도록하고 싶습니다.

세션없이 스프링 보안을 사용하고자하는 첫 번째 사람이 아니라는 것을 상상해보아야합니다.

해결법

  1. ==============================

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

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

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

    2.우리는 오늘 4-5 시간 동안 동일한 문제 (SecurityContextPersistenceFilter에 사용자 정의 SecurityContextRepository 주입하기)를 수행했습니다. 마침내, 우리는 그것을 알아 냈다. 우선, Spring Security ref. 8.3 절에서 doc에는 SecurityContextPersistenceFilter bean 정의가 있습니다.

    우리는 오늘 4-5 시간 동안 동일한 문제 (SecurityContextPersistenceFilter에 사용자 정의 SecurityContextRepository 주입하기)를 수행했습니다. 마침내, 우리는 그것을 알아 냈다. 우선, Spring Security ref. 8.3 절에서 doc에는 SecurityContextPersistenceFilter bean 정의가 있습니다.

    <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에 삽입해야했습니다. 그래서 위의 bean 정의를 우리의 커스텀 impl로 간단히 변경하고 보안 컨텍스트에 넣기 만하면됩니다.

    응용 프로그램을 실행할 때 로그를 추적하여 SecurityContextPersistenceFilter가 HttpSessionSecurityContextRepository를 사용하는 사용자 지정 impl을 사용하지 않는 것을 확인했습니다.

    우리가 시도한 다른 몇 가지 작업을 수행 한 후에 우리는 "http"네임 스페이스의 "security-context-repository-ref"특성을 가진 사용자 지정 SecurityContextRepository impl을 제공해야한다는 것을 알았습니다. "http"네임 스페이스를 사용하고 자신의 SecurityContextRepository impl을 삽입하려면 "security-context-repository-ref"특성을 시도하십시오.

    "http"네임 스페이스가 사용되면 별도의 SecurityContextPersistenceFilter 정의가 무시됩니다. 위에서 복사 한 것처럼 참조 문서입니다. 그런 말은하지 않습니다.

    내가 잘못한 것을 수정하면 나를 바로 잡으십시오.

  3. ==============================

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

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

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

    또는 SecurityContextRepository를 null로 구성하면 아무 것도 저장되지 않습니다.

  4. ==============================

    4.SecurityContextPersistenceFilter 클래스를 살펴보십시오. SecurityContextHolder가 채워지는 방법을 정의합니다. 기본적으로 HttpSessionSecurityContextRepository를 사용하여 http 세션에 보안 컨텍스트를 저장합니다.

    SecurityContextPersistenceFilter 클래스를 살펴보십시오. SecurityContextHolder가 채워지는 방법을 정의합니다. 기본적으로 HttpSessionSecurityContextRepository를 사용하여 http 세션에 보안 컨텍스트를 저장합니다.

    사용자 지정 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>
    
  5. ==============================

    5.사실 create-session = "never"는 완전히 무국적 인 것을 의미하지 않습니다. 스프링 보안 문제 관리에서 문제가 있습니다.

    사실 create-session = "never"는 완전히 무국적 인 것을 의미하지 않습니다. 스프링 보안 문제 관리에서 문제가 있습니다.

  6. ==============================

    6.간단한 참고 사항 : "create-sessions"보다는 "create-session"

    간단한 참고 사항 : "create-sessions"보다는 "create-session"

    세션 생성

    HTTP 세션이 만들어지는 열의를 제어합니다.

    설정되지 않은 경우 기본값은 "ifRequired"입니다. 다른 옵션은 "항상"과 "결코"입니다.

    이 속성의 설정은 HttpSessionContextIntegrationFilter의 allowSessionCreation 및 forceEagerSessionCreation 속성에 영향을줍니다. allowSessionCreation은이 속성이 "never"로 설정되어 있지 않으면 항상 true입니다. forceEagerSessionCreation은 "always"로 설정되어 있지 않으면 "false"입니다.

    따라서 기본 구성에서는 세션 생성이 가능하지만 강제로 실행하지는 않습니다. 예외는 동시 세션 제어가 활성화 된 경우입니다. 여기에 설정에 관계없이 forceEagerSessionCreation이 true로 설정됩니다. "never"를 사용하면 HttpSessionContextIntegrationFilter를 초기화하는 동안 예외가 발생합니다.

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

  7. ==============================

    7.이 답변에 게시 된 수많은 솔루션으로 고생 한 후 네임 스페이스 구성을 사용할 때 작동하는 것을 얻으려고 마침내 실제로 사용 사례에 맞는 접근 방식을 발견했습니다. 나는 Spring Security가 세션을 시작하지 않을 것을 요구하지는 않는다 (세션의 다른 부분에서 세션을 사용하기 때문에), 세션에서 인증을 "기억하지"않는다. (다시 점검해야한다. 모든 요청).

    이 답변에 게시 된 수많은 솔루션으로 고생 한 후 네임 스페이스 구성을 사용할 때 작동하는 것을 얻으려고 마침내 실제로 사용 사례에 맞는 접근 방식을 발견했습니다. 나는 Spring Security가 세션을 시작하지 않을 것을 요구하지는 않는다 (세션의 다른 부분에서 세션을 사용하기 때문에), 세션에서 인증을 "기억하지"않는다. (다시 점검해야한다. 모든 요청).

    우선, 위에서 설명한 "null 구현"기술을 수행하는 방법을 파악할 수 없었습니다. securityContextRepository를 null로 설정했는지 아니면 no-op 구현으로 설정해야하는지는 분명하지 않았습니다. SecurityContextPersistenceFilter.doFilter () 내에서 NullPointerException이 발생하기 때문에 전자는 작동하지 않습니다. 노 작업 구현에 관해서는, 내가 상상할 수있는 가장 간단한 방법으로 구현하려고 시도했다.

    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_ type과 관련되어 있기 때문입니다.

    (세션에서 컨텍스트를 단순히 저장하지 않음으로써) 작동하는 구현을 찾을 수 있다고 가정하더라도 구성에 의해 작성된 필터에이를 삽입하는 방법에 대한 문제가 여전히 남아 있습니다. 문서별로 SECURITY_CONTEXT_FILTER 위치에서 필터를 간단히 바꿀 수는 없습니다. 커버 아래에 생성 된 SecurityContextPersistenceFilter에 연결하는 유일한 방법은 추악한 ApplicationContextAware 빈을 작성하는 것이 었습니다.

    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. 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>
    
  8. from https://stackoverflow.com/questions/2504590/how-can-i-use-spring-security-without-sessions by cc-by-sa and MIT license