Role-Based Access Control (RBAC) in Spring Security with Kotlin
Master Role-Based Access Control (RBAC) in Spring Boot applications using Kotlin with practical examples, from basic setup to advanced configurations with method-level security
JWT (JSON Web Token) authentication is a stateless authentication mechanism widely used in modern web applications. This guide will walk you through implementing a production-ready JWT authentication system in a Spring Boot application using Kotlin.
By following this tutorial, you will:
To follow this tutorial, you will need:
First, let's create a new Spring Boot project. You can use Spring Initializr (https://start.spring.io) with the following dependencies:
Here's the complete build.gradle.kts
configuration:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "3.2.1"
id("io.spring.dependency-management") version "1.1.4"
kotlin("jvm") version "1.9.21"
kotlin("plugin.spring") version "1.9.21"
kotlin("plugin.jpa") version "1.9.21"
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_17
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
// JWT Dependencies
implementation("io.jsonwebtoken:jjwt-api:0.12.3")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.3")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.12.3")
// Database
runtimeOnly("org.postgresql:postgresql")
// Testing
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.security:spring-security-test")
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs += "-Xjsr305=strict"
jvmTarget = "17"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
Let's create our user model that will store authentication information. Create a new file User.kt
:
package com.example.auth.model
import jakarta.persistence.*
import java.time.Instant
@Entity
@Table(name = "users")
data class User(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
@Column(unique = true)
val email: String,
val password: String,
val firstName: String,
val lastName: String,
@Column(nullable = false)
val createdAt: Instant = Instant.now(),
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "user_roles", joinColumns = [JoinColumn(name = "user_id")])
@Column(name = "role")
val roles: Set<String> = setOf("ROLE_USER")
)
data class RefreshToken(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
@OneToOne
@JoinColumn(name = "user_id", referencedColumnName = "id")
val user: User,
@Column(nullable = false, unique = true)
val token: String,
@Column(nullable = false)
val expiryDate: Instant
)
Create a service to handle JWT token generation and validation.
package com.example.auth.service
import io.jsonwebtoken.Claims
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.SignatureAlgorithm
import io.jsonwebtoken.security.Keys
import org.springframework.beans.factory.annotation.Value
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.stereotype.Service
import java.security.Key
import java.util.*
@Service
class JwtService {
@Value("\${jwt.secret}")
private lateinit var secret: String
@Value("\${jwt.expiration}")
private lateinit var expirationTime: String
private val key: Key by lazy {
Keys.hmacShaKeyFor(secret.toByteArray())
}
fun generateToken(userDetails: UserDetails): String {
return Jwts.builder()
.setSubject(userDetails.username)
.claim("roles", userDetails.authorities.map { it.authority })
.setIssuedAt(Date())
.setExpiration(Date(System.currentTimeMillis() + expirationTime.toLong()))
.signWith(key)
.compact()
}
fun validateToken(token: String, userDetails: UserDetails): Boolean {
val claims = extractAllClaims(token)
val username = extractUsername(claims)
return (username == userDetails.username && !isTokenExpired(claims))
}
private fun extractUsername(claims: Claims): String {
return claims.subject
}
private fun isTokenExpired(claims: Claims): Boolean {
return claims.expiration.before(Date())
}
private fun extractAllClaims(token: String): Claims {
return Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.body
}
}
Configure Spring Security to use JWT authentication:
package com.example.auth.config
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.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.http.SessionCreationPolicy
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
@Configuration
@EnableWebSecurity
class SecurityConfig(
private val jwtAuthenticationFilter: JwtAuthenticationFilter
) {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http
.csrf { it.disable() }
.authorizeHttpRequests { auth ->
auth
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
}
.sessionManagement { session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
}
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java)
return http.build()
}
}
Implement the authentication endpoints:
package com.example.auth.controller
import com.example.auth.dto.LoginRequest
import com.example.auth.dto.RegisterRequest
import com.example.auth.service.AuthService
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("/api/auth")
class AuthController(
private val authService: AuthService
) {
@PostMapping("/register")
fun register(@RequestBody request: RegisterRequest): ResponseEntity<AuthResponse> {
return ResponseEntity.ok(authService.register(request))
}
@PostMapping("/login")
fun login(@RequestBody request: LoginRequest): ResponseEntity<AuthResponse> {
return ResponseEntity.ok(authService.login(request))
}
@PostMapping("/refresh")
fun refreshToken(@RequestBody request: RefreshTokenRequest): ResponseEntity<AuthResponse> {
return ResponseEntity.ok(authService.refreshToken(request))
}
}
When deploying to production, consider implementing these security best practices:
# application.yml
jwt:
secret: ${JWT_SECRET:your-256-bit-secret}
expiration: ${JWT_EXPIRATION:86400000} # 24 hours in milliseconds
@Configuration
class RateLimitConfig {
@Bean
fun customKeyGenerator(): KeyGenerator {
return KeyGenerator { request ->
val ip = request.remoteAddr
val path = request.requestURI
"$ip:$path"
}
}
}
@Component
class RequestLoggingFilter : OncePerRequestFilter() {
override fun doFilterInternal(
request: HttpServletRequest,
response: HttpServletResponse,
filterChain: FilterChain
) {
val startTime = System.currentTimeMillis()
filterChain.doFilter(request, response)
val duration = System.currentTimeMillis() - startTime
logger.info(
"Request completed: method=${request.method}, uri=${request.requestURI}, " +
"status=${response.status}, duration=${duration}ms"
)
}
}
Here's a test class to verify our JWT implementation:
@SpringBootTest
class JwtServiceTest {
@Autowired
private lateinit var jwtService: JwtService
@Test
fun `should generate valid JWT token`() {
val userDetails = User(
email = "[email protected]",
password = "password",
roles = setOf("ROLE_USER")
)
val token = jwtService.generateToken(userDetails)
assertThat(jwtService.validateToken(token, userDetails)).isTrue
}
}
You now have a production-ready JWT authentication system implemented in your Spring Boot Kotlin application. This implementation includes:
Remember to:
Master Role-Based Access Control (RBAC) in Spring Boot applications using Kotlin with practical examples, from basic setup to advanced configurations with method-level security
Learn how to implement secure API key authentication in Spring Boot with Kotlin, including rate limiting, monitoring, and production-ready best practices - complete with practical examples and ready-to-use code
Learn why CSRF protection isn't necessary for most REST APIs and how to properly disable it in Spring Boot while maintaining security. Includes code examples and best practices