Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add X-API-KEY authentication header #1

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.microservice.arch.authenticationservice.controller

import com.microservice.arch.authenticationservice.repository.ApiKeyRepositoryStub
import com.microservice.arch.authenticationservice.repository.UserRepository
import com.microservice.arch.authenticationservice.service.JwtParseException
import com.microservice.arch.authenticationservice.service.JwtService
Expand All @@ -10,7 +11,6 @@ import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.context.request.WebRequest
import javax.servlet.http.HttpServletRequest
Expand All @@ -19,6 +19,7 @@ import javax.servlet.http.HttpServletRequest
@RestController
class AuthenticationController(
private val userRepository: UserRepository,
private val apiKeyRepositoryStub: ApiKeyRepositoryStub,
private val jwtService: JwtService
) {

Expand All @@ -45,18 +46,27 @@ class AuthenticationController(
@GetMapping("/user-info")
fun userInfo(request: HttpServletRequest): ResponseEntity<UserInformationDto> {
val jwt = request.getHeader("Authorization").substring("Bearer ".length)
?: return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null)
return jwtService
.extractUserFromJwt(jwt).let {
ResponseEntity.ok(
UserInformationDto(it.username, it.authorities)
)
}
}

@GetMapping("/api-key-info")
fun apiKeyInfo(request: HttpServletRequest): ResponseEntity<ApiKeyInformationDto> {
val apiKeyStr = request.getHeader("X-API-KEY")
?: return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null)
return apiKeyRepositoryStub.validate(apiKeyStr).let {
ResponseEntity.ok(ApiKeyInformationDto(it))
}
}
}

data class UserPasswordDto(var username: String, var password: String)
data class UserInformationDto(val username: String, val authorities: Set<String>)
data class ApiKeyInformationDto(val authorities: Set<String>)

@ControllerAdvice
class ExceptionControllerAdvice {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.microservice.arch.authenticationservice.domain

class ApiKey(
val content: String,
val authorities: Set<String>,
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.microservice.arch.authenticationservice.repository

interface ApiKeyRepository {
fun validate(apiKey: String): Set<String>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.microservice.arch.authenticationservice.repository

import com.microservice.arch.authenticationservice.domain.ApiKey
import org.springframework.stereotype.Repository

@Repository
class ApiKeyRepositoryStub : ApiKeyRepository {

private val apiKeys = setOf(
ApiKey("AZERTYUIOP12345", setOf("ROLE_API_KEY"))
)

override fun validate(apiKey: String): Set<String> =
apiKeys.find { it.content == apiKey }!!.authorities
}
4 changes: 0 additions & 4 deletions foobar-service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-loadbalancer</artifactId>-->
<!-- </dependency>-->

<dependency>
<groupId>org.jetbrains.kotlin</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class FooBarController {
}

@GetMapping("/admin")
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_API_KEY')")
fun admin(): String {
return "Admin restricted"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,27 @@ class JwtRequestFilter(
filterChain: FilterChain
) {
val header = request.getHeader("Authorization")
println("Jwt : $header")
if (header == null) {
val apiKey = request.getHeader("X-API-KEY")
if (header == null && apiKey == null) {
response.status = HttpStatus.UNAUTHORIZED.value()
} else {
val userDto = authenticationService.findUser(header)
SecurityContextHolder.getContext().authentication =
UsernamePasswordAuthenticationToken(
userDto.username,
null,
userDto.authorities.map { SimpleGrantedAuthority(it) }
)
if (header != null) {
val userDto = authenticationService.findUser(header)
SecurityContextHolder.getContext().authentication =
UsernamePasswordAuthenticationToken(
userDto.username,
null,
userDto.authorities.map { SimpleGrantedAuthority(it) }
)
} else {
val apiKeyAuthorities = authenticationService.findApiKeyAuthorities(apiKey)
SecurityContextHolder.getContext().authentication =
UsernamePasswordAuthenticationToken(
null,
null,
apiKeyAuthorities.authorities.map { SimpleGrantedAuthority(it) }
)
}
}
filterChain.doFilter(request, response)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,16 @@ import org.springframework.web.bind.annotation.RequestMethod
interface AuthenticationService {
@RequestMapping(value = ["/user-info"], method = [RequestMethod.GET])
fun findUser(@RequestHeader("Authorization") jwt: String): UserDto

@RequestMapping(value = ["/api-key-info"], method = [RequestMethod.GET])
fun findApiKeyAuthorities(@RequestHeader("X-API-KEY") apiKey: String): ApiKeyInformationDto
}

data class UserDto(
val username: String,
val authorities: Set<String>
)

data class ApiKeyInformationDto(
val authorities: Set<String>
)

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ package com.microservice.arch.gateway
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.cloud.netflix.eureka.EnableEurekaClient
import org.springframework.cloud.openfeign.EnableFeignClients

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = ["com.microservice.arch.gateway"])
class GatewayApplication

fun main(args: Array<String>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,25 @@ class GatewayGlobalFilter : GlobalFilter, Ordered {
}
val path = request.path.toString()

if (path.startsWith("/auth")) {
if (path.startsWith("/auth/login")) {
return chain!!.filter(exchange)
}

if (request.headers.getOrEmpty("X-API-KEY").isNotEmpty()) {
return WebClient
.create("http://localhost:8083/")
.get()
.uri("/api-key-info")
.accept(MediaType.APPLICATION_JSON)
.headers { httpHeaders -> httpHeaders.addAll(request.headers)}
.exchangeToMono {
if (it.statusCode() != HttpStatus.OK) {
exchange.response.statusCode = HttpStatus.UNAUTHORIZED
return@exchangeToMono exchange.response.setComplete()
}
chain!!.filter(exchange)
}
}
return WebClient
.create("http://localhost:8083/")
.get()
Expand Down