Kotlin Spring Security JWT - Authentication and Authorization

Hello everyone, today we will learn how to handle authentication and authorization on RESTful APIs written with Kotlin & Spring Boot.GitHub repository link is provided at the end of this tutorial. You can download the source code.

Technologies used:

  • Kotlin is an open-source statically typed programming language. It is object-oriented and supports functional programming features. It is designed for the JVM (Java Virtual Machine)
  • Spring boot is used to develop REST web services and microservices. Spring Boot has taken the Spring framework to the next level. It has drastically reduced the configuration and setup time required for spring projects. We can set up a project with almost zero configuration and start building the things that actually matter to your application.
  • Spring Security is a Java/Java EE framework that provides authentication, authorization, and other security features for enterprise applications.
  • JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret or a public/private key pair.
  • Maven is a build automation tool used primarily for Java projects. Maven can also be used to build and manage projects written in C#, Ruby, Scala, and other languages.
  • MongoDB is a cross-platform document-oriented database program. Classified as a NoSQL database program, MongoDB uses JSON-like documents with optional schemas.

Application Flow Diagram


Project Directory



Maven[pom.xml] 

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev.demo</groupId>
<artifactId>kotlin-springsecurity-jwt</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>kotlin-springsecurity-jwt</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
<kotlin.version>1.5.31</kotlin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- adding the following dependency in order to avoid the java 11 issue -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<args>
<arg>-Xjsr305=strict</arg>
</args>
<compilerPlugins>
<plugin>spring</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

</project>


application.yaml

knf:
app:
jwtExpirationMs: 76300000
jwtSecret: knowledgeFactory
spring:
data:
mongodb:
database: demo_db
host: localhost
port: 27017


Document[Employee.kt]

package com.knf.dev.demo.kotlinspringsecurityjwt.model

import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.mapping.DBRef
import org.springframework.data.mongodb.core.mapping.Document
import java.util.HashSet
import javax.validation.constraints.Email
import javax.validation.constraints.NotBlank
import javax.validation.constraints.Size

@Document(collection = "employees")
class Employee {
@Id
var id: String? = null
var employeename: @NotBlank @Size(max = 20) String? = null
var email: @NotBlank @Size(max = 50) @Email String? = null
var password: @NotBlank @Size(max = 120) String? = null

@DBRef
var roles: Set<Role> = HashSet()

constructor() {}
constructor(employeename: String?, email: String?, password: String?) : super() {
this.employeename = employeename
this.email = email
this.password = password
}
}


Document[Role.kt]

package com.knf.dev.demo.kotlinspringsecurityjwt.model

import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.index.Indexed
import org.springframework.data.mongodb.core.mapping.Document

@Document(collection = "roles")
class Role {
@Id
var id: String? = null

@Indexed(unique = true)
var name: ERole? = null

constructor() {}
constructor(name: ERole?) {
this.name = name
}
}


ERole.kt

package com.knf.dev.demo.kotlinspringsecurityjwt.model

enum class ERole {
ROLE_EMPLOYEE, ROLE_ADMIN
}


Repository[EmployeeRepository.kt]

package com.knf.dev.demo.kotlinspringsecurityjwt.repository

import com.knf.dev.demo.kotlinspringsecurityjwt.model.Employee
import org.springframework.data.mongodb.repository.MongoRepository
import java.util.*


interface EmployeeRepository : MongoRepository<Employee?, String?> {
fun findByEmployeename(employeename: String?): Optional<Employee?>?
fun existsByEmployeename(employeename: String?): Boolean?
fun existsByEmail(email: String?): Boolean?
}


Repository[RoleRepository.kt]

package com.knf.dev.demo.kotlinspringsecurityjwt.repository

import org.springframework.data.mongodb.repository.MongoRepository
import com.knf.dev.demo.kotlinspringsecurityjwt.model.ERole
import com.knf.dev.demo.kotlinspringsecurityjwt.model.Role
import java.util.*

interface RoleRepository : MongoRepository<Role?, String?> {
fun findByName(name: ERole?): Optional<Role?>?
}


EmployeeDetailsImpl.kt

package com.knf.dev.demo.kotlinspringsecurityjwt.security.services

