Eu tento executar todos os 3 componentes oAuth do oAuth usando o Spring Boot 3.4.1. Eu usei os documentos oauth-server-docs e client + resource-server para implementar.
O cliente oAuth pode ter 2 modos:
- para fazer login de usuários usando OAuth 2.0 ou OpenID Connect 1.0, e
- para obter um token de acesso para usuários (usando RestClient/WebClient).
Quero usar ambos, o que é possível de acordo com a documentação .
Meu código - todos os componentes - está no GitHub: https://github.com/OhadR/oAuth2-sample
Tudo parece funcionar bem, exceto por uma coisa: quando o aplicativo cliente tenta chamar o servidor de recursos antes que o usuário tenha efetuado login, o servidor de recursos retorna 401 (o que é aceitável, eu acho), mas o aplicativo cliente, em vez de redirecionar para o servidor oauth, apenas retorna 500.
Tentei depurar o Spring, mas sem sucesso.
Vejo que em OAuth2AuthorizationRequestRedirectFilter.doFilterInternal()
, quando uma exceção é lançada do resto da cadeia de filtros, o Spring verifica se é ClientAuthorizationRequiredException
e, se for, ele sendRedirectForAuthorization()
. Mas no meu caso, por algum motivo, esse código nunca é executado (coloquei um ponto de interrupção lá e ele nunca para lá).
Além disso, vejo que AuthorizationCodeOAuth2AuthorizedClientProvider.authorize()
, que é o que supostamente lança o ClientAuthorizationRequiredException
, não é chamado. (Ele é chamado apenas no fluxo de "login", quando o aplicativo cliente chama o servidor de recursos depois que o usuário já está logado).
o que funciona? modo (1) acima, significando que o aplicativo cliente tem um botão "login", que chama /login/oauth2/code/{registrationId}. Isso redireciona para o servidor oauth, o usuário faz login e, então, quando o aplicativo cliente tenta chamar o servidor de recursos, ele funciona bem, obtém 200 com resposta.
Mas o modo (2) não está funcionando. O que estou perdendo?
Obrigado!
Vamos chamar o que envia a solicitação de
F
, o aplicativo no meioG
e o servidor de recursos no finalRS
.Como
RS
é configurado como um servidor de recursos OAuth2 (oauth2ResourceServer
no Spring Security DSL), ele espera que as solicitações sejam autorizadas com umBearer
token emitido por um servidor de autorização em que confia (ou um dos servidores de autorização em que confia no caso de multilocação).Os tokens de acesso são emitidos para clientes OAuth2, que podem ser
G
F
seG
estiver configurado para encaminhar o token de acesso que recebe deF
Existem dois fluxos principais do OAuth2 para obter um token de acesso (ambos
F
podemG
usar qualquer um deles):RS
confia no cliente OAuth2 (F
ouG
) que ele sabe o que faz com o recurso e fez verificações iniciais se um usuário final estiver envolvidoSpring Security dá flexibilidade completa para implementar as várias opções acima. Algumas coisas a considerar, no entanto:
F
for executado no dispositivo do usuário final (React, Vue, Angular, aplicativos móveis, ...), agora é recomendado que ele não seja configurado como um cliente OAuth2 . Os próximos aplicativos que usam anext-auth
lib não são afetados porque o cliente OAuth2 está sendo executado no lado do servidor do aplicativo (uma instância do Node). Ele pode ser configurado como clientes confidenciais e manter os tokens seguros no servidor.G
pode ser um OAuth2 BFF: um middleware no backend que faz a ponte entre a autorização baseada em sessão para aplicativos em execução em dispositivos de usuário final e a autorização baseada em token para servidores de recursos downstream. Eu escrevi um tutorial sobre Baeldung para configurar o Spring Cloud Gateway como um OAuth2 BFF : comoauth2Login
(código de autorização e fluxos de token de atualização) e oTokenRelay=
filtro (substitui os cookies de sessão pelo token de acesso na sessão ao rotear solicitações)G
pode usar algo diferente do OAuth2 para autenticar usuários (formLogin
?) e usar um cliente OAuth2registration
com credenciais de cliente para autorizar suas solicitações paraRS
F
é um cliente oauth2, a solicitação recebida porG
pode ser autorizada com um token de acesso. Se for desejado queG
autorize a solicitação que ele envia com o token que recebeu, então não há necessidade de configuração do cliente OAuth2 emG
: o token é retirado do contexto de segurança (isso é algo que eu uso frequentemente para comunicação entre servidores de recursos na arquitetura de microsserviços)G
diferentes nas propriedades do aplicativo com:registration
authorization_code
para autenticar usuáriosclient_credentials
para autorizar seu pedido deRS
Em
G
,RestClient
eWebClient
as solicitações de autorização não estão diretamente vinculadas ao estado da sessão e devemos fornecer respectivamenteClientHttpRequestInterceptor
ouExchangeFilterFunction
para definir o cabeçalho de autorização com umBearer
token.Por enquanto, o Spring Security fornece
OAuth2ClientHttpRequestInterceptor
eServerOAuth2AuthorizedClientExchangeFilterFunction
(ServletOAuth2AuthorizedClientExchangeFilterFunction
ao usarWebClient
em um servlet). Aqueles definem o cabeçalho de autorização com um token obtido usando um clienteregistration
(pode ser um registro de código de autorização em um aplicativo comoauth2Login
ou um registro com credenciais de cliente em qualquer tipo de aplicativo).Ao usar um registro com código de autorização, essas funções esperam que a sessão seja autorizada (o usuário já deve estar logado). Então, endpoints que delegam parte de seu processamento
RS
devem ser protegidos comisAuthenticated()
ou a solicitação interna será respondida com um401
.Isso
302 Redirect to login
acontece em aplicativos comoauth2Login
somente se o endpoint que recebe uma solicitação estiver configurado comisAuthenticated()
(ou exigir qualquer autoridade). As solicitações enviadas por um cliente REST interno a este aplicativo não são afetadas (o caso dos interceptadores de solicitação/funções de filtro referenciados acima e pelos quais você está puxando os cabelos).Se quisermos encaminhar o token de acesso no contexto de segurança, precisamos escrever um
ClientHttpRequestInterceptor
ouExchangeFilterFunction
nós mesmos (nada complicado).Criei um Spring Boot starter para autoconfigurar
RestClient
ouWebClient
beans com autorização OAuth2 (e proxy HTTP se necessário) usando apenas propriedades do aplicativo. Você deveria dar uma olhada, pode facilitar sua vida, seja qual for a opção que escolher.Após alguma discussão no GitHub com o OP, adicionei algum código para complementar a excelente resposta de @ch4mp.
OP está usando
org.springframework.web.client.RestClient
. Até recentemente, ele não tinha suporte para OAuth, portanto, usar o cabeçalho Authorization era necessário.Mas com o suporte RestClient para OAuth2 no Spring Security 6.4 , o RestClient pode ser configurado assim
e o método do controlador pode ser alterado para este
Código do controlador completo