Spring Security'yi seanslar olmadan nasıl kullanabilirim?


99

Spring Security ile Amazon EC2 üzerinde çalışacak ve Amazon'un Elastic Load Balancer'larını kullanacak bir web uygulaması oluşturuyorum. Maalesef ELB sabit oturumları desteklemiyor, bu nedenle uygulamamın oturumlar olmadan düzgün çalıştığından emin olmam gerekiyor.

Şimdiye kadar, bir tanımlama bilgisi aracılığıyla bir belirteç atamak için RememberMeServices'i kurdum ve bu iyi çalışıyor, ancak tanımlama bilgisinin tarayıcı oturumunda sona ermesini istiyorum (örneğin, tarayıcı kapandığında).

Spring Security'yi seanslar olmadan kullanmak isteyen ilk kişi olmadığımı hayal etmeliyim ... herhangi bir öneriniz var mı?

Yanıtlar:


124

Java Config ile Spring Security 3'te HttpSecurity.sessionManagement () kullanabilirsiniz :

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

2
Bu, Java yapılandırması için doğru yanıttır ve @sappenin'in xml yapılandırması için kabul edilen yanıtla ilgili bir yorumda doğru şekilde belirttiğini yansıtır. Bu yöntemi kullanıyoruz ve aslında uygulamamız oturumsuz.
Paul

Bunun bir yan etkisi var. Tomcat konteyneri görüntü, stil sayfası vb. İsteklerine "; jsessionid = ..." ekleyecektir çünkü Tomcat durum bilgisiz olmayı sevmez ve Spring Security bu varlıkları ilk yüklemede engelleyecektir çünkü "URL bir potansiyel olarak kötü niyetli String ';' ".
workerjoe

@workerjoe Peki, bu java yapılandırmasıyla ne söylemeye çalışıyorsunuz, oturumlar tomcat yerine bahar güvenliği tarafından oluşturulmuyor?
Vishwas Atrey

@VishwasAtrey Benim anlayışıma göre (yanlış olabilir), Tomcat oturumları oluşturur ve sürdürür. Bahar bunlardan yararlanarak kendi verilerini ekliyor. Vatansız bir web uygulaması yapmaya çalıştım ve yukarıda da bahsettiğim gibi çalışmadı. Daha fazlası için kendi sorumun cevabına bakın .
workerjoe

28

Spring Securitiy 3.0'da daha da kolay görünüyor. Ad alanı yapılandırması kullanıyorsanız, şunları yapabilirsiniz:

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

Yoksa boş olarak SecurityContextRepository yapılandırmak olabilir ve hiçbir şey bu şekilde kaydedilmiş alacağı yanı .


5
Bu düşündüğüm gibi işe yaramadı. Bunun yerine, aşağıda "asla" ve "vatansız" arasında ayrım yapan bir yorum var. "Hiçbir zaman" seçeneğini kullanarak uygulamam hâlâ oturumlar oluşturuyordu. "Vatansız" seçeneğini kullandığım için uygulamam aslında durumsuz hale geldi ve diğer yanıtlarda bahsedilen geçersiz kılmaların hiçbirini uygulamama gerek kalmadı. JIRA sorununa buradan bakın: jira.springsource.org/browse/SEC-1424
sappenin

27

