JWT (JSON Web Token) é padrão (RFC 7519) de token stateless (geralmente do tipo Bearer token) usado para autenticação/autorização na troca de informações entre cliente e servidor (HTTP request/response) de forma segura (geralmente em conjunto com protocolo OAuth2), assinadas com Message Authentication Code (HMAC). Access Token é token de curta duração com expiração, usado para acessar recursos protegidos da API, enviado em cada request no header Authorization. Refresh Token é token de longa duração de expiração, usado apenas para gerar novos access tokens. Sua estrutura consiste em header (tipo de token e algoritmo de assinatura), payload (dados claims, como subject, issuedAt, expiration) e signature (assinatura criptográfica do header e payload usando secret key). JWT amplamente utilizado em sistemas web RESTful (arquitetura REST) stateless (sem manter estado no servidor), com funções encoder (criação) e decoder (leitura).
Cliente acessa /api/auth/login, recebe tokens, depois envia access token em /api/hello. Filtro valida token, liberando acesso ao controller.
Projeto Spring Boot Kotlin API REST para implementação de JWT.
com.example.myjwt.controller
com.example.myjwt.dto
com.example.myjwt.security
plugins {
kotlin("jvm") version "2.2.21"
kotlin("plugin.spring") version "2.2.21"
id("org.springframework.boot") version "4.0.6"
id("io.spring.dependency-management") version "1.1.7"
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.springframework.boot:spring-boot-starter-security")
developmentOnly("org.springframework.boot:spring-boot-devtools")
testImplementation("org.springframework.boot:spring-boot-starter-test")
implementation("io.jsonwebtoken:jjwt-api:0.12.6")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.6")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.12.6")
}
kotlin {
compilerOptions {
freeCompilerArgs.addAll("-Xjsr305=strict")
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
package com.example.myjwt.dto
data class HelloResponse(
val message: String,
val status: String
)
package com.example.myjwt.controller
import com.example.myjwt.dto.HelloResponse
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api")
class HelloController {
@GetMapping("/hello")
fun hello(): HelloResponse {
return HelloResponse(
message = "API REST funcionando!",
status = "OK"
)
}
}
'//implementation("org.springframework.boot:spring-boot-starter-security")'
'implementation("org.springframework.boot:spring-boot-starter-security")'
package com.example.myjwt.security
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.security.Keys
import org.springframework.stereotype.Service
import java.util.*
import javax.crypto.SecretKey
@Service
class JwtService {
private val secretKey: SecretKey = Keys.hmacShaKeyFor(
"my-super-secret-key-my-super-secret-key".toByteArray()
)
private val accessExpirationMs = 1000 * 60 * 5L
private val refreshExpirationMs = 1000 * 60 * 60 * 24L
fun generateAccessToken(username: String): String {
return Jwts.builder()
.subject(username)
.issuedAt(Date())
.expiration(Date(System.currentTimeMillis() + accessExpirationMs))
.signWith(secretKey)
.compact()
}
fun generateRefreshToken(username: String): String {
return Jwts.builder()
.subject(username)
.issuedAt(Date())
.expiration(Date(System.currentTimeMillis() + refreshExpirationMs))
.claim("type", "refresh")
.signWith(secretKey)
.compact()
}
fun extractUsername(token: String): String {
return Jwts.parser()
.verifyWith(secretKey)
.build()
.parseSignedClaims(token)
.payload
.subject
}
}
package com.example.myjwt.security
import jakarta.servlet.http.HttpServletResponse
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.http.SessionCreationPolicy
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
@Configuration
class SecurityConfig(
private val jwtAuthenticationFilter: JwtAuthenticationFilter
) {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http
.csrf { it.disable() }
.formLogin { it.disable() }
.httpBasic { it.disable() }
.exceptionHandling {
it.authenticationEntryPoint { _, response, _ ->
response.sendError(HttpServletResponse.SC_UNAUTHORIZED)
}
}
.sessionManagement {
it.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
}
.authorizeHttpRequests {
it
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
}
.addFilterBefore(
jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter::class.java
)
return http.build()
}
}
package com.example.myjwt.security
import jakarta.servlet.FilterChain
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.userdetails.User
import org.springframework.stereotype.Component
import org.springframework.web.filter.OncePerRequestFilter
@Component
class JwtAuthenticationFilter(
private val jwtService: JwtService
) : OncePerRequestFilter() {
override fun doFilterInternal(
request: HttpServletRequest,
response: HttpServletResponse,
filterChain: FilterChain
) {
val authHeader = request.getHeader("Authorization")
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response)
return
}
val token = authHeader.substring(7)
try {
val username = jwtService.extractUsername(token)
val auth = UsernamePasswordAuthenticationToken(
User(username, "", emptyList()),
null,
emptyList()
)
SecurityContextHolder.getContext().authentication = auth
} catch (e: Exception) {
SecurityContextHolder.clearContext()
}
filterChain.doFilter(request, response)
}
}
package com.example.myjwt.dto
data class LoginRequest(
val username: String,
val password: String
)
package com.example.myjwt.dto
data class TokenResponse(
val accessToken: String,
val refreshToken: String
)
package com.example.myjwt.controller
import com.example.myjwt.dto.LoginRequest
import com.example.myjwt.dto.TokenResponse
import com.example.myjwt.security.JwtService
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("/api/auth")
class AuthController(
private val jwtService: JwtService
) {
@PostMapping("/login")
fun login(@RequestBody request: LoginRequest): TokenResponse {
if (request.username != "admin" || request.password != "123") {
throw RuntimeException("Invalid credentials")
}
val accessToken = jwtService.generateAccessToken(request.username)
val refreshToken = jwtService.generateRefreshToken(request.username)
return TokenResponse(
accessToken = accessToken,
refreshToken = refreshToken
)
}
}
{
"username": "admin",
"password": "123"
}
Elaborado por Mateus Schwede
ubsocial.github.io