Spring安全性:在servlet过滤器中访问当前经过身份验证的用户
我最近开始学习Spring安全性,今天我提出了一个基本的(我相信)问题:为什么我不能访问Servlet过滤器中的当前主体,如下所示:Spring安全性:在servlet过滤器中访问当前经过身份验证的用户,spring,spring-mvc,servlets,spring-security,Spring,Spring Mvc,Servlets,Spring Security,我最近开始学习Spring安全性,今天我提出了一个基本的(我相信)问题:为什么我不能访问Servlet过滤器中的当前主体,如下所示: package com.acme.test; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletExce
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
}
}
使用Authentication auth=securityContext.getAuthentication()检索的身份验证对象;是空的。虽然在MVC@Controller中使用上述代码段可以正常工作(如预期)
为什么会发生这种情况?您可以看到,为了访问SecurityContext,必须首先使用安全过滤器
如果您询问如何执行,这取决于您配置web应用程序的方式。
在我的例子中,我使用基于Servlet-3配置风格的Spring引导和Java中的Spring上下文配置(无XML)
因此,我的配置如下所示:
@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;
}
}
请注意,通过设置顺序,您可以控制过滤器顺序。内部
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
}
希望这有助于旧的web.xml部署描述符是确定过滤器顺序的简单方法。来自Servlet3.0规范:容器用于构建过滤器链以应用于 具体请求URI如下所示:
过滤器放在Spring security过滤器之后
同样来自同一规范文档:如果侦听器、servlet和过滤器的调用顺序对
应用程序,则必须使用部署描述符。以下代码段起作用,并提供一个
主体
实例:
@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);
}
我将研究spring安全过滤器链,以覆盖默认的spring配置,并将过滤器插入正确的位置。身份验证可能仅在特定位置可用@jpprade谢谢。我最感兴趣的是通用过滤器,所以我不想覆盖安全过滤器。看起来(从下面的答案来看)配置过滤器的顺序是有效的。这确实有效…@mordechai tamam的解决方案也有效。请您解释一下这里发生了什么……我最感兴趣的是了解发生了什么。HttpSessionSecurityContextRepository在请求之间的HttpSession中存储安全上下文。对于每个请求,它都放在一个ThreadLocal中,您可以从那里访问它。这里有@Ralph写的好文章:谢谢你的信息Vipul…有趣的东西。尽管如此,这并不能完全解释为什么SecurityContextHolder.getContext();未获取对包含当前用户的SecurityContext的引用。这是因为我的过滤器在SecurityContextPersistenceFilter之前运行吗?我想我现在已经知道了…在所有过滤器调用之后,看到了SecurityContextPersistenceFilter在链中所有过滤器运行之后对SecurityContextHolder#clearContext()的调用。我将接受这个答案,因为这是我将要使用的。虽然下面的答案对理解正在发生的事情有很大帮助。这很有效,看起来不错。想知道为什么@vipul paralikar的代码片段也起作用。哪种方法是正确的?我的回答演示了如何订购过滤器,以便您能够在FilterChainProxy之前/之后放置其他过滤器。关键是,在调用FilterChainProxy之前(其中一个链实际上正在这样做),安全上下文是空的。因此,我的解决方案适用于您的筛选器与任何与身份验证相关的操作无关的情况,并且仍然依赖于用户已通过身份验证的事实;我没有使用XML配置,但您的回答有助于理解过滤器排序的重要性。
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication != null) {
if (authentication.getPrincipal() instanceof UserDetails) {
UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
return springSecurityUser.getUsername();
} else if (authentication.getPrincipal() instanceof String) {
return (String) authentication.getPrincipal();
}
}
return null;