import com.fasterxml.jackson.annotation.JsonIgnore
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.userdetails.UserDetails
import com.knf.dev.demo.kotlinspringsecurityjwt.model.Employee
import com.knf.dev.demo.kotlinspringsecurityjwt.model.Role
import org.springframework.security.core.authority.SimpleGrantedAuthority
import java.util.stream.Collectors

class EmployeeDetailsImpl(
val id: String?, private val username: String, val email: String?,
@field:JsonIgnore private val password: String,
private val authorities: Collection<GrantedAuthority>
) : UserDetails {

override fun getAuthorities(): Collection<GrantedAuthority> {
return authorities
}

override fun getPassword(): String {
return password
}

override fun getUsername(): String {
return username
}

override fun isAccountNonExpired(): Boolean {
return true
}

override fun isAccountNonLocked(): Boolean {
return true
}

override fun isCredentialsNonExpired(): Boolean {
return true
}

override fun isEnabled(): Boolean {
return true
}

override fun equals(o: Any?): Boolean {
if (this === o) return true
if (o == null || javaClass != o.javaClass) return false
val user = o as EmployeeDetailsImpl
return id == user.id
}

companion object {
private const val serialVersionUID = 1L
fun build(user: Employee): EmployeeDetailsImpl {
val authorities = user.roles.stream()
.map { role: Role ->
SimpleGrantedAuthority(
role.name!!.name
)
}.collect(Collectors.toList())
return EmployeeDetailsImpl(
user.id, user.employeename!!,
user.email, user.password!!,
authorities
)
}
}
}


EmployeeDetailsServiceImpl.kt

package com.knf.dev.demo.kotlinspringsecurityjwt.security.services

import com.knf.dev.demo.kotlinspringsecurityjwt.model.Employee
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.transaction.annotation.Transactional
import kotlin.Throws
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.stereotype.Service
import com.knf.dev.demo.kotlinspringsecurityjwt.repository.EmployeeRepository

@Service
class EmployeeDetailsServiceImpl : UserDetailsService {
@Autowired
var employeeRepository: EmployeeRepository? = null
@Transactional
@Throws(UsernameNotFoundException::class)
override fun loadUserByUsername(employeename: String): UserDetails {
val employee: Employee = employeeRepository!!.findByEmployeename(employeename)
?.orElseThrow { UsernameNotFoundException("Employee Not Found with username: " +
"$employeename") }!!
return EmployeeDetailsImpl.build(employee)
}
}


JwtUtils.kt

package com.knf.dev.demo.kotlinspringsecurityjwt.security.jwt

import java.lang.IllegalArgumentException
import com.knf.dev.demo.kotlinspringsecurityjwt.security.services.EmployeeDetailsImpl
import io.jsonwebtoken.*
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.security.core.Authentication
import org.springframework.stereotype.Component
import java.util.*

@Component
class JwtUtils {
@Value("\${knf.app.jwtExpirationMs}")
private val jwtExpirationMs = 0

@Value("\${knf.app.jwtSecret}")
private val jwtSecret: String? = null
fun validateJwtToken(authToken: String?): Boolean {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken)
return true
} catch (e: SignatureException) {
logger.error("Invalid JWT signature: {}", e.message)
} catch (e: MalformedJwtException) {
logger.error("Invalid JWT token: {}", e.message)
} catch (e: ExpiredJwtException) {
logger.error("JWT token is expired: {}", e.message)
} catch (e: UnsupportedJwtException) {
logger.error("JWT token is unsupported: {}", e.message)
} catch (e: IllegalArgumentException) {
logger.error("JWT claims string is empty: {}", e.message)
}
return false
}

fun generateJwtToken(authentication: Authentication): String {
val employeePrincipal = authentication.principal as EmployeeDetailsImpl
return Jwts.builder().setSubject(employeePrincipal.username).setIssuedAt(Date())
.setExpiration(
Date(
Date().time +
jwtExpirationMs
)
)
.signWith(SignatureAlgorithm.HS512, jwtSecret).compact()
}

fun getEmployeeNameFromJwtToken(token: String?): String {
return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).body.subject
}

companion object {
private val logger = LoggerFactory.getLogger(JwtUtils::class.java)
}
}


AuthTokenFilter.kt

package com.knf.dev.demo.kotlinspringsecurityjwt.security.jwt

