authenticaction, registration, login

This commit is contained in:
Bossanyi Tibor 2020-06-10 16:19:48 +02:00
parent dba2228939
commit c685dfb71c
25 changed files with 608 additions and 38 deletions

View File

@ -4,9 +4,10 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { plugins {
id("org.springframework.boot") version "2.3.0.RELEASE" id("org.springframework.boot") version "2.3.0.RELEASE"
id("io.spring.dependency-management") version "1.0.9.RELEASE" id("io.spring.dependency-management") version "1.0.9.RELEASE"
kotlin("jvm") version "1.3.71" kotlin("jvm") version "1.3.72"
kotlin("plugin.spring") version "1.3.72" kotlin("plugin.spring") version "1.3.72"
kotlin("plugin.jpa") version "1.3.72" kotlin("plugin.jpa") version "1.3.72"
kotlin("plugin.serialization") version "1.3.70"
} }
group = "com.aitrainer" group = "com.aitrainer"
@ -22,12 +23,18 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-aop") implementation("org.springframework.boot:spring-boot-starter-aop")
implementation("org.springframework.boot:spring-boot-starter-validation") implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.3.0.RELEASE")
implementation("org.springframework.security.oauth:spring-security-oauth2:2.5.0.RELEASE")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.apache.logging.log4j:log4j-core:2.13.3") implementation("org.apache.logging.log4j:log4j-core:2.13.3")
implementation("org.apache.logging.log4j:log4j-api:2.13.3") implementation("org.apache.logging.log4j:log4j-api:2.13.3")
implementation("org.slf4j:slf4j-api:1.7.30") implementation("org.slf4j:slf4j-api:1.7.30")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0") // JVM dependency
implementation("io.jsonwebtoken:jjwt:0.9.1")
runtimeOnly("mysql:mysql-connector-java") runtimeOnly("mysql:mysql-connector-java")

1
data/db/update_0_0_3.sql Normal file
View File

@ -0,0 +1 @@
INSERT INTO `customer` (`name`, `firstname`, `email`, `password`, `sex`, `age`, `active`, `date_add`, `date_change`, `data_policy_allowed`, `admin`) VALUES ('Dummy User', NULL, 'bosi', '$2a$10$thOc8jS750c7xe9U9Qq3GuSPs/H0Pt2Ads05yzUlyzQBIj.Rk9QCy', 'm', 40, 'N', NULL, NULL, 1, 1);

View File

@ -4,11 +4,8 @@ import org.slf4j.LoggerFactory
import org.springframework.boot.SpringApplication import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.builder.SpringApplicationBuilder import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.web.bind.annotation.RestController
@SpringBootApplication @SpringBootApplication
@RestController
class ApiApplication class ApiApplication
private val logger = LoggerFactory.getLogger(ApiApplication::class.simpleName) private val logger = LoggerFactory.getLogger(ApiApplication::class.simpleName)

View File

