Browse Source

🌐 Add Portuguese translation for `docs/pt/docs/tutorial/security/simple-oauth2.md` (#12520)

pull/12656/merge
Lídia 5 months ago
committed by GitHub
parent
commit
bb7921be25
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 539
      docs/pt/docs/tutorial/security/simple-oauth2.md

539
docs/pt/docs/tutorial/security/simple-oauth2.md

@ -0,0 +1,539 @@
# Simples OAuth2 com senha e Bearer
Agora vamos construir a partir do capítulo anterior e adicionar as partes que faltam para ter um fluxo de segurança completo.
## Pegue o `username` (nome de usuário) e `password` (senha)
É utilizado o utils de segurança da **FastAPI** para obter o `username` e a `password`.
OAuth2 especifica que ao usar o "password flow" (fluxo de senha), que estamos usando, o cliente/usuário deve enviar os campos `username` e `password` como dados do formulário.
E a especificação diz que os campos devem ser nomeados assim. Portanto, `user-name` ou `email` não funcionariam.
Mas não se preocupe, você pode mostrá-lo como quiser aos usuários finais no frontend.
E seus modelos de banco de dados podem usar qualquer outro nome que você desejar.
Mas para a *operação de rota* de login, precisamos usar esses nomes para serem compatíveis com a especificação (e poder, por exemplo, usar o sistema integrado de documentação da API).
A especificação também afirma que o `username` e a `password` devem ser enviados como dados de formulário (portanto, não há JSON aqui).
### `scope`
A especificação também diz que o cliente pode enviar outro campo de formulário "`scope`" (Escopo).
O nome do campo do formulário é `scope` (no singular), mas na verdade é uma longa string com "escopos" separados por espaços.
Cada “scope” é apenas uma string (sem espaços).
Normalmente são usados para declarar permissões de segurança específicas, por exemplo:
* `users:read` ou `users:write` são exemplos comuns.
* `instagram_basic` é usado pelo Facebook e Instagram.
* `https://www.googleapis.com/auth/drive` é usado pelo Google.
/// info | Informação
No OAuth2, um "scope" é apenas uma string que declara uma permissão específica necessária.
Não importa se tem outros caracteres como `:` ou se é uma URL.
Esses detalhes são específicos da implementação.
Para OAuth2 são apenas strings.
///
## Código para conseguir o `username` e a `password`
Agora vamos usar os utilitários fornecidos pelo **FastAPI** para lidar com isso.
### `OAuth2PasswordRequestForm`
Primeiro, importe `OAuth2PasswordRequestForm` e use-o como uma dependência com `Depends` na *operação de rota* para `/token`:
//// tab | Python 3.10+
```Python hl_lines="4 78"
{!> ../../docs_src/security/tutorial003_an_py310.py!}
```
////
//// tab | Python 3.9+
```Python hl_lines="4 78"
{!> ../../docs_src/security/tutorial003_an_py39.py!}
```
////
//// tab | Python 3.8+
```Python hl_lines="4 79"
{!> ../../docs_src/security/tutorial003_an.py!}
```
////
//// tab | Python 3.10+ non-Annotated
/// tip | Dica
Prefira usar a versão `Annotated`, se possível.
///
```Python hl_lines="2 74"
{!> ../../docs_src/security/tutorial003_py310.py!}
```
////
//// tab | Python 3.8+ non-Annotated
/// tip | Dica
Prefira usar a versão `Annotated`, se possível.
///
```Python hl_lines="4 76"
{!> ../../docs_src/security/tutorial003.py!}
```
////
`OAuth2PasswordRequestForm` é uma dependência de classe que declara um corpo de formulário com:
* O `username`.
* A `password`.
* Um campo `scope` opcional como uma string grande, composta de strings separadas por espaços.
* Um `grant_type` (tipo de concessão) opcional.
/// tip | Dica
A especificação OAuth2 na verdade *requer* um campo `grant_type` com um valor fixo de `password`, mas `OAuth2PasswordRequestForm` não o impõe.
Se você precisar aplicá-lo, use `OAuth2PasswordRequestFormStrict` em vez de `OAuth2PasswordRequestForm`.
///
* Um `client_id` opcional (não precisamos dele em nosso exemplo).
* Um `client_secret` opcional (não precisamos dele em nosso exemplo).
/// info | Informação
O `OAuth2PasswordRequestForm` não é uma classe especial para **FastAPI** como é `OAuth2PasswordBearer`.
`OAuth2PasswordBearer` faz com que **FastAPI** saiba que é um esquema de segurança. Portanto, é adicionado dessa forma ao OpenAPI.
Mas `OAuth2PasswordRequestForm` é apenas uma dependência de classe que você mesmo poderia ter escrito ou poderia ter declarado os parâmetros do `Form` (formulário) diretamente.
Mas como é um caso de uso comum, ele é fornecido diretamente pelo **FastAPI**, apenas para facilitar.
///
### Use os dados do formulário
/// tip | Dica
A instância da classe de dependência `OAuth2PasswordRequestForm` não terá um atributo `scope` com a string longa separada por espaços, em vez disso, terá um atributo `scopes` com a lista real de strings para cada escopo enviado.
Não estamos usando `scopes` neste exemplo, mas a funcionalidade está disponível se você precisar.
///
Agora, obtenha os dados do usuário do banco de dados (falso), usando o `username` do campo do formulário.
Se não existir tal usuário, retornaremos um erro dizendo "Incorrect username or password" (Nome de usuário ou senha incorretos).
Para o erro, usamos a exceção `HTTPException`:
//// tab | Python 3.10+
```Python hl_lines="3 79-81"
{!> ../../docs_src/security/tutorial003_an_py310.py!}
```
////
//// tab | Python 3.9+
```Python hl_lines="3 79-81"
{!> ../../docs_src/security/tutorial003_an_py39.py!}
```
////
//// tab | Python 3.8+
```Python hl_lines="3 80-82"
{!> ../../docs_src/security/tutorial003_an.py!}
```
////
//// tab | Python 3.10+ non-Annotated
/// tip | Dica
Prefira usar a versão `Annotated`, se possível.
///
```Python hl_lines="1 75-77"
{!> ../../docs_src/security/tutorial003_py310.py!}
```
////
//// tab | Python 3.8+ non-Annotated
/// tip | Dica
Prefira usar a versão `Annotated`, se possível.
///
```Python hl_lines="3 77-79"
{!> ../../docs_src/security/tutorial003.py!}
```
////
### Confira a password (senha)
Neste ponto temos os dados do usuário do nosso banco de dados, mas não verificamos a senha.
Vamos colocar esses dados primeiro no modelo `UserInDB` do Pydantic.
Você nunca deve salvar senhas em texto simples, portanto, usaremos o sistema de hashing de senhas (falsas).
Se as senhas não corresponderem, retornaremos o mesmo erro.
#### Hashing de senha
"Hashing" significa: converter algum conteúdo (uma senha neste caso) em uma sequência de bytes (apenas uma string) que parece algo sem sentido.
Sempre que você passa exatamente o mesmo conteúdo (exatamente a mesma senha), você obtém exatamente a mesma sequência aleatória de caracteres.
Mas você não pode converter a sequência aleatória de caracteres de volta para a senha.
##### Porque usar hashing de senha
Se o seu banco de dados for roubado, o ladrão não terá as senhas em texto simples dos seus usuários, apenas os hashes.
Assim, o ladrão não poderá tentar usar essas mesmas senhas em outro sistema (como muitos usuários usam a mesma senha em todos os lugares, isso seria perigoso).
//// tab | Python 3.10+
```Python hl_lines="82-85"
{!> ../../docs_src/security/tutorial003_an_py310.py!}
```
////
//// tab | Python 3.9+
```Python hl_lines="82-85"
{!> ../../docs_src/security/tutorial003_an_py39.py!}
```
////
//// tab | Python 3.8+
```Python hl_lines="83-86"
{!> ../../docs_src/security/tutorial003_an.py!}
```
////
//// tab | Python 3.10+ non-Annotated
/// tip | Dica
Prefira usar a versão `Annotated`, se possível.
///
```Python hl_lines="78-81"
{!> ../../docs_src/security/tutorial003_py310.py!}
```
////
//// tab | Python 3.8+ non-Annotated
/// tip | Dica
Prefira usar a versão `Annotated`, se possível.
///
```Python hl_lines="80-83"
{!> ../../docs_src/security/tutorial003.py!}
```
////
#### Sobre `**user_dict`
`UserInDB(**user_dict)` significa:
*Passe as keys (chaves) e values (valores) de `user_dict` diretamente como argumentos de valor-chave, equivalente a:*
```Python
UserInDB(
username = user_dict["username"],
email = user_dict["email"],
full_name = user_dict["full_name"],
disabled = user_dict["disabled"],
hashed_password = user_dict["hashed_password"],
)
```
/// info | Informação
Para uma explicação mais completa de `**user_dict`, verifique [a documentação para **Extra Models**](../extra-models.md#about-user_indict){.internal-link target=_blank}.
///
## Retorne o token
A resposta do endpoint `token` deve ser um objeto JSON.
Deve ter um `token_type`. No nosso caso, como estamos usando tokens "Bearer", o tipo de token deve ser "`bearer`".
E deve ter um `access_token`, com uma string contendo nosso token de acesso.
Para este exemplo simples, seremos completamente inseguros e retornaremos o mesmo `username` do token.
/// tip | Dica
No próximo capítulo, você verá uma implementação realmente segura, com hash de senha e tokens <abbr title="JSON Web Tokens">JWT</abbr>.
Mas, por enquanto, vamos nos concentrar nos detalhes específicos de que precisamos.
///
//// tab | Python 3.10+
```Python hl_lines="87"
{!> ../../docs_src/security/tutorial003_an_py310.py!}
```
////
//// tab | Python 3.9+
```Python hl_lines="87"
{!> ../../docs_src/security/tutorial003_an_py39.py!}
```
////
//// tab | Python 3.8+
```Python hl_lines="88"
{!> ../../docs_src/security/tutorial003_an.py!}
```
////
//// tab | Python 3.10+ non-Annotated
/// tip | Dica
Prefira usar a versão `Annotated`, se possível.
///
```Python hl_lines="83"
{!> ../../docs_src/security/tutorial003_py310.py!}
```
////
//// tab | Python 3.8+ non-Annotated
/// tip | Dica
Prefira usar a versão `Annotated`, se possível.
///
```Python hl_lines="85"
{!> ../../docs_src/security/tutorial003.py!}
```
////
/// tip | Dica
Pela especificação, você deve retornar um JSON com um `access_token` e um `token_type`, o mesmo que neste exemplo.
Isso é algo que você mesmo deve fazer em seu código e certifique-se de usar essas chaves JSON.
É quase a única coisa que você deve se lembrar de fazer corretamente, para estar em conformidade com as especificações.
De resto, **FastAPI** cuida disso para você.
///
## Atualize as dependências
Agora vamos atualizar nossas dependências.
Queremos obter o `user_user` *somente* se este usuário estiver ativo.
Portanto, criamos uma dependência adicional `get_current_active_user` que por sua vez usa `get_current_user` como dependência.
Ambas as dependências retornarão apenas um erro HTTP se o usuário não existir ou se estiver inativo.
Portanto, em nosso endpoint, só obteremos um usuário se o usuário existir, tiver sido autenticado corretamente e estiver ativo:
//// tab | Python 3.10+
```Python hl_lines="58-66 69-74 94"
{!> ../../docs_src/security/tutorial003_an_py310.py!}
```
////
//// tab | Python 3.9+
```Python hl_lines="58-66 69-74 94"
{!> ../../docs_src/security/tutorial003_an_py39.py!}
```
////
//// tab | Python 3.8+
```Python hl_lines="59-67 70-75 95"
{!> ../../docs_src/security/tutorial003_an.py!}
```
////
//// tab | Python 3.10+ non-Annotated
/// tip | Dica
Prefira usar a versão `Annotated`, se possível.
///
```Python hl_lines="56-64 67-70 88"
{!> ../../docs_src/security/tutorial003_py310.py!}
```
////
//// tab | Python 3.8+ non-Annotated
/// tip | Dica
Prefira usar a versão `Annotated`, se possível.
///
```Python hl_lines="58-66 69-72 90"
{!> ../../docs_src/security/tutorial003.py!}
```
////
/// info | Informação
O cabeçalho adicional `WWW-Authenticate` com valor `Bearer` que estamos retornando aqui também faz parte da especificação.
Qualquer código de status HTTP (erro) 401 "UNAUTHORIZED" também deve retornar um cabeçalho `WWW-Authenticate`.
No caso de tokens ao portador (nosso caso), o valor desse cabeçalho deve ser `Bearer`.
Na verdade, você pode pular esse cabeçalho extra e ainda funcionaria.
Mas é fornecido aqui para estar em conformidade com as especificações.
Além disso, pode haver ferramentas que esperam e usam isso (agora ou no futuro) e que podem ser úteis para você ou seus usuários, agora ou no futuro.
Esse é o benefício dos padrões...
///
## Veja em ação
Abra o docs interativo: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
### Autenticação
Clique no botão "Authorize".
Use as credenciais:
User: `johndoe`
Password: `secret`
<img src="/img/tutorial/security/image04.png">
Após autenticar no sistema, você verá assim:
<img src="/img/tutorial/security/image05.png">
### Obtenha seus próprios dados de usuário
Agora use a operação `GET` com o caminho `/users/me`.
Você obterá os dados do seu usuário, como:
```JSON
{
"username": "johndoe",
"email": "[email protected]",
"full_name": "John Doe",
"disabled": false,
"hashed_password": "fakehashedsecret"
}
```
<img src="/img/tutorial/security/image06.png">
Se você clicar no ícone de cadeado, sair e tentar a mesma operação novamente, receberá um erro HTTP 401 de:
```JSON
{
"detail": "Not authenticated"
}
```
### Usuário inativo
Agora tente com um usuário inativo, autentique-se com:
User: `alice`
Password: `secret2`
E tente usar a operação `GET` com o caminho `/users/me`.
Você receberá um erro "Usuário inativo", como:
```JSON
{
"detail": "Inactive user"
}
```
## Recaptulando
Agora você tem as ferramentas para implementar um sistema de segurança completo baseado em `username` e `password` para sua API.
Usando essas ferramentas, você pode tornar o sistema de segurança compatível com qualquer banco de dados e com qualquer usuário ou modelo de dados.
O único detalhe que falta é que ainda não é realmente "seguro".
No próximo capítulo você verá como usar uma biblioteca de hashing de senha segura e tokens <abbr title="JSON Web Tokens">JWT</abbr>.
Loading…
Cancel
Save