Форум программистов, компьютерный форум, киберфорум
Java: Spring, Spring Boot
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.71/7: Рейтинг темы: голосов - 7, средняя оценка - 4.71
3 / 3 / 1
Регистрация: 25.12.2014
Сообщений: 63

Autowired менеджера аутентификации в Spring Security Filter

15.10.2018, 09:49. Показов 1630. Ответов 8

Студворк — интернет-сервис помощи студентам
Доброго дня!
Интегрирую токеновую аутентификацию через Spring Security в уже существующий проект на Spring+Hibernate с конфигом в xml.

Создал Entity для User, потом DAO, потом Service.
Затем я создал фильтр и применил его через web.xml:

XML
1
2
3
4
5
6
7
8
9
<filter>
    <filter-name>springSecurityTokenFilter</filter-name>
    <filter-class>com.ivt.da.security.TokenAuthenticationFilter</filter-class>
</filter>
 
<filter-mapping>
    <filter-name>springSecurityTokenFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
Код фильтра:
Кликните здесь для просмотра всего текста

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class TokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
 
public TokenAuthenticationFilter() {
    super("/**");
    this.setAuthenticationManager(new TokenAuthenticationManager()); // <-------- видимо, проблема тут
    setAuthenticationSuccessHandler((request, response, authentication) ->
    {
        SecurityContextHolder.getContext().setAuthentication(authentication);
        request.getRequestDispatcher(request.getServletPath() + request.getPathInfo()).forward(request, response);
    });
    setAuthenticationFailureHandler((request, response, authenticationException) -> {
        response.getOutputStream().print(authenticationException.getMessage());
    });
}
 
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
        throws AuthenticationException, IOException, ServletException {
    String token = request.getHeader("token");
    if (token == null)
        token = request.getParameter("token");
    if (token == null) {
        TokenAuthentication authentication = new TokenAuthentication(null);
        authentication.setAuthenticated(false);
        return authentication;
    }
    TokenAuthentication tokenAuthentication = new TokenAuthentication(token);
    Authentication authentication = getAuthenticationManager().authenticate(tokenAuthentication);
    return authentication;
}
 
@Override
public void doFilter(ServletRequest req, ServletResponse res,
                     FilterChain chain) throws IOException, ServletException {
    super.doFilter(req, res, chain);
}



Проблема заключается в необходимость задать кастомный Authentication Manager. Созданный кастомный менеджер TokenAuthenticationManager должен обращаться к UserService, который требует @Autowired.

Код TokenAuthenticationManager
Кликните здесь для просмотра всего текста
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@Service
public class TokenAuthenticationManager implements AuthenticationManager {
 
@Autowired
private UserService usersService;
 
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    try {
        if (authentication instanceof TokenAuthentication) {
            TokenAuthentication readyTokenAuthentication = processAuthentication((TokenAuthentication) authentication);
            return readyTokenAuthentication;
        } else {
            authentication.setAuthenticated(false);
            return authentication;
        }
    } catch (Exception ex) {
        if(ex instanceof AuthenticationServiceException)
            throw ex;
    }
    return null;
}
 
private TokenAuthentication processAuthentication(TokenAuthentication authentication) throws AuthenticationException {
    String token = authentication.getToken();
    String key = Base64.getEncoder().encodeToString("here gonna be a secret key".getBytes());
    DefaultClaims claims;
    // ...
    // tokenParsingThings
    // ...
    Date expiredDate = new Date(claims.get("token_expiration_date", Long.class));
    if (expiredDate.after(new Date()))
        return buildFullTokenAuthentication(authentication, claims);
    else
        throw new AuthenticationServiceException("Token expired date error");
}
 