@ -6,7 +6,7 @@ import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
@RestController @RestController
@RequestMapping() @RequestMapping
class ApplicationProperties { class ApplicationProperties {
@Value("\${application.version}") @Value("\${application.version}")

View File

@ -1,39 +1,53 @@
package com.aitrainer.api.controller package com.aitrainer.api.controller
import com.aitrainer.api.model.Customer import com.aitrainer.api.model.Customer
import com.aitrainer.api.model.User
import com.aitrainer.api.service.ServiceBeans
import com.aitrainer.api.repository.CustomerRepository import com.aitrainer.api.repository.CustomerRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpHeaders
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.security.access.annotation.Secured
import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.*
import javax.validation.Valid import javax.validation.Valid
@RestController @RestController
@RequestMapping("/api") @RequestMapping("/api")
class CustomerController ( private val customerRepository: CustomerRepository ) { class CustomerController ( private val customerRepository: CustomerRepository ) {
@Autowired
var serviceBeans: ServiceBeans? = null
@Secured
@GetMapping("/customers") @GetMapping("/customers")
fun getAllCustomers(): List<Customer> = fun getAllCustomers(@RequestHeader headers: HttpHeaders): List<Customer> =
customerRepository.findAll() customerRepository.findAll()
@Secured
@PostMapping("/customers") @PostMapping("/customers")
fun createNewCustomer(@Valid @RequestBody customer: Customer): Customer = fun createNewCustomer(@Valid @RequestBody customer: Customer, @RequestHeader headers: HttpHeaders): Customer =
customerRepository.save(customer) customerRepository.save(customer)
@Secured
@GetMapping("/customers/{id}") @GetMapping("/customers/{id}")
fun getCustomerById(@PathVariable(value = "id") customerId: Long): ResponseEntity<Customer> { fun getCustomerById(@PathVariable(value = "id") customerId: Long, @RequestHeader headers: HttpHeaders): ResponseEntity<Customer> {
return customerRepository.findById(customerId).map { customer -> return customerRepository.findById(customerId).map { customer ->
ResponseEntity.ok(customer) ResponseEntity.ok(customer)
}.orElse(ResponseEntity.notFound().build()) }.orElse(ResponseEntity.notFound().build())
} }
@Secured
@GetMapping("/customers/real") @GetMapping("/customers/real")
fun getRealCustomers(active: String): List<Customer> = fun getRealCustomers(active: String, @RequestHeader headers: HttpHeaders): List<Customer> =
customerRepository.findByActive(active) customerRepository.findByActive(active)
@Secured
@PutMapping("/customers/{id}") @PutMapping("/customers/{id}")
fun updateCustomerById(@PathVariable(value = "id") customerId: Long, fun updateCustomerById(@PathVariable(value = "id") customerId: Long,
@Valid @RequestBody newCustomer: Customer): ResponseEntity<Customer> { @Valid @RequestBody newCustomer: Customer,
@RequestHeader headers: HttpHeaders): ResponseEntity<Customer> {
return customerRepository.findById(customerId).map { existingCustomer -> return customerRepository.findById(customerId).map { existingCustomer ->
val updatedCustomer: Customer = existingCustomer val updatedCustomer: Customer = existingCustomer
@ -43,6 +57,60 @@ class CustomerController ( private val customerRepository: CustomerRepository )
age = newCustomer.age) age = newCustomer.age)
ResponseEntity.ok().body(customerRepository.save(updatedCustomer)) ResponseEntity.ok().body(customerRepository.save(updatedCustomer))
}.orElse(ResponseEntity.notFound().build()) }.orElse(ResponseEntity.notFound().build())
}
@PostMapping("/registration")
fun registration(@Valid @RequestBody json: String): ResponseEntity<*> {
val customer = Customer()
val newUser: User = User().fromJson(json)
with (customer) {
email = newUser.username
password = serviceBeans!!.passwordEncoder().encode(newUser.password)
}
val returnCustomer: Customer? = customerRepository.findByEmail(newUser.username).let {
if ( it == null) {
customerRepository.save(customer)
} else {
null
}
}
return if ( returnCustomer != null ) {
ResponseEntity.ok().body(returnCustomer)
} else {
ResponseEntity.badRequest().body("Customer exists")
}
} }
@GetMapping("/login")
fun login(@Valid @RequestBody json: String): ResponseEntity<*> {
val customer = Customer()
val newUser: User = User().fromJson(json)
with (customer) {
email = newUser.username
password = newUser.password
}
val returnCustomer: Customer? = customerRepository.findByEmail(newUser.username).let {
if ( it == null) {
null
} else {
if (serviceBeans!!.passwordEncoder().matches(newUser.password, it.password)) {
it
} else {
null
}
}
}
return if ( returnCustomer != null ) {
ResponseEntity.ok().body(returnCustomer)
} else {
ResponseEntity.badRequest().body("Customer does not exist or the password is wrong")
}
}
} }

View File

@ -2,15 +2,13 @@ package com.aitrainer.api.controller
import com.aitrainer.api.ApiApplication import com.aitrainer.api.ApiApplication
import com.aitrainer.api.repository.ConfigurationRepository import com.aitrainer.api.repository.ConfigurationRepository
import org.aspectj.lang.annotation.Around import org.aspectj.lang.JoinPoint
import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before import org.aspectj.lang.annotation.Before
import org.aspectj.lang.annotation.Pointcut
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@Suppress("unused") @Suppress("unused")
@Aspect @Aspect
@Component @Component
@ -22,13 +20,11 @@ class CustomerControllerAspect {
@Autowired @Autowired
private lateinit var properties: ApplicationProperties private lateinit var properties: ApplicationProperties
@Suppress("unused") @Before("execution(* com.aitrainer.api.controller.CustomerController.*(..))")
@Pointcut("execution(* com.aitrainer.api.controller.CustomerController.*())") fun customerControllerAspect(joinPoint: JoinPoint) {
fun customerControllerAspect() { println("customer controller")
}
@Before("customerControllerAspect()")
fun loggingAop() {
Singleton.checkDBUpdate(configurationRepository, properties) Singleton.checkDBUpdate(configurationRepository, properties)
} }
}
}

