Quarkus Security

affaan-m/ECC/skills/quarkus-security

作者 affaan-mef648e01899ba3e8dc6371642deaaf64b4477775無授權條款275K 個星標收錄於 2026年10月9日更新於 2026年10月9日儲存庫4 天前更新

Quarkus security implementation patterns: JWT and OIDC authentication, @RolesAllowed RBAC and SecurityIdentity checks, Bean Validation and custom validators, parameterized Panache queries, BCrypt password hashing, CORS and security headers, rate limiting, audit logging, Vault or environment-variable secrets, and dependency CVE scanning. Use when adding authentication or authorization, validating input, managing secrets, or hardening a Quarkus application.

AI 產生的概覽

指導如何以 JWT/OIDC 驗證、RBAC、輸入驗證、密鑰管理與強化來保護 Quarkus 應用程式。

功能
提供 Quarkus 安全實作模式與程式碼範例,涵蓋 JWT 與 OIDC 驗證、@RolesAllowed 與 SecurityIdentity 授權、Bean Validation 與自訂驗證器、參數化 Panache 查詢、BCrypt 密碼雜湊、CORS、安全回應標頭、速率限制、稽核記錄,以及透過 Vault 或環境變數管理密鑰。也涵蓋相依套件 CVE 掃描並列出強化最佳實務。此技能僅為說明性參考,不會產生檔案或指令碼。
適用情境
適用於為 Quarkus 應用程式加入驗證或授權、驗證使用者輸入、管理密鑰,或強化現有 Quarkus 服務時。也適合檢視 CORS、安全回應標頭、速率限制、稽核記錄與相依套件 CVE 掃描。
執行需求
不含指令碼或隨附資源,除代理本身外不需其他工具。套用範例時假設已有 Quarkus/Java 專案與相關擴充套件(例如 SmallRye JWT、OIDC、Hibernate Validator、Panache、BCrypt);涉及密鑰或 CVE 掃描時,需要存取 Vault 或 Maven/Gradle 建置環境。

Quarkus Security Review

Best practices for securing Quarkus applications with authentication, authorization, and input validation.

When to Activate

  • Adding authentication (JWT, OIDC, Basic Auth)
  • Implementing authorization with @RolesAllowed or SecurityIdentity
  • Validating user input (Bean Validation, custom validators)
  • Configuring CORS or security headers
  • Managing secrets (Vault, environment variables, config sources)
  • Adding rate limiting or brute-force protection
  • Scanning dependencies for CVEs
  • Working with MicroProfile JWT or SmallRye JWT

Authentication

JWT Authentication

java
// Resource protected with JWT@Path("/api/protected")@Authenticatedpublic class ProtectedResource {
  @Inject  JsonWebToken jwt;
  @Inject  SecurityIdentity securityIdentity;
  @GET  public Response getData() {    String username = jwt.getName();    Set<String> roles = jwt.getGroups();    return Response.ok(Map.of(        "username", username,        "roles", roles,        "principal", securityIdentity.getPrincipal().getName()    )).build();  }}

Configuration (application.properties):

properties
mp.jwt.verify.publickey.location=publicKey.pemmp.jwt.verify.issuer=https://auth.example.com
# OIDCquarkus.oidc.auth-server-url=https://auth.example.com/realms/myrealmquarkus.oidc.client-id=backend-servicequarkus.oidc.credentials.secret=${OIDC_SECRET}

Custom Authentication Filter

