Implement authentication and authorization basics for Java web services: JWT validation, claim hygiene, roles vs permissions and common production pitfalls.
On this page
Authn vs Authz: Stop Mixing Them
Authentication (authn) answers:
Who are you?
Authorization (authz) answers:
What are you allowed to do?
Production teams routinely mix these and ship security bugs.
A valid identity does not automatically mean permission.
Incident Scenario: Privilege Escalation via Missing Authorization
A service validated JWT properly.
Then it assumed:
If token is valid, user can access any resource.
Endpoint:
GET /accounts/{id}
Attacker used a valid token for their own account and changed the id to someone else.
Service returned data because it never checked ownership.
Root cause:
Authentication implemented, authorization missing.
This is one of the most common production authorization failures.
JWT Basics That Matter
A JWT is:
- header
- payload (claims)
- signature
It is not encrypted by default.
Anyone can read the payload.
Never put secrets in JWT claims.
JWT gives you:
- integrity (if properly signed and verified)
- portability
JWT does not automatically give:
- revocation
- session control
- authorization correctness
Anti-Pattern: Trusting Unverified Tokens
If you decode without verifying signature, you are trusting attacker input.
Also dangerous:
- accepting tokens with alg=none
- algorithm confusion issues
- accepting wrong issuer or audience
- skipping exp validation
Production rule:
Validate token signature and critical claims every time.
Correct JWT Validation Requirements
On every request, verify:
- signature with correct key
- algorithm allowlist (only what you support)
- issuer (iss)
- audience (aud)
- expiration (exp)
- not before (nbf) if used
- clock skew handling
- subject (sub) presence and format
JWT validation is not just signature validation.
Key Management and Rotation
If you use symmetric keys (HMAC):
- key compromise breaks everything
- rotation is harder across services
If you use asymmetric keys (RSA/ECDSA):
- private key signs, public key verifies
- easier to distribute verification keys
Use a JWKS endpoint for key distribution when appropriate.
Support key rotation by key id (kid), but do not trust kid blindly without controlling source of keys.
Token Transport
Best practice:
Authorization: Bearer <token>
Avoid putting tokens in:
- query parameters (leak in logs and caches)
- local storage in browsers (XSS risk) depending on app context
For browser apps, consider secure HTTP-only cookies and CSRF protections. Do not cargo-cult JWT without understanding tradeoffs.
Roles vs Permissions
Roles:
Coarse-grained groupings, often organizational.
Permissions:
Fine-grained actions (order.read, order.refund).
Production reality:
Roles alone are too blunt for complex systems.
Permissions are easier to audit and reason about.
Avoid encoding complex authorization logic in controller annotations only.
Centralize authorization decisions where possible.
Authorization Patterns
Common patterns:
- Ownership checks (resource belongs to user)
- RBAC (role-based access control)
- ABAC (attribute-based access control)
- Policy-based checks (permissions evaluated by policy engine)
Ownership check example logic:
- token sub must match resource owner
- admin role may bypass with explicit policy
Never rely on client-provided userId fields for authorization.
Service-to-Service Authentication
Do not reuse end-user JWTs for internal service calls without care.
Service-to-service auth should use:
- mTLS
- service identity tokens
- short-lived credentials
This reduces blast radius if a token is leaked.
Expiration, Revocation, and Logout Reality
JWT is stateless.
That means:
- revocation is hard
- logout is not automatic
Mitigations:
- short-lived access tokens
- refresh tokens stored securely
- server-side revocation list for critical cases
- rotate signing keys when compromise occurs
If you need strict session control, consider stateful sessions.
Logging and Observability
Log:
- auth failures (rate-limited)
- invalid issuer/audience attempts
- expired token counts
Never log:
- full tokens
- raw Authorization header
Log token identifiers only if needed and safe.
Checklist
- Separate authentication from authorization explicitly
- Validate JWT signature and critical claims (iss, aud, exp)
- Use algorithm allowlist, reject alg=none
- Do not store secrets in JWT payload
- Use Authorization header, avoid query tokens
- Implement ownership checks for resources
- Prefer permissions over roles for fine-grained control
- Plan key rotation and token lifetime strategy
- Do not log tokens or sensitive auth headers
- Design service-to-service auth separately from user auth
If your system only authenticates but does not authorize, you did not build security.
You built a login page.