Я пытаюсь реализовать очень простой пример настраиваемого процесса аутентификации в Spring, чтобы лучше понять концепцию.
Я думал, что у меня все на месте, но отправка запроса на проверку того, что я реализовал, приводит к исключению NullPointerException, которое можно отследить до this.getAuthenticationManager () возвращает ноль в моем настраиваемом фильтре. Но я не понимаю почему. К сожалению, очень похожий существующий вопрос мне не помог. Так что я был бы благодарен за помощь; я думаю, вот самые актуальные классы, не стесняйтесь спрашивать, нужны ли еще какие-нибудь.
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;
}
}
MyAuthenticationProvider:
@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);
}
}
Я думаю, что OP хочет, чтобы его authenticate удостоился чести.
Добавил ответ, надеюсь он не слишком плотный




Вы должны установить диспетчер аутентификации для своего фильтра. Вы можете сделать это, добавив этот метод в свой класс конфигурации
@Bean
public MyAuthenticationFilter myAuthenticationFilter() {
MyAuthenticationFilter res = new MyAuthenticationFilter();
try {
res.setAuthenticationManager(authenticationManagerBean());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return res;
}
и изменив метод настройки с помощью
http.addFilterBefore(myAuthenticationFilter(), BasicAuthenticationFilter.class). ...
Почему вы видите исключение NullPointerException
Вы видите NullPointerException, потому что у вас нет AuthenticationManager, подключенного к вашему фильтру. Согласно javadocs для AbstractAuthenticationProcessingFilter
The filter requires that you set the authenticationManager property. An AuthenticationManager is required to process the authentication request tokens created by implementing classes
В этом случае я почесываю в затылке, почему 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));
}
Это позволит вам гибко подходить к размещению вашего bean-компонента AuthenticationManager и избежать:
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));
}
}
Дополнительная точка отсчета: AuthenticationManager против AuthenticationProvider
Большое спасибо! Я, очевидно, не уделил достаточно внимания при чтении документа, но все же ваш ответ, вероятно, был необходим, поскольку я не знал бы подходящего места, чтобы собрать все вместе. Как я вижу в документе, ProviderManager - единственная реализация для AuthenticationManager; имеет ли смысл для моего примера написать собственную реализацию AuthenticationManager или это довольно необычно? Я, кстати, всегда благодарен за дополнительные рекомендации, как вы написали; это помогает установить лучшее контекстное знание, так что спасибо за это.
Учитывая, что у вас только один AuthenticationProvider, у вас есть возможность предоставить реализацию AuthenticationManager. ProviderManager предназначен для работы по разным стратегиям.
Понятно, хотя это не кажется чрезмерно необходимым; Спасибо!
Что если добавить в свой конструктор
setAuthenticationManager(new NoOpAuthenticationManager());?