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
92 changes: 92 additions & 0 deletions auth/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.coded.spring</groupId>
<artifactId>Ordering</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>

<artifactId>auth</artifactId>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.code.style>official</kotlin.code.style>
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
</properties>

<repositories>
<repository>
<id>mavenCentral</id>
<url>https://repo1.maven.org/maven2/</url>
</repository>
</repositories>

<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<mainClass>MainKt</mainClass>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<version>2.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>

</project>


Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.coded.spring.ordering
package `auth-service`

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class Application
class AuthApplication

fun main(args: Array<String>) {
runApplication<Application>(*args)
runApplication<AuthApplication>(*args)
}
38 changes: 38 additions & 0 deletions auth/src/main/kotlin/auth-service/AuthenticationController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package `auth-service`

import org.springframework.security.authentication.*
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/authentication")
class AuthenticationController(
private val authenticationManager: AuthenticationManager,
private val userDetailsService: UserDetailsService,
private val jwtService: JwtService
) {

@PostMapping("/login")
fun login(@RequestBody request: AuthenticationRequest): AuthenticationResponse {
val authenticationToken = UsernamePasswordAuthenticationToken(request.username, request.password)
val authentication = authenticationManager.authenticate(authenticationToken)

if (authentication.isAuthenticated) {
val userDetails = userDetailsService.loadUserByUsername(request.username)
val token = jwtService.generateToken(userDetails.username)
return AuthenticationResponse(token)
} else {
throw UsernameNotFoundException("Invalid login")
}
}
}

data class AuthenticationRequest(
val username: String,
val password: String
)

data class AuthenticationResponse(
val token: String
)
46 changes: 46 additions & 0 deletions auth/src/main/kotlin/auth-service/SecurityConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package `auth-service`

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationManager
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 userDetailsService: UserDetailsService,
private val jwtAuthenticationFilter: JwtAuthenticationFilter
) {

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

@Bean
fun authenticationManager(authConfig: AuthenticationConfiguration): AuthenticationManager {
return authConfig.authenticationManager
}

@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http
.csrf { it.disable() }
.authorizeHttpRequests {
it.requestMatchers("/menu", "/clients", "/welcome", "/authentication/**","/api-docs").permitAll()
it.anyRequest().authenticated()
}
.sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java)
.httpBasic { it.disable() }
.formLogin { it.disable() }

return http.build()
}
}
23 changes: 23 additions & 0 deletions auth/src/main/kotlin/auth-service/TalabatRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package `auth-service`

import jakarta.persistence.*
import org.springframework.data.jpa.repository.JpaRepository

interface TalabatRepository : JpaRepository<UserEntity, Long>{
fun findByUsername(username: String): UserEntity?
}

@Entity
@Table(name = "users")
data class UserEntity(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null,
var name: String,
@Column(unique = true)
var username: String,

var password: String
) {
constructor() : this(null, "", "","")
}
22 changes: 22 additions & 0 deletions auth/src/main/kotlin/auth-service/UserDetailsService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package `auth-service`


import `auth-service`.TalabatRepository
import org.springframework.security.core.userdetails.*
import org.springframework.stereotype.Service

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

return User.builder()
.username(user.username)
.password(user.password)
.roles("USER")
.build()
}
}
21 changes: 21 additions & 0 deletions auth/src/main/kotlin/auth-service/customUserDetails.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package `auth-service`

import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails

data class CustomUserDetails(
val id: Long,
private val username: String,
private val password: String
) : UserDetails {
override fun getAuthorities(): Collection<GrantedAuthority> =
listOf(SimpleGrantedAuthority("ROLE_USER"))

override fun getPassword(): String = password
override fun getUsername(): String = username
override fun isAccountNonExpired(): Boolean = true
override fun isAccountNonLocked(): Boolean = true
override fun isCredentialsNonExpired(): Boolean = true
override fun isEnabled(): Boolean = true
}
45 changes: 45 additions & 0 deletions auth/src/main/kotlin/auth-service/jwtAuthenticationFilter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package `auth-service`

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)
}
}
42 changes: 42 additions & 0 deletions auth/src/main/kotlin/auth-service/jwtService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package `auth-service`

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
}
}
}
Loading