View File

@ -3,9 +3,7 @@ package com.aitrainer.api.controller
import com.aitrainer.api.ApiApplication import com.aitrainer.api.ApiApplication
import com.aitrainer.api.model.Configuration import com.aitrainer.api.model.Configuration
import com.aitrainer.api.repository.ConfigurationRepository import com.aitrainer.api.repository.ConfigurationRepository
import org.hibernate.Hibernate.isInitialized
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
import java.io.File import java.io.File
import java.sql.Connection import java.sql.Connection
@ -73,8 +71,7 @@ object Singleton {
} }
fun execSQL( sql: String ) { private fun execSQL( sql: String ) {
if (! this.initialized ) { if (! this.initialized ) {
this.getConnection() this.getConnection()
} }

View File

@ -2,7 +2,6 @@ package com.aitrainer.api.controller
import com.aitrainer.api.ApiApplication import com.aitrainer.api.ApiApplication
import com.aitrainer.api.repository.ConfigurationRepository import com.aitrainer.api.repository.ConfigurationRepository
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before import org.aspectj.lang.annotation.Before
import org.aspectj.lang.annotation.Pointcut import org.aspectj.lang.annotation.Pointcut

View File

@ -5,7 +5,6 @@ import javax.persistence.GeneratedValue
import javax.persistence.GenerationType import javax.persistence.GenerationType
import javax.persistence.Id import javax.persistence.Id
import javax.validation.constraints.NotBlank import javax.validation.constraints.NotBlank
import javax.validation.constraints.Null
@Entity @Entity
data class Configuration ( data class Configuration (

View File

@ -4,13 +4,10 @@ import javax.persistence.Entity
import javax.persistence.GeneratedValue import javax.persistence.GeneratedValue
import javax.persistence.GenerationType import javax.persistence.GenerationType
import javax.persistence.Id import javax.persistence.Id
import javax.validation.constraints.NotBlank
@Entity @Entity
data class Customer ( data class Customer (
@get: NotBlank var name: String = "",
var name: String = "",
var firstname: String = "", var firstname: String = "",
var email: String = "", var email: String = "",
var age: Int = 0, var age: Int = 0,
@ -20,7 +17,8 @@ data class Customer (
var dateChange: String? = null, var dateChange: String? = null,
var dataPolicyAllowed: Int = 0, var dataPolicyAllowed: Int = 0,
var admin: Int = 0, var admin: Int = 0,
var password: String = "",
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val customer_id: Long = 0 val customer_id: Long = 0
) )

View File

@ -0,0 +1,15 @@
package com.aitrainer.api.model
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
data class User (
var username: String = "",
var password: String = ""
) {
@OptIn(UnstableDefault::class)
fun fromJson(json: String): User {
return Json.parse(serializer(), json)
}
}

View File

@ -6,5 +6,7 @@ import org.springframework.stereotype.Repository
@Repository @Repository
interface CustomerRepository : JpaRepository<Customer, Long> { interface CustomerRepository : JpaRepository<Customer, Long> {
fun findByActive( active: String? ):List<Customer> fun findByActive( active: String? ): List<Customer>
fun findByEmail(email: String?): Customer?
} }

View File

@ -0,0 +1,31 @@
package com.aitrainer.api.security
import com.aitrainer.api.ApiApplication
import com.aitrainer.api.controller.ApplicationProperties
import com.aitrainer.api.controller.Singleton
import com.aitrainer.api.repository.ConfigurationRepository
import org.aspectj.lang.JoinPoint
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
@Suppress("unused")
@Aspect
@Component
class AuthenticationControllerAspect {
private val logger = LoggerFactory.getLogger(ApiApplication::class.simpleName)
@Autowired
private lateinit var configurationRepository: ConfigurationRepository
@Autowired
private lateinit var properties: ApplicationProperties
@Before("execution(* com.aitrainer.api.security.JwtAuthenticationController.*(..))")
fun customerControllerAspect(joinPoint: JoinPoint) {
println("auth controller join")
Singleton.checkDBUpdate(configurationRepository, properties)
}
}

View File

@ -0,0 +1,49 @@
package com.aitrainer.api.security
import com.aitrainer.api.service.UserDetailsServiceImpl
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.ResponseEntity
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.BadCredentialsException
import org.springframework.security.authentication.DisabledException
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.web.bind.annotation.*
import org.springframework.stereotype.Component
@Component
@RestController
//@CrossOrigin
@RequestMapping("/api")
class JwtAuthenticationController {
@Autowired
private val authenticationManager: AuthenticationManager? = null
@Autowired
private val jwtTokenUtil: JwtTokenUtil? = null
@Autowired
private val jwtUserDetailsService: UserDetailsServiceImpl? = null
@PostMapping("/authenticate")
fun generateAuthenticationToken(@RequestBody authenticationRequest: JwtRequest): ResponseEntity<*> {
authenticate(authenticationRequest.username!!, authenticationRequest.password!!)
val userDetails = jwtUserDetailsService
?.loadUserByUsername(authenticationRequest.username)
val token: String = jwtTokenUtil!!.generateToken(userDetails!!)
return ResponseEntity.ok<Any>(JwtResponse(token))
}
private fun authenticate(username: String, password: String) {
try {
authenticationManager!!.authenticate(UsernamePasswordAuthenticationToken(username, password))
} catch (e: DisabledException) {
throw Exception("USER_DISABLED", e)
} catch (e: BadCredentialsException) {
throw Exception("INVALID_CREDENTIALS", e)
}
}
}

View File

@ -0,0 +1,23 @@
package com.aitrainer.api.security
import org.springframework.security.web.AuthenticationEntryPoint
import org.springframework.stereotype.Component
import java.io.IOException
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import java.io.Serializable
import org.springframework.security.core.AuthenticationException
@Component
class JwtAuthenticationEntryPoint : AuthenticationEntryPoint, Serializable {
@Throws(IOException::class)
override fun commence(request: HttpServletRequest?, response: HttpServletResponse,
authException: AuthenticationException?) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized")
}
companion object {
private const val serialVersionUID = -7858869558953243875L
}
}

View File

@ -0,0 +1,19 @@
package com.aitrainer.api.security
import java.io.Serializable
class JwtRequest : Serializable {
var username: String? = null
var password: String? = null
//default constructor for JSON Parsing
constructor(username: String?, password: String?) {
this.username = username
this.password = password
}
companion object {
private const val serialVersionUID = 5926468583005150707L
}
}

View File

@ -0,0 +1,77 @@
package com.aitrainer.api.security
import com.aitrainer.api.service.UserDetailsServiceImpl
import io.jsonwebtoken.ExpiredJwtException
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource
import org.springframework.stereotype.Component
import org.springframework.web.filter.OncePerRequestFilter
import java.io.IOException
import javax.servlet.FilterChain
import javax.servlet.ServletException
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
@Component
class JwtRequestFilter : OncePerRequestFilter() {
@Autowired
private val jwtUserDetailsService: UserDetailsServiceImpl? = null
@Autowired
private val jwtTokenUtil: JwtTokenUtil? = null
//@Autowired
//private lateinit var authenticationController: JwtAuthenticationController
@Throws(ServletException::class, IOException::class)
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain) {
val requestTokenHeader = request.getHeader("Authorization")
var username: String? = null
var jwtToken: String? = null
// JWT Token is in the form "Bearer token". Remove Bearer word and get only the Token
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer")) {
jwtToken = requestTokenHeader.substring(7)
try {
username = jwtTokenUtil!!.getUsernameFromToken(jwtToken)
} catch (e: IllegalArgumentException) {
println("Unable to get JWT Token")
} catch (e: ExpiredJwtException) {
println("JWT Token has expired")
}
} else if (requestTokenHeader != null && requestTokenHeader.equals("1") ) {
logger.warn("Authenticate")
//val credentials: User = ObjectMapper().readValue(request.inputStream, User::class.java)
} else {
logger.warn("JWT Token does not begin with Bearer String")
}
//Once we get the token validate it.
if (username != null && SecurityContextHolder.getContext().authentication == null) {
val userDetails: UserDetails = jwtUserDetailsService!!.loadUserByUsername(username)
// if token is valid configure Spring Security to manually set authentication
if (jwtTokenUtil!!.validateToken(jwtToken!!, userDetails)) {
val usernamePasswordAuthenticationToken = UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.authorities)
usernamePasswordAuthenticationToken.details = WebAuthenticationDetailsSource().buildDetails(request)
// After setting the Authentication in the context, we specify
// that the current user is authenticated. So it passes the Spring Security Configurations successfully.
SecurityContextHolder.getContext().authentication = usernamePasswordAuthenticationToken
}
}
chain.doFilter(request, response)
}
/*private fun readUserCredentials(request: HttpServletRequest): UserCredentials? {
return try {
ObjectMapper().readValue(request.inputStream, UserCredentials::class.java)
} catch (ioe: IOException) {
throw BadCredentialsException("Invalid request", ioe)
}
}*/
}

View File

@ -0,0 +1,12 @@
package com.aitrainer.api.security
import java.io.Serializable
class JwtResponse(val token: String) : Serializable {
companion object {
private const val serialVersionUID = -8091879091924046844L
}
}

View File

@ -0,0 +1,65 @@
package com.aitrainer.api.security
import com.aitrainer.api.service.ServiceBeans
import com.aitrainer.api.service.UserDetailsServiceImpl
import org.springframework.beans.factory.annotation.Autowired
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.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.config.http.SessionCreationPolicy
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
@Configuration
@EnableWebSecurity
class JwtSecurityConfig : WebSecurityConfigurerAdapter() {
@Autowired
private val jwtAuthenticationEntryPoint: JwtAuthenticationEntryPoint? = null
@Autowired
private val jwtUserDetailsService: UserDetailsServiceImpl? = null
@Autowired
private val jwtRequestFilter: JwtRequestFilter? = null
@Autowired
private val serviceBeans: ServiceBeans? = null
override fun configure(auth: AuthenticationManagerBuilder?) {
auth!!.userDetailsService(jwtUserDetailsService).passwordEncoder(serviceBeans!!.passwordEncoder())
}
@Bean
@Throws(Exception::class)
override fun authenticationManagerBean(): AuthenticationManager {
return super.authenticationManagerBean()
}
@Throws(Exception::class)
override fun configure(httpSecurity: HttpSecurity) {
// We don't need CSRF for this example
httpSecurity.
csrf().disable().
// dont authenticate this particular request
authorizeRequests().antMatchers("/api/authenticate").permitAll().
// all other requests need to be authenticated
anyRequest().authenticated().and().
// make sure we use stateless session; session won't be used to
// store user's state.
exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().
// Add a filter to validate the tokens with every request
//addFilterAt(JwtAuthenticationFilter(authenticationManagerBean()), UsernamePasswordAuthenticationFilter::class.java).
addFilterAfter(jwtRequestFilter, UsernamePasswordAuthenticationFilter::class.java).
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
}
}

View File

@ -0,0 +1,67 @@
package com.aitrainer.api.security
import org.springframework.beans.factory.annotation.Value
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.stereotype.Component
import java.util.*
import java.io.Serializable
import io.jsonwebtoken.Claims
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.SignatureAlgorithm
@Component
class JwtTokenUtil : Serializable {
@Value("\${jwt.secret}")
private val secret: String? = null
fun getUsernameFromToken(token: String?): String {
return getClaimFromToken(token, Claims::getSubject)
}
fun getIssuedAtDateFromToken(token: String?): Date {
return getClaimFromToken<Date>(token, Claims::getIssuedAt)
}
fun getExpirationDateFromToken(token: String?): Date {
return getClaimFromToken<Date>(token, Claims::getExpiration)
}
fun <T> getClaimFromToken( token: String?, claimsResolver: ( Claims.()-> T ) ): T {
val claims: Claims = getAllClaimsFromToken(token)
return claims.claimsResolver()
}
private fun getAllClaimsFromToken(token: String?): Claims {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).body
}
private fun isTokenExpired(token: String): Boolean {
val expiration: Date = getExpirationDateFromToken(token)
return expiration.before(Date())
}
fun generateToken(userDetails: UserDetails): String {
val claims: Map<String, Any> = HashMap()
return doGenerateToken(claims, userDetails.username)
}
private fun doGenerateToken(claims: Map<String, Any>, subject: String): String {
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(Date(System.currentTimeMillis()))
.setExpiration(Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000)).signWith(SignatureAlgorithm.HS512, secret).compact()
}
fun canTokenBeRefreshed(token: String): Boolean {
return !isTokenExpired(token)
}
fun validateToken(token: String, userDetails: UserDetails): Boolean {
val username = getUsernameFromToken(token)
return username == userDetails.username && !isTokenExpired(token)
}
companion object {
private const val serialVersionUID = -2550185165626007488L
const val JWT_TOKEN_VALIDITY = 5 * 60 * 60.toLong()
}
}