import org.springframework.web.filter.OncePerRequestFilter
import org.springframework.beans.factory.annotation.Autowired
import com.knf.dev.demo.kotlinspringsecurityjwt.security.services.EmployeeDetailsServiceImpl
import kotlin.Throws
import javax.servlet.ServletException
import java.io.IOException
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.servlet.FilterChain
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource
import org.springframework.security.core.context.SecurityContextHolder
import org.slf4j.LoggerFactory
import org.springframework.util.StringUtils
import java.lang.Exception

class AuthTokenFilter : OncePerRequestFilter() {
@Autowired
private val jwtUtils: JwtUtils? = null

@Autowired
private val employeeDetailsService: EmployeeDetailsServiceImpl? = null
@Throws(ServletException::class, IOException::class)
override fun doFilterInternal(
request: HttpServletRequest,
response: HttpServletResponse, filterChain: FilterChain
) {
try {
val jwt = parseJwt(request)
if (jwt != null && jwtUtils!!.validateJwtToken(jwt)) {
val employeename = jwtUtils.getEmployeeNameFromJwtToken(jwt)
val employeeDetails = employeeDetailsService!!.loadUserByUsername(employeename)
val authentication = UsernamePasswordAuthenticationToken(
employeeDetails, null, employeeDetails.authorities
)
authentication.details = WebAuthenticationDetailsSource().buildDetails(request)
SecurityContextHolder.getContext().authentication = authentication
}
} catch (e: Exception) {
Companion.logger.error("Cannot set employee authentication: {}", e)
}
filterChain.doFilter(request, response)
}

private fun parseJwt(request: HttpServletRequest): String? {
val headerAuth = request.getHeader("Authorization")
return if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer")) {
headerAuth.substring(7, headerAuth.length)
} else null
}

companion object {
private val logger = LoggerFactory.getLogger(AuthTokenFilter::class.java)
}
}


AuthEntryPointJwt.kt

package com.knf.dev.demo.kotlinspringsecurityjwt.security.jwt

import org.springframework.security.web.AuthenticationEntryPoint
import kotlin.Throws
import java.io.IOException
import javax.servlet.ServletException
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import com.knf.dev.demo.kotlinspringsecurityjwt.security.jwt.AuthEntryPointJwt
import org.slf4j.LoggerFactory
import org.springframework.security.core.AuthenticationException
import org.springframework.stereotype.Component

@Component
class AuthEntryPointJwt : AuthenticationEntryPoint {
@Throws(IOException::class, ServletException::class)
override fun commence(
request: HttpServletRequest, response: HttpServletResponse,
authException: AuthenticationException
) {
logger.error("Unauthorized error: {}", authException.message)
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized")
}

companion object {
private val logger = LoggerFactory.getLogger(AuthEntryPointJwt::class.java)
}
}


WebSecurityConfig.kt

package com.knf.dev.demo.kotlinspringsecurityjwt.security

import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.beans.factory.annotation.Autowired
import com.knf.dev.demo.kotlinspringsecurityjwt.security.jwt.AuthEntryPointJwt
import com.knf.dev.demo.kotlinspringsecurityjwt.security.services.EmployeeDetailsServiceImpl
import kotlin.Throws
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.http.SessionCreationPolicy
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import com.knf.dev.demo.kotlinspringsecurityjwt.security.jwt.AuthTokenFilter
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.authentication.AuthenticationManager
import java.lang.Exception

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class WebSecurityConfig : WebSecurityConfigurerAdapter() {
@Autowired
private val unauthorizedHandler: AuthEntryPointJwt? = null

@Autowired
var employeeDetailsService: EmployeeDetailsServiceImpl? = null
@Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http.cors().and().csrf().disable().exceptionHandling().
authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().
sessionCreationPolicy(SessionCreationPolicy.STATELESS).
and().authorizeRequests()
.antMatchers("/api/auth/**").permitAll().
antMatchers("/api/test/**").permitAll().anyRequest()
.authenticated()
http.addFilterBefore(
authenticationJwtTokenFilter(),
UsernamePasswordAuthenticationFilter::class.java
)
}

@Bean
fun passwordEncoder(): PasswordEncoder {
return BCryptPasswordEncoder()
}

@Bean
fun authenticationJwtTokenFilter(): AuthTokenFilter {
return AuthTokenFilter()
}