@Transactional
public TokenAuthentication buildFullTokenAuthentication(TokenAuthentication authentication, DefaultClaims claims) {
    User user = usersService.loadUserByUsername(claims.get("USERNAME", String.class)); // <--- NPE, потому что сервис не заинжектился
    if (user.isEnabled()) {
        Role role = user.getRole();
        List<String> roles = new ArrayList<>();
        roles.add(role.getRole());
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority(role.getRole()));
        TokenAuthentication fullTokenAuthentication =
                new TokenAuthentication(authentication.getToken(), authorities, true, user);
        return fullTokenAuthentication;
    } else {
        throw new AuthenticationServiceException("User disabled");
    }
}


Кажется, проблема в том, что я выполнил setAuthenticationManager в фильтре, инициализировав сам менеджер оператором new, следствием чего является неработающий @Autowired. С другой стороны, я не могу сделать что-то типа:
Java
1
2
3
4
5
public class TokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
 
@Autowired
AuthenticationManager authenticationManager;
//...
, потому что фильтры не позволяют использовать внутри себя эту аннотацию.

Но и объект userService, который является null в такой ситуации, не может быть разыменован в new UserServiceImpl, потому что содержит внутри себя ссылку на DAO (которая тоже Autowired), а DAO — на сессию, и так далее.

Можно ли как-то решить проблему доступа к TokenAuthenticationManager в фильтре? Может, можно как-то передать его в конструктор, или передать как-то через конфигурацию, чтобы внутри этого менеджера UserService не был null. Помогите, пожалуйста.

Заранее спасибо!
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
15.10.2018, 09:49
Ответы с готовыми решениями:

[Spring Security] Отловить баг в аутентификации
Ничего не менял в конфигах SS, но перестали подходить валидные пары username - password. Кидает на ту же страницу(логина) с error'ом. В...

Spring на клиенте и Autowired
У меня есть спринг на клиенте (так уж получилось), в spring-config есть список бинов и есть фабрика которая их один раз грузит и раздаёт...

Spring Autowired Annotation
Ребята, че за нафиг, как это вообще работает?! Стандартный пример, напечатать HELLO_WORLD, для этого: 1. Класс MessageProvider ...

8
Эксперт Java
 Аватар для KEKCoGEN
2399 / 2224 / 565
Регистрация: 28.12.2010
Сообщений: 8,672
15.10.2018, 10:30
Цитата Сообщение от unit37 Посмотреть сообщение
Проблема заключается в необходимость задать кастомный Authentication Manager.
Ошибка в этой строке.
Ненадо задавать кастомный менеджер. Надо создать кастомный AuthenticationProvider который будет выоплнять авторизацию по токену и добавить его как один из провайдеров в менеджер.
1
3 / 3 / 1
Регистрация: 25.12.2014
Сообщений: 63
15.10.2018, 20:40  [ТС]
KEKCoGEN, спасибо за ответ!

Я погуглил про AuthProvider, и практически не понял конструктивную разницу между ними. Моя реализация менеджера очень напоминала примеры реализации кастомных провайдеров, поэтому я так и не понял, как именно они должны работать вместе и как их подружить.

Во всех этих примерах вместо кастомного фильтра на запросы в web.xml прописывается такое:
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
<listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
 
<filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
 
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
Однако, тогда я получаю Duplicate Filter registration for 'springSecuirtyFilterChain', хотя, кажется, не имею его двойной инициализации (нет какого-то Java-конфига, поиск по этому слову в проекте тоже ничего не даёт).

Кажется, я совсем запутался.

Если не затруднит, детализируйте, пожалуйста, что я должен сделать? Правильно ли я понял, что @Autowired юзер-сервиса должен жить в кастомном провайдере? А для чего тогда менеджер? И как правильно сконфигурировать всё это добро, не используя Java config классы?
0
Эксперт Java
 Аватар для KEKCoGEN