View File

@ -0,0 +1,19 @@
package com.aitrainer.api.service
import org.springframework.context.annotation.Bean
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Component
/*
Commonly used Beans
*/
@Component
class ServiceBeans {
@Bean
fun passwordEncoder(): PasswordEncoder {
return BCryptPasswordEncoder()
}
}

View File

@ -0,0 +1,34 @@
package com.aitrainer.api.service
import com.aitrainer.api.model.Customer
import com.aitrainer.api.repository.CustomerRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.userdetails.User
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import kotlin.collections.HashSet
@Service
class UserDetailsServiceImpl: UserDetailsService {
@Autowired
private lateinit var customerRepository: CustomerRepository
@Override
@Transactional(readOnly = true)
override fun loadUserByUsername(username: String?): UserDetails {
val customer: Customer? = customerRepository.findByEmail(username)
val grantedAuthorities = HashSet<GrantedAuthority>()
grantedAuthorities.add(SimpleGrantedAuthority("user"))
if (customer != null) {
return User(customer.email, customer.password, grantedAuthorities)
} else {throw Exception("User does not exist")}
}
}

View File

@ -16,4 +16,6 @@ logging.config=classpath:logback-spring.xml
logging.file=logs logging.file=logs
# if the database structure has been changed, increment this version number # if the database structure has been changed, increment this version number
application.version=0.0.2 application.version=0.0.3
jwt.secret=aitrainer

