2014-09-30 39 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); 
} 
相关问题