java - Spring Security getAuthenticationManager() 在自定义过滤器中返回 null
问题描述
我正在尝试在 Spring 中实现一个非常简单的自定义身份验证过程示例,以更好地理解该概念。
我以为我现在已经准备好了所有东西,但是发送一个请求来测试我实现的结果会导致 NullPointerException,这可以追溯到 this.getAuthenticationManager()在我的自定义过滤器中返回 null。但我不明白为什么。不幸的是,非常相似的现有问题并没有真正帮助我。所以我会感谢你的帮助;这是我认为最相关的课程,请随时询问是否需要更多课程。
MyAuthenticationFilter(基于 UsernamePasswordAuthenticationFilter 的源代码),最后一行发生错误:
public class MyAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public MyAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String username = request.getParameter("username");
String password = request.getParameter("password");
String secondSecret = request.getParameter("secondSecret");
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
MyAuthenticationToken authRequest = new MyAuthenticationToken(username, new MyCredentials(password, secondSecret));
return this.getAuthenticationManager().authenticate(authRequest);
}
}
我的配置类:
@Configuration
@EnableWebSecurity
@EnableWebMvc
@ComponentScan
public class AppConfig extends WebSecurityConfigurerAdapter {
@Autowired
MyAuthenticationProvider myAuthenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(myAuthenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new MyAuthenticationFilter(), BasicAuthenticationFilter.class)
.authorizeRequests().antMatchers("/**")
.hasAnyRole()
.anyRequest()
.authenticated()
.and()
.csrf().disable();
}
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
我的身份验证提供者:
@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
MyAuthenticationToken myAuthenticationToken = (MyAuthenticationToken) authentication;
MyCredentials credentials = (MyCredentials) myAuthenticationToken.getCredentials();
if (credentials.getPassword().equals("sesamOeffneDich") && credentials.getSecondSecret().equals(MyAuthenticationToken.SECOND_SECRET)) {
myAuthenticationToken.setAuthenticated(true);
return myAuthenticationToken;
} else {
throw new BadCredentialsException("Bad credentials supplied!");
}
}
@Override
public boolean supports(Class<?> authentication) {
return MyAuthenticationToken.class.isAssignableFrom(authentication);
}
}
解决方案
为什么您会看到 NullPointerException
您看到 a 是NullPointerException
因为您的过滤器没有AuthenticationManager
接线。根据javadocsAbstractAuthenticationProcessingFilter
过滤器要求您设置 authenticationManager 属性。需要一个 AuthenticationManager 来处理由实现类创建的身份验证请求令牌
在这种情况下,我确实对为什么authenticationManager
没有为这个抽象过滤器的构造函数参数进行切割而摸不着头脑。我建议在您的构造函数中为您的自定义过滤器强制执行此操作。
public MyAuthenticationFilter(AuthenticationManager authenticationManager) {
super(new AntPathRequestMatcher("/login", "POST"));
this.setAuthenticationManager(authenticationManager);
}
AuthenticationManager 或 AuthenticationProvider
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(myAuthenticationProvider);
}
将AuthenticationManagerBuilder
创建一个ProviderManager (an AuthenticationManager)
.
这ProviderManager
是一个集合,AuthenticationProvider
并将尝试使用它管理authenticate()
的每一个。AuthenticationProvider
(这是合同public boolean supports(Class<?> authentication)
中极其重要的地方)AuthenticationProvider
在您的配置中,您创建了一个ProviderManager
仅包含您的自定义Authentication Provider
凉爽的。现在我的 AuthenticationManager 在哪里?
可以AuthenticationManager
通过在configure()
_ this.authenticationManager()
WebSecurityConfigurerAdapter
@Bean
public AuthenticationManager authenticationManager throws Exception() {
this.authenticationManager();
}
建议
创建你自己的ProviderManager
确实有它的好处,因为它是明确的并且在你的控制范围内。
@Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(myAuthenticationProvider));
}
这将使您可以灵活地放置AuthenticationManager
bean 并避免:
- bean 配置中潜在的循环依赖问题
Exception
由于调用而冒泡检查this.authenticationManager()
把它们放在一起
...
public class AppConfig extends WebSecurityConfigurerAdapter {
@Autowired
MyAuthenticationProvider myAuthenticationProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new MyAuthenticationFilter(authenticationManager()), BasicAuthenticationFilter.class)
...
}
@Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(myAuthenticationProvider));
}
}
推荐阅读
- javascript - 为什么对对象 setState 使用 ES6 计算属性语法?
- python - 移动平均线计算不正确
- kubernetes - Kubernetes 内的 systemd 服务无法获取环境
- asp.net-web-api - 如何在 .Net Core 2.2 中使用 IHttpClientFactory 下载文件?
- postgresql - 提高选择查询的性能
- reactjs - 未调度的操作
- reactjs - React-admin:如何将更多参数传递给 GET_LIST 类型的 dataProvider
- java - 即使 onDestroy 被解雇,我的服务也不会停止
- python - 如何在python中标记列表列表
- php - 如何修复mysql中的“外键约束失败”