Основы безопасности
Сейчас удалить любой пост может кто угодно. Нужно две вещи. Аутентификация — узнать, кто прислал запрос: проверить логин и пароль. Авторизация — решить, можно ли этому пользователю то, что он просит: например, удалять посты разрешено только роли admin. В Quarkus проверки описывают стандартными аннотациями @RolesAllowed, а источник пользователей подключают расширением.
Подключение
Для учёбы возьмём самый простой источник — пользователей прямо в application.properties:
./mvnw quarkus:add-extension -Dextensions="elytron-security-properties-file"# src/main/resources/application.properties
quarkus.security.users.embedded.enabled=true
quarkus.security.users.embedded.plain-text=true
quarkus.security.users.embedded.users.anna=changeme
quarkus.security.users.embedded.roles.anna=admin
quarkus.security.users.embedded.users.oleg=changeme
quarkus.security.users.embedded.roles.oleg=user
quarkus.http.auth.basic=trueУ Анны роль admin, у Олега — user. quarkus.http.auth.basic=true включает Basic-аутентификацию: клиент отправляет логин и пароль в заголовке Authorization каждого запроса. Пароли открытым текстом годятся только для учёбы; на сервере пользователей хранят в базе с хешами паролей или используют токены (об этом в конце урока).
Защита метода
// src/main/java/com/example/blog/PostResource.java
@DELETE
@Path("/{id}")
@Transactional
@RolesAllowed("admin")
public Response delete(@PathParam("id") long id) {
if (!Post.deleteById(id)) {
throw new PostNotFoundException(id);
}
return Response.noContent().build();
}@RolesAllowed("admin") (импорт jakarta.annotation.security.RolesAllowed) пускает только пользователей с этой ролью. Остальные методы ресурса по-прежнему открыты. Проверим три случая — без пароля, от Олега и от Анны:
curl -i -X DELETE http://localhost:8080/posts/1
curl -i -u oleg:changeme -X DELETE http://localhost:8080/posts/1
curl -i -u anna:changeme -X DELETE http://localhost:8080/posts/1HTTP/1.1 401 Unauthorized
www-authenticate: basic
HTTP/1.1 403 Forbidden
HTTP/1.1 204 No Content
Флаг -u передаёт логин и пароль. 401 значит «не знаю, кто вы» — нужно войти. 403 — «знаю, кто вы, но вам нельзя». Неверный пароль тоже даёт 401.
Кто сделал запрос
Текущего пользователя можно получить в коде через SecurityIdentity:
// src/main/java/com/example/blog/MeResource.java
package com.example.blog;
import io.quarkus.security.Authenticated;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import java.util.Set;
@Path("/me")
@Authenticated
public class MeResource {
@Inject
SecurityIdentity identity;
@GET
public Me me() {
return new Me(identity.getPrincipal().getName(), identity.getRoles());
}
public record Me(String name, Set<String> roles) {
}
}@Authenticated на классе пускает любого вошедшего пользователя, роль не важна.
curl -u oleg:changeme http://localhost:8080/me{"name":"oleg","roles":["user"]}
Аннотации доступа
| Аннотация | Кого пускает |
|---|---|
@PermitAll | Всех, даже без входа |
@Authenticated | Любого вошедшего пользователя |
@RolesAllowed("admin") | Только пользователей с ролью |
@DenyAll | Никого |
Что используют на практике
Для настоящих API чаще берут токены: расширение oidc проверяет токены от Keycloak или другого сервера входа, smallrye-jwt — JWT-токены, подписанные вашим ключом. Аннотации @RolesAllowed и @Authenticated при этом остаются теми же — меняется только источник пользователей.
Частая ошибка
Basic-аутентификация передаёт пароль почти открытым текстом: заголовок Authorization — это просто логин и пароль в Base64, который расшифровывается за секунду. Без HTTPS его видит любой посредник в сети. Используйте Basic только локально или поверх HTTPS и не храните настоящие пароли в application.properties — этот файл попадает в Git и в собранное приложение.