@Throws(Exception::class)
public override fun configure
(authenticationManagerBuilder: AuthenticationManagerBuilder) {
authenticationManagerBuilder.userDetailsService(employeeDetailsService).
passwordEncoder(passwordEncoder())
}

@Bean
@Throws(Exception::class)
override fun authenticationManagerBean(): AuthenticationManager {
return super.authenticationManagerBean()
}
}


LoginRequest.kt

package com.knf.dev.demo.kotlinspringsecurityjwt.request

import javax.validation.constraints.NotBlank

class LoginRequest {
var employeename: @NotBlank String? = null
var password: @NotBlank String? = null
}


SignupRequest.kt

package com.knf.dev.demo.kotlinspringsecurityjwt.request

import javax.validation.constraints.Email
import javax.validation.constraints.NotBlank
import javax.validation.constraints.Size

class SignupRequest {
var employeename: @NotBlank @Size(min = 3, max = 20) String? = null
var email: @NotBlank @Size(max = 50) @Email String? = null
var roles: Set<String>? = null
private set
var password: @NotBlank @Size(min = 6, max = 40) String? = null
fun setRole(roles: Set<String>?) {
this.roles = roles
}
}


JwtResponse.kt

package com.knf.dev.demo.kotlinspringsecurityjwt.response

class JwtResponse(
var accessToken: String, var id: String,
var employeename: String, var email: String, val roles: List<String>
) {
var tokenType = "Bearer"

}


MessageResponse.kt

package com.knf.dev.demo.kotlinspringsecurityjwt.response

class MessageResponse(var message: String)


AuthController.kt

package com.knf.dev.demo.kotlinspringsecurityjwt.controller

import org.springframework.web.bind.annotation.CrossOrigin
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.authentication.AuthenticationManager
import com.knf.dev.demo.kotlinspringsecurityjwt.repository.EmployeeRepository
import com.knf.dev.demo.kotlinspringsecurityjwt.repository.RoleRepository
import org.springframework.security.crypto.password.PasswordEncoder
import com.knf.dev.demo.kotlinspringsecurityjwt.security.jwt.JwtUtils
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import javax.validation.Valid
import com.knf.dev.demo.kotlinspringsecurityjwt.request.LoginRequest
import org.springframework.http.ResponseEntity
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.context.SecurityContextHolder
import com.knf.dev.demo.kotlinspringsecurityjwt.security.services.EmployeeDetailsImpl
import org.springframework.security.core.GrantedAuthority
import java.util.stream.Collectors
import com.knf.dev.demo.kotlinspringsecurityjwt.response.JwtResponse
import com.knf.dev.demo.kotlinspringsecurityjwt.request.SignupRequest
import com.knf.dev.demo.kotlinspringsecurityjwt.response.MessageResponse
import com.knf.dev.demo.kotlinspringsecurityjwt.model.Employee
import java.util.HashSet
import com.knf.dev.demo.kotlinspringsecurityjwt.model.ERole
import com.knf.dev.demo.kotlinspringsecurityjwt.model.Role
import java.lang.RuntimeException
import java.util.function.Consumer
import java.util.function.Supplier

@CrossOrigin(origins = ["*"], maxAge = 3600)
@RestController
@RequestMapping("/api/auth")
class AuthController {
@Autowired
var authenticationManager: AuthenticationManager? = null

@Autowired
var employeeRepository: EmployeeRepository? = null

@Autowired
var roleRepository: RoleRepository? = null

@Autowired
var encoder: PasswordEncoder? = null

@Autowired
var jwtUtils: JwtUtils? = null
@PostMapping("/signin")
fun authenticateEmployee(@RequestBody loginRequest: @Valid LoginRequest?):
ResponseEntity<*> {
val authentication = authenticationManager!!.authenticate(
UsernamePasswordAuthenticationToken
(loginRequest!!.employeename, loginRequest.password)
)
SecurityContextHolder.getContext().authentication = authentication
val jwt = jwtUtils!!.generateJwtToken(authentication)
val employeeDetails = authentication.principal as EmployeeDetailsImpl
val roles = employeeDetails.authorities.stream().
map { item: GrantedAuthority -> item.authority }
.collect(Collectors.toList())
return ResponseEntity.ok(
JwtResponse(
jwt, employeeDetails.id!!,
employeeDetails.username,
employeeDetails.email!!, roles
)
)
}

@PostMapping("/signup")
fun registerUser(@RequestBody signUpRequest:
@Valid SignupRequest?): ResponseEntity<*> {
if (employeeRepository!!.
existsByEmployeename(signUpRequest!!.employeename)!!) {
return ResponseEntity.badRequest().
body(MessageResponse("Error: Employeename is already taken!"))
}
if (employeeRepository!!.
existsByEmail(signUpRequest.email)!!) {
return ResponseEntity.badRequest().
body(MessageResponse
("Error: Email is already in use!"))
}

// Create new employee account
val employee = Employee(
signUpRequest.employeename,
signUpRequest.email,
encoder!!.encode(signUpRequest.password)
)
val strRoles = signUpRequest.roles
val roles: MutableSet<Role> = HashSet()
if (strRoles == null) {
val employeeRole = roleRepository!!.findByName(ERole.ROLE_EMPLOYEE)
?.orElseThrow(Supplier
{ RuntimeException("Error: Role is not found.") })!!
roles.add(employeeRole)
} else {
strRoles.forEach(Consumer { role: String? ->
when (role) {
"admin" -> {
val adminRole = roleRepository!!.
findByName(ERole.ROLE_ADMIN)
?.orElseThrow(Supplier
{ RuntimeException("Error: Role is not found.") })!!
roles.add(adminRole)
}
else -> {
val defaultRole = roleRepository!!.
findByName(ERole.ROLE_EMPLOYEE)
?.orElseThrow(Supplier
{ RuntimeException("Error: Role is not found.") })!!
roles.add(defaultRole)
}
}
})
}
employee.roles = roles
employeeRepository!!.save(employee)
return ResponseEntity.
ok(MessageResponse("Employee registered successfully!"))
}
}


EmployeeController.kt

package com.knf.dev.demo.kotlinspringsecurityjwt.controller

import org.springframework.web.bind.annotation.CrossOrigin
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.GetMapping
import com.knf.dev.demo.kotlinspringsecurityjwt.response.MessageResponse
import org.springframework.security.access.prepost.PreAuthorize

@CrossOrigin(origins = ["*"], maxAge = 4800)
@RestController
@RequestMapping("/api/test")
class EmployeeController {
@GetMapping("/all")
fun allAccess(): MessageResponse {
return MessageResponse("Public ")
}

@GetMapping("/employee")
@PreAuthorize("hasRole('EMPLOYEE') ")
fun employeeAccess(): MessageResponse {
return MessageResponse("Employee zone")
}

@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')")
fun adminAccess(): MessageResponse {
return MessageResponse("Admin zone")
}
}


Spring Boot Main Driver

package com.knf.dev.demo.kotlinspringsecurityjwt

import com.knf.dev.demo.kotlinspringsecurityjwt.model.ERole
import com.knf.dev.demo.kotlinspringsecurityjwt.model.Role
import com.knf.dev.demo.kotlinspringsecurityjwt.repository.RoleRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication


@SpringBootApplication
class KotlinSpringsecurityJwtApplication : CommandLineRunner {
@Autowired
var roleRepository: RoleRepository? = null

//Add some rows into roles collection before
// assigning any role to Employee.
@Throws(Exception::class)
override fun run(vararg args: String) {
try {
val role = Role()
role.name=ERole.ROLE_EMPLOYEE
roleRepository!!.save<Role>(role)
val role2 = Role()
role2.name=ERole.ROLE_ADMIN
roleRepository!!.save<Role>(role2)
} catch (e: Exception) {
}
}

companion object {
@JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(KotlinSpringsecurityJwtApplication::class.java, *args)
}
}
}


Run & Test the Application

Run the Spring Boot application: mvn spring-boot:run

Register employee:
http://localhost:8080/api/auth/signup API



Employee Sign in to an account:
http://localhost:8080/api/auth/signin API



Using accessToken access ROLE_EMPLOYEE resource:
http://localhost:8080/api/test/employee API



Register Admin:
http://localhost:8080/api/auth/signup API



Admin Sign in to an account:
http://localhost:8080/api/auth/signin API


Using accessToken access ROLE_ADMIN resource:
http://localhost:8080/api/test/admin API

Download the source code: 

git clone: 

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Spring Boot + Mockito simple application with 100% code coverage

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete