JWT (JSON Web Token) is a compact, URL-safe means of representing claims to be transferred between two parties. It is commonly used for authentication and authorization in modern web applications. Below is a detailed guide on implementing JWT Authentication in Java, including key concepts, libraries, and step-by-step examples.
1. Key Concepts
a. What is JWT?
- A JWT is a JSON object that is encoded and signed.
- It consists of three parts:
- Header: Contains metadata (e.g., algorithm used for signing).
- Payload: Contains claims (e.g., user ID, roles, expiration time).
- Signature: Ensures the token’s integrity.
b. Structure of a JWT
Header.Payload.Signature
- Example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
c. Use Cases
- Authentication: Verify the identity of a user.
- Authorization: Grant access to resources based on roles or permissions.
- Information Exchange: Securely transmit information between parties.
2. JWT Libraries for Java
Several libraries can be used to create, parse, and validate JWTs in Java:
- JJWT (Java JWT): A simple and easy-to-use library.
- Nimbus JOSE + JWT: A comprehensive library for JWT and JOSE (JSON Object Signing and Encryption).
- Auth0 Java JWT: A library provided by Auth0 for JWT handling.
3. Implementing JWT Authentication
a. Add Dependencies
Include the necessary dependencies in your pom.xml
(for Maven) or build.gradle
(for Gradle).
Maven (JJWT):
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
Gradle (JJWT):
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
b. Create a JWT Utility Class
Create a utility class to handle JWT creation and validation.
Example:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.util.Date;
public class JwtUtil {
private static final Key SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
private static final long EXPIRATION_TIME = 86400000; // 24 hours
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SECRET_KEY)
.compact();
}
public static Claims parseToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(SECRET_KEY)
.build()
.parseClaimsJws(token)
.getBody();
}
public static boolean validateToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(SECRET_KEY).build().parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
}
c. Create a Login Endpoint
Create a REST endpoint to authenticate users and issue JWTs.
Example:
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("/auth")
public class AuthResource {
@POST
@Path("/login")
@Produces(MediaType.APPLICATION_JSON)
public Response login(UserCredentials credentials) {
// Authenticate user (e.g., check username and password)
if ("admin".equals(credentials.getUsername()) && "password".equals(credentials.getPassword())) {
String token = JwtUtil.generateToken(credentials.getUsername());
return Response.ok().entity(new AuthResponse(token)).build();
} else {
return Response.status(Response.Status.UNAUTHORIZED).build();
}
}
}
d. Create a Protected Endpoint
Create a REST endpoint that requires a valid JWT for access.
Example:
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
@Path("/protected")
public class ProtectedResource {
@GET
public Response getProtectedResource(@Context HttpHeaders headers) {
String authHeader = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring("Bearer ".length());
if (JwtUtil.validateToken(token)) {
return Response.ok().entity("Access granted").build();
}
}
return Response.status(Response.Status.UNAUTHORIZED).build();
}
}
4. JWT Claims
JWT claims are key-value pairs that provide information about the token. Common claims include:
- sub (Subject): Identifies the subject of the token (e.g., user ID).
- exp (Expiration Time): Specifies the token’s expiration time.
- iat (Issued At): Specifies when the token was issued.
- roles: Custom claim for user roles.
Example:
public static String generateToken(String username, List<String> roles) {
return Jwts.builder()
.setSubject(username)
.claim("roles", roles)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SECRET_KEY)
.compact();
}
5. Best Practices
- Use Strong Keys: Use a strong secret key for signing JWTs.
- Set Expiration: Always set an expiration time for JWTs.
- Validate Tokens: Validate tokens on every request.
- Use HTTPS: Transmit JWTs over HTTPS to prevent interception.
- Store Securely: Store JWTs securely on the client side (e.g., in HTTP-only cookies).
6. Example Use Cases
- User Authentication: Verify user identity during login.
- API Security: Secure RESTful APIs with JWT-based authentication.
- Single Sign-On (SSO): Implement SSO across multiple applications.