Как достать пользователя из дб при использовании OAuth2

Использую Spring Boot + Security через протокол OAuth2. Есть бд с пользователями и их ролями. Как сделать так, чтобы когда пользователь заходит через OAuth, в контекст ложился пользователь из бд с нужными ролями(по умолчанию в OAUth ROLE_USER, а мне нужна ROLE_ADMIN из бд) ?


Ответы (1 шт):

Автор решения: tequillaz

С использованием актуального стека Spring Security OAuth соединить описание пользователя из сервера авторизации и из СУБД возможно создав собственный наследник Converter<Jwt, AbstractAuthenticationToken>, например, так:

/**
 * Конвертер Jwt с донасыщением данными пользователя из БД
 */
@Component
@AllArgsConstructor
public class JwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {

    private static final List<Converter<Jwt, Collection<GrantedAuthority>>> AUTHORITIES_CONVERTER =
            Arrays.asList(new JwtGrantedAuthoritiesConverter(),
                          new KeycloakRoleGrantedAuthoritiesConverter());

    private final ConversionService conversionService;
    private final AccountRepository accountRepository;

    @Override
    @Transactional
    public AbstractAuthenticationToken convert(Jwt jwt) {

        // [1]
        // Загрузка данных о пользователе из БД
        String subject = jwt.getSubject();
        Account account = accountRepository.findBySubject(subject)
                                           .orElseGet(() -> this.createAccount(subject));
        account = updateAccountBy(account, jwt);

        // [2]
        // Получение authorities
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        for (Converter<Jwt, Collection<GrantedAuthority>> authorityConverter : AUTHORITIES_CONVERTER) {
            Collection<GrantedAuthority> jwtAuthorities = authorityConverter.convert(jwt);
            if (jwtAuthorities != null && !jwtAuthorities.isEmpty()) {
                authorities.addAll(jwtAuthorities);
            }
        }

        Collection<GrantedAuthority> accountAuthorities = account2authorities(account);
        if (accountAuthorities != null && !accountAuthorities.isEmpty()) {
            authorities.addAll(accountAuthorities);
        }

        // [3]
        // Создание токена
        AccountInfo accountInfo = conversionService.convert(account, AccountInfo.class);
        authorities = authorities.stream()
                                 .map(ga -> new SimpleGrantedAuthority(ga.getAuthority().toUpperCase()))
                                 .collect(Collectors.toSet());

        return new JwtAuthenticationToken(accountInfo, jwt, authorities);
    }

    /**
     * Создать аккаунт и установить значения по умолчанию
     *
     * @return Новый аккаунт
     */
    private Account createAccount(String subject) {
        Account account = new Account();
        account.setSubject(subject);
        account.setState(TypeAccountStateEnum.NEW);

        return account;
    }

    /**
     * Обновить аккаунт данными из jwt-токена
     *
     * @param account Аккаунт
     * @param jwt     Jwt-токен
     * @return Обновленный аккаунт
     */
    private Account updateAccountBy(Account account, Jwt jwt) {
        Map<String, Object> claims = jwt.getClaims();
        // [1]
        account.setLogin((String) claims.getOrDefault("preferred_username", account.getLogin()));

        // [...]

        //
        return accountRepository.save(account);
    }

    /**
     * Получить authorities из аккаунта
     *
     * @param account Аккаунт
     * @return Authorities
     */
    private Collection<GrantedAuthority> account2authorities(Account account) {
        return Collections.singleton(new SimpleGrantedAuthority("STATE_" + account.getState().toString()));
    }

}

Конвертер срабатывает сразу после проверки квитанции на сервере авторизации. При получении токена ищется связанный пользователь в БД. Если пользователь не находится - он создается. После получения пользователя извлекаются все доступные роли из токена, а после - из учетной записи в БД. На основе всех полученных данных создается наследник AbstractAuthenticationToken с необходимыми дополнительными полями и именно он попадает в контекст Spring Security.

Конвертер, который Spring Security будет использовать для преобразования JWT-токенов, указывается в конфигурации:

@Configuration
@AllArgsConstructor
public class OAuth20Config extends WebSecurityConfigurerAdapter {

    // Конвертер для использования в JWT
    private final JwtAuthenticationConverter authenticationConverter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
         
        // [...]

        // Подключение конвертера
        http.oauth2ResourceServer(oauth2 -> oauth2.jwt()
                                                  .jwtAuthenticationConverter(authenticationConverter));

    }

}

→ Ссылка