2399 / 2224 / 565
Регистрация: 28.12.2010
Сообщений: 8,672
15.10.2018, 21:52
unit37, нет смысла городить ещё один менеджер. Это избытычно. Провайдер как раз для этих целей. Насчет фильтра я через XML уже несколько лет не конфигурировал поэтому непомню, но вполне может помочь повесить @Component на фильтр. Тогда туда можно будет инжектить менеджер.
Впринципе контекст спринга можно получить и не из бина, но это будет костыль поэтому пример приводить не буду.
1
3 / 3 / 1
Регистрация: 25.12.2014
Сообщений: 63
15.10.2018, 23:58  [ТС]
KEKCoGEN, спасибо вам, но я всё ещё не понимаю, каким способом решить проблему.
Дело в том, что если я вешаю @Component на фильтр, то @Autowired по-прежнему не работает для менеджера внутри фильтра. В то же время, если убрать из web.xml всё, что касается фильтра, повесить @Component, а менеджер передавать в конструкторе с аннотацией @Autowired, то он перестанет быть null, но тогда, естественно, фильтр не применяется:

Java
1
public TokenAuthenticationFilter(@Autowired AuthenticationManager authenticationManager) { .. }
Оставить такой конструктор с определением фильтра в web.xml тоже не получится, потому что конструктор должен не иметь параметров.

Получается, что я либо ломаю одно, либо другое.
0
Эксперт Java
 Аватар для KEKCoGEN
2399 / 2224 / 565
Регистрация: 28.12.2010
Сообщений: 8,672
16.10.2018, 08:34
unit37, в web.xml помню тоже можно было прописывать аргументы и инжектить через сам xml. Думаю это просто нагуглить.

А вообще пора уже на джава конфиг переходить...
0
3 / 3 / 1
Регистрация: 25.12.2014
Сообщений: 63
16.10.2018, 11:39  [ТС]
KEKCoGEN, спасибо.
Переконфигурировал всё на Java code.
Смог всё заинжектить, заавтовайрить. Токен читается, парсится, взаимодействует с репозиторием.

Видимо, в конфигурации я умудрился напартачить. Я наверно неправильно использую переданный в super-конструктор фильтра defaultProcessingUrl и matching patterns в конфиге.
Java
1
2
3
4
5
6
7
8
9
10
11
public TokenAuthenticationFilter() {
        super("/**"); // <----
        setAuthenticationSuccessHandler((request, response, authentication) ->
        {
            SecurityContextHolder.getContext().setAuthentication(authentication);
            request.getRequestDispatcher(request.getServletPath() + request.getPathInfo()).forward(request, response);
        });
        setAuthenticationFailureHandler((request, response, authenticationException) -> {
            response.getOutputStream().print(authenticationException.getMessage());
        });
    }
Java
1
2
3
4
5
6
http.headers().frameOptions().sameOrigin()
                .and()
                .addFilterAfter(restTokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .authorizeRequests()
                .antMatchers(HttpMethod.GET, "/safe/*").hasAnyRole("ROLE_ADMIN")
                .anyRequest().permitAll();
По задумке, мне нужно защитить только некоторые URL, а все остальные допускается выполнять не имея токена и не авторизуясь. Например, защитить надо только /safe/*, а все остальные не надо.

Но теперь я на тех же контроллерах, которым не нужна защита, получаю ошибку 400: The request sent by the client was syntactically incorrect. На консоль выводится "Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is java.lang.NumberFormatException: For input string: "null". Но URL не содержит никаких параметров: http://localhost:8080/subjectSections/

Контроллер, который не требуется защищать:
Кликните здесь для просмотра всего текста
Java
1
2
3
4
5
6
7
8
9
10
11
12
@RestController
@CrossOrigin
@RequestMapping("/subjectSections")
public class SubjectSectionsApiController {
 
... 
 
    @CrossOrigin
    @RequestMapping(value = "/", method = RequestMethod.GET, headers = "Accept=application/json")
    public @ResponseBody List<SubjectSection> listSubjectSections() {
        return subjectSectionService.getSubjectSections();
    }
0
Эксперт Java
 Аватар для KEKCoGEN
2399 / 2224 / 565
Регистрация: 28.12.2010
Сообщений: 8,672
16.10.2018, 22:26
unit37, super() в фильтре не нужен. Фильтр должен искать токен в хедере и если его нет, просто пропускать реквест дальше по цепочке. Проверка авторизации идет дальше.
1
3 / 3 / 1
Регистрация: 25.12.2014
Сообщений: 63
17.10.2018, 23:20  [ТС]
Итак, решил возникшие проблемы следующим образом.

1. Полностью выпилил XML-конфигурацию и заменил на Java-код и аннотации.
2. Изменил фильтр:
Кликните здесь для просмотра всего текста
Java
1
2
3
4
5
6
7
@Override
    public void doFilter(ServletRequest req, ServletResponse res,
                         FilterChain chain) throws IOException, ServletException {
        if (((HttpServletRequest)req).getHeader("Authorization") == null || ((HttpServletRequest)req).getHeader("Authorization").equals(""))
            chain.doFilter(req, res);
        else super.doFilter(req, res, chain);
    }


3. Изменил менеджер авторизации (выполнил @Autowired для UserService)
4. В конфигурацию Spring Security добавил:
Кликните здесь для просмотра всего текста
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
    protected void configure(HttpSecurity http) throws Exception {
 
        http
                .csrf().disable().headers().frameOptions().sameOrigin()
                .and()
                .addFilterAfter(restTokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .authorizeRequests()
                .antMatchers(HttpMethod.GET, "/").permitAll()
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
 
    }
 
    @Bean(name = "restTokenAuthenticationFilter")
    public TokenAuthenticationFilter restTokenAuthenticationFilter() {
        TokenAuthenticationFilter restTokenAuthenticationFilter = new TokenAuthenticationFilter();
        restTokenAuthenticationFilter.setAuthenticationManager(tokenAuthenticationManager);
        return restTokenAuthenticationFilter;
    }


Все запросы, которые нужно защитить, помечаю аннотацией @Secured

Отдельное спасибо KEKCoGEN за оказанную помощь.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
17.10.2018, 23:20
Помогаю со студенческими работами здесь

Spring аннотация Autowired
Всем, добрый день. Изучаю Spring недавно и соответственно столкнулся с некоторыми трудностями. У меня есть фабрика, которая возвращает...

[Spring] @Autowired сервиса
По ТЗ нельзя использовать mvc-фреймворков, но как я понимаю сам контейнер спринга не является mvc фреймворком. Мне нужно на мои сервлеты...

Spring Boot @Autowired в коллекцию
Здравствуйте! Подскажите, пожалуйста, у меня есть коллекция типа Cars, где Cars мой абстрактный класс, от этого класса есть много...

Spring и аннотации @Configuration & @Autowired
Здравствуйте! Не могу найти ответ на свой вопрос, подскажите возможно ли такое. При работе с веб проектом всю конфигурацию бинов...

Spring - Как сделать Autowired в N объектах класса
Подскажите пожалуйста как правильно сделать. Есть класс ImagesPool, он создается через new и его всегда существует несколько экземпляров. В...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли
8Observer8 30.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
SDL3 для Web (WebAssembly): Установка Emscripten SDK (emsdk) и CMake для сборки C и C++ приложений в Wasm
8Observer8 30.01.2026
Содержание блога Для того чтобы скачать Emscripten SDK (emsdk) необходимо сначало скачать и уставить Git: Install for Windows. Следуйте стандартной процедуре установки Git через установщик. . . .
SDL3 для Android: Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования. Часть библиотеки BedvitCOM Использованы. . .
SDL3 для Android: Загрузка PNG с альфа-каналом с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 28.01.2026
Содержание блога SDL3 имеет собственные средства для загрузки и отображения PNG-файлов с альфа-каналом и базовой работы с ними. В этой инструкции используется функция SDL_LoadPNG(), которая. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru