Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 47 additions & 6 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,41 @@
<kotlin.version>1.9.25</kotlin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<scope>runtime</scope>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<scope>runtime</scope>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
Expand All @@ -43,23 +78,29 @@
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>5.5.0</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>


</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package com.coded.spring.ordering
package com.coded.spring


import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
Expand All @@ -8,4 +9,5 @@ class Application

fun main(args: Array<String>) {
runApplication<Application>(*args)

}
36 changes: 36 additions & 0 deletions src/main/kotlin/com/coded/spring/ordering/InitUserRunner.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.coded.spring.com.coded.spring.ordering

import com.coded.spring.Application
import com.coded.spring.ordering.users.Roles
import com.coded.spring.ordering.users.UserEntity


import com.coded.spring.ordering.users.UserRepository
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import org.springframework.security.crypto.password.PasswordEncoder

// @SpringBootApplication
class InitUserRunner {
@Bean
fun initUsers(userRepository: UserRepository, passwordEncoder: PasswordEncoder) = CommandLineRunner {
val user = UserEntity(
name = "HelloUser",
username = "testuser",
password = passwordEncoder.encode("password123"),
role = Roles.USER

)
if (userRepository.findByUsername(user.username) == null) {
println("Creating user ${user.username}")
userRepository.save(user)
} else {
println("User ${user.username} already exists")
}
}
}

fun main(args: Array<String>) {
runApplication<Application>(*args).close()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.coded.spring.com.coded.spring.ordering.authentication

import com.coded.spring.ordering.users.UserRepository
import org.springframework.security.core.userdetails.*
import org.springframework.stereotype.Service


@Service
class CustomUserDetailsService(
private val userRepository: UserRepository
) : UserDetailsService {
override fun loadUserByUsername(username: String): UserDetails {
val user = userRepository.findByUsername(username)
?: throw UsernameNotFoundException("User not found!")

return User.builder()
.username(user.username)
.password(user.password)
.roles(user.role.toString())
.build()
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.coded.spring.com.coded.spring.ordering.authentication

import com.coded.spring.com.coded.spring.ordering.authentication.jwt.JwtAuthenticationFilter
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
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.core.userdetails.UserDetailsService
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter

@Configuration
@EnableWebSecurity
class SecurityConfig(
private val jwtAuthFilter: JwtAuthenticationFilter,
private val userDetailsService: UserDetailsService
) {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http.csrf { it.disable() }
.authorizeHttpRequests {
it.requestMatchers("/user").permitAll()
.requestMatchers("/auth/**").permitAll()
// .requestMatchers("/profile").authenticated()
.anyRequest().authenticated()

}
.sessionManagement {
it.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
}
.authenticationProvider(authenticationProvider())
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter::class.java)

return http.build()
}

@Bean
fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder()

@Bean
fun authenticationManager(config: AuthenticationConfiguration): AuthenticationManager =
config.authenticationManager

@Bean
fun authenticationProvider(): AuthenticationProvider {
val provider = DaoAuthenticationProvider()
provider.setUserDetailsService(userDetailsService)
provider.setPasswordEncoder(passwordEncoder())
return provider
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.coded.spring.com.coded.spring.ordering.authentication.jwt

import jakarta.servlet.FilterChain
import jakarta.servlet.http.*
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource
import org.springframework.stereotype.Component
import org.springframework.web.filter.OncePerRequestFilter

@Component
class JwtAuthenticationFilter(
private val jwtService: JwtService,
private val userDetailsService: UserDetailsService
) : 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)
val username = jwtService.extractUsername(token)

if (SecurityContextHolder.getContext().authentication == null) {
if (jwtService.isTokenValid(token, username)) {
val userDetails = userDetailsService.loadUserByUsername(username)
val authToken = UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.authorities
)
authToken.details = WebAuthenticationDetailsSource().buildDetails(request)
SecurityContextHolder.getContext().authentication = authToken
}
}

filterChain.doFilter(request, response)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.coded.spring.com.coded.spring.ordering.authentication.jwt

import io.jsonwebtoken.*
import io.jsonwebtoken.security.Keys
import org.springframework.stereotype.Component
import java.util.*
import javax.crypto.SecretKey

@Component
class JwtService {

private val secretKey: SecretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256)
private val expirationMs: Long = 1000 * 60 * 60

fun generateToken(username: String): String {
val now = Date()
val expiry = Date(now.time + expirationMs)

return Jwts.builder()
.setSubject(username)
.setIssuedAt(now)
.setExpiration(expiry)
.signWith(secretKey)
.compact()
}

fun extractUsername(token: String): String =
Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.body
.subject

fun isTokenValid(token: String, username: String): Boolean {
return try {
extractUsername(token) == username
} catch (e: Exception) {
false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.coded.spring.com.coded.spring.ordering.config


import com.hazelcast.config.Config
import com.hazelcast.core.Hazelcast
import com.hazelcast.core.HazelcastInstance
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class HazelcastConfig {

@Bean
fun hazelcastInstance(): HazelcastInstance {
val config = Config("menu-cache")
config.getMapConfig("menus").timeToLiveSeconds = 5
return Hazelcast.newHazelcastInstance(config)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
package com.coded.spring.ordering.items

24 changes: 24 additions & 0 deletions src/main/kotlin/com/coded/spring/ordering/items/ItemEntity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.coded.spring.com.coded.spring.ordering.items

import com.coded.spring.ordering.orders.OrderEntity
import jakarta.persistence.*


@Entity
@Table(name="items")
data class ItemEntity(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,

val name: String,

val quantity: Int,

@ManyToOne
@JoinColumn(name = "order_id")
val order: OrderEntity

){
constructor() : this(null, "", 0, OrderEntity() )
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.coded.spring.ordering.items

import com.coded.spring.com.coded.spring.ordering.items.ItemEntity
import org.springframework.data.jpa.repository.JpaRepository

interface ItemsRepository: JpaRepository<ItemEntity, Long>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
package com.coded.spring.com.coded.spring.ordering.items

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
package com.coded.spring.com.coded.spring.ordering.items

22 changes: 22 additions & 0 deletions src/main/kotlin/com/coded/spring/ordering/menu/MenuController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.coded.spring.com.coded.spring.ordering.menu


import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/menu")
class MenuController(
private val menuService: MenuService
) {
@PostMapping
fun createMenu(@RequestBody menu: MenuEntity): ResponseEntity<String> {
return menuService.createMenu(menu)
}

@GetMapping
fun getAllMenus(): ResponseEntity<List<MenuEntity>> {
return menuService.getAllMenus()
}

}
Loading