2014-09-30 77 views
8

我最近開始學習Spring Security,今天我介紹了這個基本的(我相信)問題:爲什麼我無法像Servlet Filter那樣訪問Servlet Filter中的當前Principal?在下面的類:Spring Security:在servlet中訪問當前經過身份驗證的用戶Filter

package com.acme.test; 

import java.io.IOException; 

import javax.servlet.Filter; 
import javax.servlet.FilterChain; 
import javax.servlet.FilterConfig; 
import javax.servlet.ServletException; 
import javax.servlet.ServletRequest; 
import javax.servlet.ServletResponse; 

import org.springframework.security.core.Authentication; 
import org.springframework.security.core.context.SecurityContext; 
import org.springframework.security.core.context.SecurityContextHolder; 
import org.springframework.stereotype.Component; 

@Component 
public class TestFilter implements Filter { 

    /* 
    * (non-Javadoc) 
    * 
    * @see javax.servlet.Filter#init(javax.servlet.FilterConfig) 
    */ 
    @Override 
    public void init(FilterConfig filterConfig) throws ServletException { 
     // TODO Auto-generated method stub 

    } 

    /* 
    * (non-Javadoc) 
    * 
    * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, 
    * javax.servlet.ServletResponse, javax.servlet.FilterChain) 
    */ 
    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
      throws IOException, ServletException { 

     SecurityContext securityContext = SecurityContextHolder.getContext(); 
     Authentication auth = securityContext.getAuthentication(); 

     // auth is null here 

     chain.doFilter(request, response); 
    } 

    /* 
    * (non-Javadoc) 
    * 
    * @see javax.servlet.Filter#destroy() 
    */ 
    @Override 
    public void destroy() { 
     // TODO Auto-generated method stub 

    } 

} 

認證對象與認證AUTH = securityContext.getAuthentication()取回;爲空。在MVC @Controller中使用上面的代碼片段工作得很好(如預期的那樣)。

這是怎麼發生的?

+0

我想看看春季安全過濾器鏈,覆蓋默認的Spring配置,並在適當位置插入過濾器。身份驗證可能只能在某個位置http://docs.spring.io/spring-security/site/docs/3.1.x/reference/security-filter-chain.html – jpprade 2014-09-30 20:46:00

+0

@jpprade謝謝。我最感興趣的是泛型過濾器,所以我不想重寫安全過濾器。看起來(從下面的答案),配置過濾器的順序工作。 – dimi 2014-10-01 08:30:18

回答

16

doFilter

HttpServletRequest request = (HttpServletRequest) request; 
HttpSession session = request.getSession(false); 

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

if (sci != null) { 
     UserDetails cud = (UserDetails) sci.getAuthentication().getPrincipal(); 
     // do whatever you need here with the UserDetails 
} 

希望這有助於

+0

確實有效...... @ mordechai-tamam的解決方案也有效。你能解釋一下這裏發生了什麼......我主要是想了解發生了什麼。 – dimi 2014-10-01 08:19:58

+0

HttpSessionSecurityContextRepository將請求之間的安全上下文存儲在HttpSession中。並且對於每個請求,它都將放置在您訪問它的ThreadLocal中。這裏有很好的@Ralph寫的:http://stackoverflow.com/questions/6408007/spring-securitys-securitycontextholder-session-or-request-bound – 2014-10-01 09:15:11

+0

感謝信息Vipul ..有趣的東西。儘管如此,這並不能完全解釋爲什麼SecurityContextHolder.getContext();沒有獲得對包含當前用戶的SecurityContext的引用。這是因爲我的過濾器在SecurityContextPersistenceFilter之前運行? – dimi 2014-10-01 13:18:29

2

您可以看到here,即要訪問SecurityContext,必須先安全過濾器。

如果您問的是如何去做,這取決於您配置Web應用程序的方式。 就我而言,我使用Spring的啓動,基於Servlet的3配置的方式,並在Java中(無XML)Spring上下文配置 所以,我的配置是這樣的:

@Configuration 
@EnableWebMvc 
@EnableWebMvcSecurity 
public class WebCtxConfig extends WebMvcConfigurerAdapter { 

    @Autowired 
    ApplicationContext ctx; 


    @Bean 
    FilterRegistrationBean springSecurityFilter() { 
     FilterChainProxy o = (FilterChainProxy) ctx 
       .getBean(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME); 
     FilterRegistrationBean trVal = new FilterRegistrationBean(); 
     trVal.setFilter(o); 
     trVal.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE + 1); 
     return trVal; 
    } 

    @Bean 
    public FilterRegistrationBean applicationContextIdFilter(final IThreadLifecycleManager threadLifecycleManager) { 
     FilterRegistrationBean retVal = new FilterRegistrationBean(); 
     YourFilter filter = new YourFilter(); 
     retVal.setFilter(filter); 
     retVal.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE + 2); 
     return retVal; 
    } 
} 

注意,由設置順序,您可以控制過濾器順序。

+0

這工作,看起來不錯。想知道爲什麼@ vipul-paralikar的片段也有效。哪種方法是正確的? – dimi 2014-10-01 08:23:54

+0

我的答案,演示了您可以訂購過濾器的方式,以便您可以在FilterChainProxy之前/之後放置其他過濾器。重點是在FilterChainProxy被調用之前(其中一個鏈實際上正在執行),安全上下文是空的。因此,我的解決方案適用於您的篩選器與任何與身份驗證相關的操作無關的情況,並且依賴於用戶已經通過身份驗證的事實。 – Modi 2014-10-02 04:19:10

2

好老web.xml部署描述符是一個簡單的方法來確定的過濾器的順序。從Servlet 3.0規範:在構建的過濾器被應用於一個 特定請求URI被如下鏈中的容器使用的順序:

  1. 首先,匹配濾波器中相同的順序映射這些 元素出現在部署描述符中。
  2. 接下來,匹配過濾器映射的順序與這些 元素出現在部署描述符中的順序相同。

總之,你必須把<filter-mapping>爲你過濾後的一個春季安全。

同樣來自同一規範文檔:如果調用監聽器,servlet,過濾器的順序對於應用程序很重要,那麼必須使用部署描述符。

+0

謝謝;我沒有使用XML配置,但您的答案有助於理解過濾器排序的重要性。 – dimi 2014-10-01 13:33:28

3

下面的代碼片段的作品,並提供了一個Principal實例:

@Override 
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
     throws IOException, ServletException { 
    HttpServletRequest req = (HttpServletRequest) request; 

    Principal principal = req.getUserPrincipal(); 

    if (principal != null) { 
     // do something with the Principal 
    } 

    chain.doFilter(request, response); 
} 
相關問題