View File

@ -0,0 +1,25 @@
package com.aitrainer.api.test
import com.aitrainer.api.security.JwtAuthenticationController
import com.aitrainer.api.security.JwtRequest
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import kotlin.test.assertEquals
@SpringBootTest
class AuthenticationTest {
@Autowired
private lateinit var authController: JwtAuthenticationController
@Test
fun testAuthentication() {
val response: ResponseEntity<*>
val jwtRequest = JwtRequest("bosi", "andio2009")
response = authController.generateAuthenticationToken(jwtRequest)
assertEquals(response.statusCode, HttpStatus.OK)
}
}

View File

@ -1,16 +1,32 @@
package com.aitrainer.api.test package com.aitrainer.api.test
import com.aitrainer.api.controller.CustomerController
import com.aitrainer.api.model.Customer import com.aitrainer.api.model.Customer
import com.aitrainer.api.model.User
import com.aitrainer.api.repository.CustomerRepository import com.aitrainer.api.repository.CustomerRepository
import com.aitrainer.api.service.ServiceBeans
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
@SpringBootTest @SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class CustomerTests { class CustomerTests {
@Autowired
private var serviceBean: ServiceBeans? = null
@BeforeAll
fun init() {
if ( serviceBean == null ) { serviceBean = ServiceBeans() }
}
@Autowired @Autowired
private lateinit var customerRepository: CustomerRepository private lateinit var customerRepository: CustomerRepository
private var insertedId: Long? = null private var insertedId: Long? = null
@ -49,6 +65,58 @@ class CustomerTests {
assertEquals( customer.firstname, "Tiborka") assertEquals( customer.firstname, "Tiborka")
customerRepository.delete(updatedCustomer) customerRepository.delete(updatedCustomer)
} }
@Test
fun testRegistration() {
val json = "{\"username\":\"bosi@example.com\",\"password\":\"94385\"}"
val user: User = User().fromJson(json)
assertEquals(user.username, "bosi@example.com")
val customer = Customer()
with(customer) {
email = user.username
password = user.password
}
val customerController = CustomerController(customerRepository)
customerController.serviceBeans = serviceBean
var response: ResponseEntity<*> = customerController.registration(json)
val newCustomer: Customer? = response.body as Customer
assertEquals(response.statusCode, HttpStatus.OK)
val json2 = "{\"username\":\"bosi@example.com\",\"password\":\"934345\"}"
response = customerController.registration(json2)
assertEquals(response.statusCode, HttpStatus.BAD_REQUEST)
if ( newCustomer != null) {
customerRepository.delete(newCustomer)
}
}
@Test fun testLogin() {
val json = "{\"username\":\"bosi2@example.com\",\"password\":\"94333385\"}"
val user: User = User().fromJson(json)
val customer = Customer()
with(customer) {
email = user.username
password = user.password
}
val customerController = CustomerController(customerRepository)
customerController.serviceBeans = serviceBean
var response: ResponseEntity<*> = customerController.registration(json)
val newCustomer: Customer? = response.body as Customer
assertEquals(response.statusCode, HttpStatus.OK)
response = customerController.login(json)
val loginedCustomer: Customer? = response.body as Customer
assertEquals(response.statusCode, HttpStatus.OK)
if ( loginedCustomer != null ) {
assertEquals(loginedCustomer.email, ("bosi2@example.com") )
} else {
assert(true)
}
if ( newCustomer != null) {
customerRepository.delete(newCustomer)
}
}
} }