How to use spring-cloud-gateway for authentication and authorization?

Viewed 626

I am very confused about this architecture. I am not even sure is it possible.

I have more than 10 microservises and a API Gateway. I want to add authentication and authorization to this system. One of this services is authentication-server and it has an endpoint which is /signin

@PostMapping(value = "/signin")
public UserLoginResponse login(@Valid @RequestBody UserLoginRequest userLoginRequest) {
    return authService.login(userLoginRequest);
}

public class UserLoginResponse {
    private String accessToken; //accessToken is jwt token and it has ROLE field.
}

public class UserLoginRequest {
    private String username;
    private String password;
}

Here is the confusing part for me: Right now gateway creates code duplication. When I add to a new endpoint, I need to add almost same controller/service/models to API Gateway. For example: Lets say microservice A has /product endpoint, these are (veeery roughly) the classes I should have

// Controller
class ProductController {
    @GetMapping("/product/{id}")
    public ProductResponse getProduct(@PathVariable String id) {
        return productService.getProduct(id)
    }
}

// Service
class ProductService {
    public getProduct(String id){
        return productRepository.get(id);
    }    
}

// Response DTO
class ProductResponse(){
    private String id;
    private String name;
}

Our team also has implemented classes in the gateway.

//Controller has authorization with @PreAuthorize annotation.
@RestController
@PreAuthorize("hasAnyRole('USER', 'ADMIN')")
class ProductController {
    @GetMapping("/product/{id}")
    public ProductResponse getProduct(PathVariable String id) {
        return productService.getProduct(id)
    }
}

// Service
class ProductService {
    private final ClientApi productClientApi;

    public ProductService(ClientApi productClientApi) {
        this.productClientApi = productClientApi;
    }

    public getProduct(String id){
        return productClientApi.getProduct(id);
    }    
}

//This is feign client. It makes http requests to product-api
@FeignClient(
    "product-api",
    url = "\${product-api.base-url}",
    configuration = [FeignClientConfiguration::class]
)
interface ClientApi(){
    @GetMapping( value = {"product/{id}"}, consumes = {"application/json"} )
    ProductResponse getProduct(@PathVariable String id);
}

// Response DTO
class ProductResponse(){
    private String id;
    private String name;
}
  1. When a request comes to /product its jwt token controlled here and if it has proper permission, it goes to the service layer,
  2. Service layer makes request to product-api(which is microservice A)
  3. returns the response

Question: There should be easier way. Every new endpoint in the services costs us code duplication in Gateway. I think I want just routing. Whatever comes to gateway directly should be routed to services and it should still has the authentication/authorization responsibility. I know that I can do that as below with spring-cloud-gateway but I couldn't figure out how can i do that with authentication and authorization. Can anyone explain me that am i thinking wrong ?

spring:
  application:
  name: "API-GATEWAY"


  cloud:
   gateway:
    routes:
      - id: product-service
        uri: 'http://product-url:8083'
        predicates:
          - Path=/product/**
0 Answers
Related