java
@Provider@Priority(Priorities.AUTHENTICATION)public class CustomAuthFilter implements ContainerRequestFilter {
  @Inject  SecurityIdentity identity;
  @Override  public void filter(ContainerRequestContext requestContext) {    String authHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
    // Reject immediately if header is absent or malformed    if (authHeader == null || !authHeader.startsWith("Bearer ")) {      requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());      return;    }
    String token = authHeader.substring(7);    if (!validateToken(token)) {      requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());    }  }
  private boolean validateToken(String token) {    // Token validation logic    return true;  }}

Authorization

Role-Based Access Control

java
@Path("/api/admin")@RolesAllowed("ADMIN")public class AdminResource {
  @GET  @Path("/users")  public List<UserDto> listUsers() {    return userService.findAll();  }
  @DELETE  @Path("/users/{id}")  @RolesAllowed({"ADMIN", "SUPER_ADMIN"})  public Response deleteUser(@PathParam("id") Long id) {    userService.delete(id);    return Response.noContent().build();  }}
@Path("/api/users")public class UserResource {
  @Inject  SecurityIdentity securityIdentity;
  @GET  @Path("/{id}")  @RolesAllowed("USER")  public Response getUser(@PathParam("id") Long id) {    // Check ownership    if (!securityIdentity.hasRole("ADMIN") &&        !isOwner(id, securityIdentity.getPrincipal().getName())) {      return Response.status(Response.Status.FORBIDDEN).build();    }    return Response.ok(userService.findById(id)).build();  }
  private boolean isOwner(Long userId, String username) {    return userService.isOwner(userId, username);  }}

Programmatic Security

java
@ApplicationScopedpublic class SecurityService {
  @Inject  SecurityIdentity securityIdentity;
  public boolean canAccessResource(Long resourceId) {    if (securityIdentity.isAnonymous()) {      return false;    }
    if (securityIdentity.hasRole("ADMIN")) {      return true;    }
    String userId = securityIdentity.getPrincipal().getName();    return resourceRepository.isOwner(resourceId, userId);  }}

Input Validation

Bean Validation

java
// BAD: No validation@POSTpublic Response createUser(UserDto dto) {  return Response.ok(userService.create(dto)).build();}
// GOOD: Validated DTOpublic record CreateUserDto(    @NotBlank @Size(max = 100) String name,    @NotBlank @Email String email,    @NotNull @Min(18) @Max(150) Integer age,    @Pattern(regexp = "^\\+?[1-9]\\d{1,14}$") String phone) {}
@POST@Path("/users")public Response createUser(@Valid CreateUserDto dto) {  User user = userService.create(dto);  return Response.status(Response.Status.CREATED).entity(user).build();}

Custom Validators

java
@Target({ElementType.FIELD, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Constraint(validatedBy = UsernameValidator.class)public @interface ValidUsername {  String message() default "Invalid username format";  Class<?>[] groups() default {};  Class<? extends Payload>[] payload() default {};}
public class UsernameValidator implements ConstraintValidator<ValidUsername, String> {  @Override  public boolean isValid(String value, ConstraintValidatorContext context) {    if (value == null) return false;    return value.matches("^[a-zA-Z0-9_-]{3,20}$");  }}
// Usagepublic record CreateUserDto(    @ValidUsername String username,    @NotBlank @Email String email) {}

SQL Injection Prevention

Panache Active Record (Safe by Default)

java
// GOOD: Parameterized queries with PanacheList<User> users = User.list("email = ?1 and active = ?2", email, true);
Optional<User> user = User.find("username", username).firstResultOptional();
// GOOD: Named parametersList<User> users = User.list("email = :email and age > :minAge",    Parameters.with("email", email).and("minAge", 18));

Native Queries (Use Parameters)

java
// BAD: String concatenation@Query(value = "SELECT * FROM users WHERE name = '" + name + "'", nativeQuery = true)
// GOOD: Parameterized native query@Entitypublic class User extends PanacheEntity {  public static List<User> findByEmailNative(String email) {    return getEntityManager()        .createNativeQuery("SELECT * FROM users WHERE email = :email", User.class)        .setParameter("email", email)        .getResultList();  }}

Password Hashing

java
@ApplicationScopedpublic class PasswordService {
  public String hash(String plainPassword) {    return BcryptUtil.bcryptHash(plainPassword);  }
  public boolean verify(String plainPassword, String hashedPassword) {    return BcryptUtil.matches(plainPassword, hashedPassword);  }}
// In service@ApplicationScopedpublic class UserService {  @Inject  PasswordService passwordService;
  @Transactional  public User register(CreateUserDto dto) {    String hashedPassword = passwordService.hash(dto.password());    User user = new User();    user.email = dto.email();    user.password = hashedPassword;    user.persist();    return user;  }
  public boolean authenticate(String email, String password) {    return User.find("email", email)        .firstResultOptional()        .map(u -> passwordService.verify(password, u.password))        .orElse(false);  }}

CORS Configuration

properties
# application.propertiesquarkus.http.cors=truequarkus.http.cors.origins=https://app.example.com,https://admin.example.comquarkus.http.cors.methods=GET,POST,PUT,DELETEquarkus.http.cors.headers=accept,authorization,content-type,x-requested-withquarkus.http.cors.exposed-headers=Content-Dispositionquarkus.http.cors.access-control-max-age=24Hquarkus.http.cors.access-control-allow-credentials=true

Secrets Management

properties
# application.properties - NO SECRETS HERE
# Use environment variablesquarkus.datasource.username=${DB_USER}quarkus.datasource.password=${DB_PASSWORD}quarkus.oidc.credentials.secret=${OIDC_CLIENT_SECRET}
# Or use Vaultquarkus.vault.url=https://vault.example.comquarkus.vault.authentication.kubernetes.role=my-role

HashiCorp Vault Integration

java
@ApplicationScopedpublic class SecretService {
  @ConfigProperty(name = "api-key")  String apiKey; // Fetched from Vault
  public String getSecret(String key) {    return ConfigProvider.getConfig().getValue(key, String.class);  }}

Rate Limiting

Security Note: Never use X-Forwarded-For directly — clients can spoof it. Use the actual remote address from the servlet request, or an authenticated identity (API key, JWT subject) when available.

java
@ApplicationScopedpublic class RateLimitFilter implements ContainerRequestFilter {  private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();
  @Inject  HttpServletRequest servletRequest;
  @Override  public void filter(ContainerRequestContext requestContext) {    String clientId = getClientIdentifier();    RateLimiter limiter = limiters.computeIfAbsent(clientId,        k -> RateLimiter.create(100.0)); // 100 requests per second
    if (!limiter.tryAcquire()) {      requestContext.abortWith(          Response.status(429)              .entity(Map.of("error", "Too many requests"))              .build()      );    }  }
  private String getClientIdentifier() {    // Use the container-provided remote address (not X-Forwarded-For).    // If behind a trusted proxy, configure quarkus.http.proxy.proxy-address-forwarding=true    // so getRemoteAddr() returns the real client IP.    return servletRequest.getRemoteAddr();  }}

Security Headers

java
@Providerpublic class SecurityHeadersFilter implements ContainerResponseFilter {
  @Override  public void filter(ContainerRequestContext request, ContainerResponseContext response) {    MultivaluedMap<String, Object> headers = response.getHeaders();
    // Prevent clickjacking    headers.putSingle("X-Frame-Options", "DENY");
    // XSS protection    headers.putSingle("X-Content-Type-Options", "nosniff");    headers.putSingle("X-XSS-Protection", "1; mode=block");
    // HSTS    headers.putSingle("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
    // CSP — avoid 'unsafe-inline' for script-src as it negates XSS protection;    // use nonces or hashes instead. 'unsafe-inline' for style-src is acceptable    // when CSS frameworks require it, but prefer nonces where possible.    headers.putSingle("Content-Security-Policy",        "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'");  }}

Audit Logging

java
@ApplicationScopedpublic class AuditService {  private static final Logger LOG = Logger.getLogger(AuditService.class);
  @Inject  SecurityIdentity securityIdentity;
  public void logAccess(String resource, String action) {    String user = securityIdentity.isAnonymous()        ? "anonymous"        : securityIdentity.getPrincipal().getName();
    LOG.infof("AUDIT: user=%s action=%s resource=%s timestamp=%s",        user, action, resource, Instant.now());  }}
// Usage in resource@Path("/api/sensitive")public class SensitiveResource {  @Inject  AuditService auditService;
  @GET  @RolesAllowed("ADMIN")  public Response getData() {    auditService.logAccess("sensitive-data", "READ");    return Response.ok(data).build();  }}

Dependency Security Scanning

bash
# Mavenmvn org.owasp:dependency-check-maven:check
# Gradle./gradlew dependencyCheckAnalyze
# Check Quarkus extensionsquarkus extension list --installable

Best Practices

  • Always use HTTPS in production
  • Enable JWT or OIDC for stateless authentication
  • Use @RolesAllowed for declarative authorization
  • Validate all input with Bean Validation
  • Hash passwords with BCrypt (never plaintext)
  • Store secrets in Vault or environment variables
  • Use parameterized queries to prevent SQL injection
  • Add security headers to all responses
  • Implement rate limiting for public endpoints
  • Audit sensitive operations
  • Keep dependencies updated and scan for CVEs
  • Use SecurityIdentity for programmatic checks
  • Set appropriate CORS policies
  • Test authentication and authorization paths

來源與署名

來源:affaan-m/ECC位於skills/quarkus-security提交ef648e0

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架