Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4946c44
Exercise 1 & 2
ahjadi Apr 8, 2025
540996d
Exercise 1 & 2
ahjadi Apr 8, 2025
74a4467
Exercise 1 & 2
ahjadi Apr 8, 2025
f400a3e
Thursdays work
ahjadi Apr 10, 2025
540ea08
Added SQL code used to create TABLES
ahjadi Apr 11, 2025
9c5753f
added securit
ahjadi Apr 16, 2025
b86fab1
Did menu table and finished Online Ordering - User Authentication
ahjadi Apr 17, 2025
c6af91e
BONUS Did password validation for Online Ordering - User Authentication
ahjadi Apr 17, 2025
570d841
some of autherization
ahjadi Apr 18, 2025
6c66f4f
added compiler plugin for no-arg constructor
ahjadi Apr 18, 2025
a5f4b41
simplified entity relationships user direct foreign keys and removed …
ahjadi Apr 19, 2025
7512634
added validaion for user identity before order submission to prevent …
ahjadi Apr 19, 2025
1975497
Did Online Ordering - Profiles with BONUS but no testing yet
ahjadi Apr 19, 2025
d40f6b7
exception hanfling for no user found in Profile service and entity
ahjadi Apr 20, 2025
361c618
removed userId from requests and instead gets extracted from token
ahjadi Apr 20, 2025
536347b
Minor polishing
ahjadi Apr 20, 2025
29de2dd
Did 1 GET test
ahjadi Apr 21, 2025
8d020f1
Implemented logging
ahjadi Apr 27, 2025
e432535
swagger
ahjadi Apr 28, 2025
6672261
swagger
ahjadi Apr 28, 2025
a337159
swagger
ahjadi Apr 28, 2025
8b85ff3
Online Ordering - Configuration
ahjadi Apr 28, 2025
2e71a85
Online Ordering - Configuration and discount
ahjadi Apr 28, 2025
628b424
Menu Endpoint with cache
ahjadi Apr 29, 2025
8046a53
Menu Endpoint with cache With BONUS 1
ahjadi Apr 29, 2025
d2fe915
split monolith two authentication microservice and ordering microservice
ahjadi May 2, 2025
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
24 changes: 24 additions & 0 deletions OrderingDB.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
CREATE TABLE users (
id serial PRIMARY KEY,
name VARCHAR(255),
email VARCHAR(255),
username VARCHAR(255) unique,
password VARCHAR(255)
);

CREATE TABLE orders (
id serial PRIMARY KEY,
user_id BIGINT,
restaurant VARCHAR(255),
FOREIGN KEY (user_id) REFERENCES users(id)
);

CREATE TABLE items (
id serial PRIMARY KEY,
name VARCHAR(255),
quantity INT,
price DECIMAL(9, 3),
order_id BIGINT,
FOREIGN KEY (order_id) REFERENCES orders(id)
);
-- added menu too
16 changes: 16 additions & 0 deletions authentication/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?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>monolith</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>

<artifactId>authentication</artifactId>



</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.ali.authentication

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

@SpringBootApplication
class AuthenticationApplication

fun main(args: Array<String>) {
runApplication<AuthenticationApplication>(*args)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.ali.authentication


import com.ali.authentication.jwt.JwtService
import com.ali.authentication.user.UserService
import org.springframework.http.HttpStatus
import org.springframework.security.authentication.*
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.web.bind.annotation.*
import org.springframework.web.server.ResponseStatusException
import java.security.Principal


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

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

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

@PostMapping("/check-token")
fun checkToken(
principal: Principal
): CheckTokenResponse {
return CheckTokenResponse(
userId = userService.findByUsername(principal.name)
)
}
// @PostMapping("/check-token")
// fun checkToken(): CheckTokenResponse {
// val auth = SecurityContextHolder.getContext().authentication
// if (auth == null || !auth.isAuthenticated) {
// throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "Invalid token")
// }
// val username = auth.name
// val userId = userService.findByUsername(username)
// return CheckTokenResponse(userId)
// }
}

data class CheckTokenResponse(
val userId: Long
)

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

data class AuthenticationResponse(
val token: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.ali.authentication


import com.ali.authentication.user.UserRepository
import org.springframework.security.core.userdetails.*
import org.springframework.stereotype.Service

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

return User.builder()
.username(user.username)
.password(user.password)
.build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.ali.authentication


import com.ali.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
) {
// For authorization you need to add code in security config, customuserdetails, and jwtservice
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http.csrf { it.disable() }
.authorizeHttpRequests {
it
// .anyRequest().permitAll() // permit all

.requestMatchers("/auth/login", "/public/**", "/api-docs", "/hello").permitAll()
.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,47 @@
package com.ali.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
) {
logger.info("JwtAuthenticationFilter running for: ${request.requestURI}")
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,43 @@
package com.ali.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,21 @@
package com.ali.authentication.profile

import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.Table

@Entity
@Table(name = "profiles")
data class ProfileEntity(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null,

var firstName: String,
var lastName: String,
var phoneNumber: String,

var userId: Long,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.ali.authentication.profile

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
interface ProfileRepository : JpaRepository<ProfileEntity, Long> {
fun findByUserId(userId: Long): ProfileEntity?
}
Loading