Bugün aynı sorun üzerinde (SecurityContextPersistenceFilter'e özel bir SecurityContextRepository enjekte ederek) 4-5 saat boyunca çalıştık. Sonunda anladık. Her şeyden önce, Bahar Güvenliği ref. 8.3 bölümünde. doc, bir SecurityContextPersistenceFilter bean tanımı var

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

Ve bu tanımın ardından şu açıklama var: "Alternatif olarak, istek sırasında bir oturum zaten oluşturulmuş olsa bile, güvenlik bağlamının depolanmasını önleyecek olan SecurityContextRepository arabiriminin boş bir uygulamasını sağlayabilirsiniz."

Özel SecurityContextRepository'mizi SecurityContextPersistenceFilter'e enjekte etmemiz gerekiyordu. Bu yüzden yukarıdaki fasulye tanımını özel uygulamamızla değiştirdik ve güvenlik bağlamına koyduk.

Uygulamayı çalıştırdığımızda, günlükleri izledik ve SecurityContextPersistenceFilter'ın bizim özel impl'mizi kullanmadığını, HttpSessionSecurityContextRepository kullandığını gördük.

Denediğimiz birkaç başka şeyden sonra, özel SecurityContextRepository impl'imize "http" ad alanının "security-context-repository-ref" özniteliğini vermemiz gerektiğini anladık. "Http" ad alanını kullanıyorsanız ve kendi SecurityContextRepository uygulamanızı enjekte etmek istiyorsanız, "security-context-repository-ref" özniteliğini deneyin.

"Http" ad alanı kullanıldığında, ayrı bir SecurityContextPersistenceFilter tanımı yok sayılır. Yukarıda kopyaladığım gibi, referans dokümanı. bunu belirtmiyor.

Yanlış anladıysam lütfen beni düzeltin.


Teşekkürler, bu değerli bir bilgidir. Başvurumda deneyeceğim.
Jeff Evans

Teşekkürler, 3.0 baharında ihtiyacım olan buydu
Justin Ludwig

1
Http ad alanının özel bir SecurityContextPersistenceFilter'e izin vermediğini söylediğinizde oldukça doğrusunuz, bunu
anlamam

Bunu gönderdiğiniz için çok teşekkür ederim! Sahip olduğum küçük saçlarımı çıkarmak üzereydim. SecurityContextPersistenceFilter'ın setSecurityContextRepository yönteminin neden kullanımdan kaldırıldığını merak ediyordum (belgeler, yapıcı enjeksiyonu kullanmayı söylüyor, ki bu da doğru değil).
fool4jesus

10

SecurityContextPersistenceFilterSınıfa bir göz atın . SecurityContextHolderNüfusun nasıl olduğunu tanımlar . Varsayılan olarak, HttpSessionSecurityContextRepositorygüvenlik bağlamını http oturumunda depolamak için kullanır .

Bu mekanizmayı özel olarak oldukça kolay uyguladım SecurityContextRepository.

Aşağıya securityContext.xmlbakın:

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

1
Merhaba Lukas, güvenlik bağlamı deposu uygulamanızla ilgili daha fazla ayrıntı verebilir misiniz?
Jim Downing

1
sınıf TokenSecurityContextRepository, HashMap <String, SecurityContext> contextMap içerir. LoadContext () yönteminde, requestParameter sid veya tanımlama bilgisi veya özel requestHeader veya yukarıdakilerin kombinasyonu tarafından geçirilen oturum karma kodu için SecurityContext'in var olup olmadığını kontrol eder. Bağlam çözümlenemezse SecurityContextHolder.createEmptyContext () öğesini döndürür. Yöntem saveContext, çözümlenmiş bağlamı contextMap'e koyar.
Lukas Herman

8

Aslında create-session="never"tamamen vatansız olmak anlamına gelmez. Spring Security sorun yönetiminde bununla ilgili bir sorun var .


3

Bu yanıtta yayınlanan sayısız çözümle uğraştıktan sonra, <http>ad alanı yapılandırmasını kullanırken çalışan bir şey elde etmeye çalıştıktan sonra , nihayet kullanım durumum için gerçekten işe yarayan bir yaklaşım buldum. Aslında Spring Security'nin bir oturum başlatmamasını istemiyorum (çünkü uygulamanın diğer bölümlerinde oturum kullanıyorum), sadece oturumdaki kimlik doğrulamasını hiç "hatırlamıyor" (yeniden kontrol edilmelidir) her istek).

Öncelikle, yukarıda açıklanan "boş uygulama" tekniğini nasıl yapacağımı çözemedim. SecurityContextRepository'yi nullişlemsiz bir uygulamaya mı ayarlamanız gerektiği açık değildi . İlki işe yaramıyor çünkü içine bir NullPointerExceptionatılıyor SecurityContextPersistenceFilter.doFilter(). Operasyonsuz uygulamaya gelince, hayal edebileceğim en basit şekilde uygulamayı denedim:

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

}

Bunun nedeni bazı garip, benim uygulamada çalışmıyor ClassCastExceptionile yapmak zorunda response_tip.

Çalışan bir uygulama bulmayı başardığımı varsayarsak bile (bağlamı oturumda saklamayarak), bunun <http>konfigürasyon tarafından oluşturulan filtrelere nasıl enjekte edileceği sorunu hala var . Dokümanlara göreSECURITY_CONTEXT_FILTER , filtreyi pozisyonda kolayca değiştiremezsiniz . Kapakların altında yaratılana bağlanmanın tek yolu çirkin bir fasulye yazmaktı :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());
                        }
                    }
                }

            }
        }
    }
}

Her neyse, gerçekten işe yarayan çözüme, çok hileli de olsa. Basitçe , işini yaptığında aradığı Filteroturum girişini silen bir kullanın 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_);
    }
}

Ardından konfigürasyonda:

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

Dokuz yıl sonra, bu hala doğru cevap. Artık XML yerine Java yapılandırmasını kullanabiliriz. WebSecurityConfigurerAdapter" http.addFilterBefore(new SpringSecuritySessionDeletingFilter(), SecurityContextPersistenceFilter.class)"
İle

3

Kısa bir not: Bu, "oturum oluşturma" yerine "oturum oluşturma" dır

oturum oluştur

Bir HTTP oturumunun oluşturulma isteğini kontrol eder.

Ayarlanmazsa, varsayılan olarak "ifRequired" olur. Diğer seçenekler "her zaman" ve "asla" dır.

Bu özniteliğin ayarı, HttpSessionContextIntegrationFilter'ın allowSessionCreation ve forceEagerSessionCreation özelliklerini etkiler. allowSessionCreation, bu öznitelik "asla" olarak ayarlanmadıkça her zaman doğru olacaktır. forceEagerSessionCreation, "her zaman" olarak ayarlanmadıkça "false" olur.

Bu nedenle varsayılan yapılandırma oturum oluşturmaya izin verir ancak bunu zorlamaz. Buradaki ayarın ne olduğuna bakılmaksızın forceEagerSessionCreation true olarak ayarlanacağı zaman, eşzamanlı oturum kontrolünün etkinleştirilmesi istisnadır. "Never" kullanılması, HttpSessionContextIntegrationFilter'ın başlatılması sırasında bir istisnaya neden olur.

Oturum kullanımının belirli ayrıntıları için, HttpSessionSecurityContextRepository javadoc'da bazı iyi belgeler bulunmaktadır.


Bunların hepsi harika cevaplar, ancak <http> yapılandırma öğesini kullanarak bunu nasıl başaracağımı bulmaya çalışırken kafamı duvara dayadım. Bununla bile auto-config=false, görünüşe göre SECURITY_CONTEXT_FILTERpozisyonda olanı kendi pozisyonunuzla değiştiremezsiniz . Biraz ApplicationContextAwarefasulye ile devre dışı bırakmaya çalışırken ( securityContextRepositoryboş bir uygulamaya zorlamak için yansıma kullanarak SessionManagementFilter) ama zar olmadan etrafta dolaşıyorum . Ve ne yazık ki, ilkbahar güvenliği olan 3.1 yılına geçemiyorum create-session=stateless.
Jeff Evans

Lütfen bu siteyi ziyaret edin, her zaman bilgilendirici. Umarım bu size ve diğerlerine yardımcı olur " baeldung.com/spring-security-session " • her zaman - zaten mevcut değilse bir oturum her zaman oluşturulur • ifRequired - yalnızca gerekirse bir oturum oluşturulur (varsayılan) • asla - çerçeve hiçbir zaman kendi başına bir oturum oluşturmayacak, ancak zaten varsa bir oturum kullanacaktır • durumsuz - Spring Security tarafından hiçbir oturum oluşturulmayacak veya kullanılmayacak
Java_Fire_With içinde
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.