I'm using Spring Cloud Gateway like entrypoint for my infrastructure. The gateway is configured with keycloak to validate Authentication header with following configuration
spring:
security:
oauth2:
resource-server:
jwt:
jwk-set-uri: https://httpd.keycloak.local:443/keycloak/realms/myRealm/protocol/openid-connect/certs
An example Route is the following
spring:
cloud:
gateway:
routes:
- id: my-route
uri: http://service.local:8020
predicates:
- Path=/myPath/api/myRoute/test
filters:
- name: StripPrefix
args:
parts: 2
How can I define, into yml file, this route public and another one authenticated through jwk-uri directed to keycloak?
I see nothing about security rules in spring-cloud-gateway configuration doc.
I believe you'll have to either:
let all traffic go through gateway and handle security on each service
write a web-security configuration class and define a SecurityWebFilterChain bean.
use spring-addons-webflux-jwt-resource-server to define permitAll() routes from properties (other routes requirering user to be authenticated)
Only last solution would work with yaml file configuration only:
in pom, replace spring-boot-starter-oauth2-resource-server with
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-webflux-jwt-resource-server</artifactId>
<version>${com.c4-soft.springaddons.version}</version>
</dependency>
in yaml, replace spring.security.oauth2.resource-server with
com.c4-soft.springaddons.security:
jwt-issuers:
- location: https://httpd.keycloak.local:443/keycloak/realms/myRealm
permit-all:
- /myPath/api/myRoute/test
- /public/**
Note that trying to access "non-public" routes without valid authorization would result in 401 (unauthorized) and not in 302 (redirect to login). In my opinion, client should unsure requests to protected routes are issued with Authorization header or handle unauthorized with a redirection to authorization-server and a retry.
Also note that spring-security-oauth2-webflux-addons will auto-configure more than just permit-all routes (CORS, CSRF and authorities mapping for instance).
Last, I haven't tried it yet with spring-cloud-gateway. please let me know how it goes ;-)
I am configuring multiple authentication clients for a spring-boot application, and am attempting to override the default redirect URI using:
spring.security.oauth2.client.registration.google.redirectUri={baseUrl}/oauth2/callback/{registrationId}
and then setting the following in SecurityConfig:
http.oauth2Login()
.authorizationEndpoint().baseUri("/oauth2/authorize")
.and()
.redirectionEndpoint().baseUri("/oauth2/callback/*")
However, this is not working - when accessing {baseUrl}/oauth2/authorize/google, the client is redirected to
https://accounts.google.com/o/oauth2/v2/auth/oauthchooseaccount
?response_type=code
&client_id<clientId>
&scope=email%20profile
&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Flogin%2Foauth2%2Fcode%2Fgoogle
&flowName=GeneralOAuthFlow
with redirect uri parameter "{baseUrl}/login/oauth2/code/google" which is the default set by spring security when redirectUri is not set. If I switch to using application.yml with the below configuration:
spring:
security:
oauth2:
client:
registration:
google:
redirectUri: "{baseUrl}/oauth2/callback/{registrationId}"
it works fine. However, for various reasons I want to stick with the application.properties format. Any idea why the setting is ignored?
I have designed a micro service prototype using below technologies
Eureka Server
a service
Spring Cloud API Gateway
Above mentioned service are registered in the Eureka Server
API Gateway routing Configuration
server.port=8080
eureka.client.serviceUrl.defaultZone = http://localhost:8083/eureka
spring.application.name=ApiGateway
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
spring.cloud.gateway.routes[0].id=service1
spring.cloud.gateway.routes[0].uri=lb://MICROSERVICE1
spring.cloud.gateway.routes[0].predicates[0]=Path=/service1/**
The service Configuration
server.port=8081
server.address=127.0.0.1
eureka.client.serviceUrl.defaultZone = http://localhost:8083/eureka
spring.application.name=MicroService1
error.whitelabel.enabled= false
Controller
#RestController
#RequestMapping("/service1")
public class HomeController {
#GetMapping("/message")
public String hello() {
return "response from micro service1";
}
}
When I send a request to the gateway it's showing the below error
2020-12-16 22:26:09.770 ERROR 16700 --- [ctor-http-nio-3] a.w.r.e.AbstractErrorWebExceptionHandler : [d3334561-1] 500 Server Error for HTTP GET "/service1/message"
java.net.UnknownHostException: failed to resolve 'LAPTOP-KU56B6A8' after 3 queries
at io.netty.resolver.dns.DnsResolveContext.finishResolve(DnsResolveContext.java:1013) ~[netty-resolver-dns-4.1.55.Final.jar:4.1.55.Final]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ HTTP GET "/service1/message" [ExceptionHandlingWebHandler]
How can we solve the above issue?
Add eureka.instance.hostname=localhost in both the microservices instances this will work and not give an error
i have modified the API Gate Way routing Configuration like below
spring.cloud.gateway.routes[0].id=service1
spring.cloud.gateway.routes[0].uri=http://localhost:8081/service1/
spring.cloud.gateway.routes[0].predicates[0]=Path=/service1/**
Now is working fine
Add this bean in your API gateway and you are good to go.
#Bean
public HttpClient httpClient() {
return HttpClient.create().resolver(DefaultAddressResolverGroup.INSTANCE);
}
Add below to both gateway and individual microservice fix the issue
eureka.instance.hostname=localhost
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:8010/eureka/
Add in your application.properties:
spring.cloud.discovery.enabled=true
I have modified my .yaml file with this configuration.
Issue resolved for me.
**server:
port: 9999
spring:
application:
name: gateway-ws
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: userService
uri: http://user-service/
predicates:
- Path=/user/**
- id: contactService
uri: http://contact-service/
predicates:
- Path=/contact/**
eureka:
client:
service-url:
defaultZone: http://localhost:8085/eureka**
This is the only solution works among all of the answers above.
#Bean
public HttpClient httpClient() {
return HttpClient.create().resolver(DefaultAddressResolverGroup.INSTANCE);
}
This is the default behavior, so no effect.
spring.cloud.discovery.enabled=true
This has nothing to do with the discovery client.
It is related with the discovery server.
eureka.instance.hostname=localhost
So if you don't know, just don't mess it up with wrong directions.
hello jebji if you still have this problem
add
spring.cloud.discovery.enabled=true
in application.properties
Only Add the following property into your API gateway:
spring.cloud.discovery.enabled=true
Make sure you already added DevTool maven dependency into your API gateway project but if not then restart it.
add flowing property in application.property file of all eruka client microservice and api gateway , i face same issue and resolve doing same activity
spring.cloud.discovery.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id= true
spring.cloud.gateway.discovery.locator.enabled= true
eureka.instance.hostname=localhost
after adding all the above properties then also if you are facing issue then try the below one,
don't use lb://albums_service , but use lb://albums-service .Because URI don‘t support underline.
You can add the following in application.yml file
spring:
cloud:
gateway:
routes:
- id: test-service
uri: lb://MICROSERVICE1
predicates:
- Path=/microservice1/**
filters:
- RewritePath=/microservice1/(?<segment>.*), /$\{segment}
with this it should works.
Like let say if your microservice1 is url is
localhost:8081/service1/message
then you can define the base path of your microservice1 in api-gateway by setting up the path as i did in above configuration.
The error message is "failed to resolve 'LAPTOP-KU56B6A8'".
This is an DNS issue.
You can set eureka.instance.prefer-ip-address=true in the service.
So it will register with its ip at Eureka and the DNS issue can be avoided.
This is actually the same issue as this QUESTION.
With this ANSWER
I want enable default routing in my spring cloud gateway (no zuul) by service ids registered in eureka (application names) but I always got 404 error.
In my chat service's bootstrap.yml
I have defined application name
spring:
application:
name: chat-service
and in application properties:
eureka:
instance:
preferIpAddress: true
client:
healthcheck:
enabled: true
serviceUrl:
defaultZone: http://${EUREKA_HOST:localhost}:${EUREKA_PORT:8761}/eureka/
when I go to eureka's dashboard I can see registered my chat service and gateway as well.
Eureka's configuration in gateway application is same as chat service, but I also have this:
spring:
application:
name: gateway
cloud:
gateway:
discovery:
locator:
enabled: true
and next I also tried add explicit routes which din't work as well, but if I have discovery locator enabled set to true this shouldn't be needed right?
routes:
- id: chat-service-route
uri: lb://chat-service
predicates:
- Path=**
I created test endpoint which I tried call directly on chat service and also with gateway. Direct call works fine so issue will be with routing.
#RestController
#RequestMapping
public class TestController {
#GetMapping
public String test() {
return "chat-service ready";
}
}
What I did wrong? I am little desperate. I am using spring boot 2.2.2 and Hoxton.RELEASE cloud dependencies version
Try removing explicit routes and add below property to application yml. This works for me.
spring:
cloud:
gateway:
discovery:
locator:
lower-case-service-id: true
I have created a Single Page Application using the Spring Tutorial for making one with AngularJS and OAuth and such found here:
https://spring.io/guides/tutorials/spring-security-and-angular-js/#_multiple_ui_applications_and_a_gateway_single_page_application_with_spring_and_angular_js_part_vi
This is the application.yml files for the SPA application:
security:
user:
password: none
oauth2:
client:
accessTokenUri: localhost:7777/uaa/oauth/token
userAuthorizationUri: localhost:7777/uaa/oauth/authorize
clientId: acme
clientSecret: acmesecret
resource:
user-info-uri: localhost:7777/uaa/user
zuul:
routes:
resource:
path: /resource/**
url: localhost:9000/resource
user:
path: /user/**
url: localhost:7777/uaa/user
eureka:
client:
serviceUrl:
defaultZone: ${vcap.services.eureka-service.credentials.uri:127.0.0.1:8761}/eureka/
---
spring:
profiles: cloud
eureka:
instance:
hostname: ${APPLICATION_DOMAIN}
nonSecurePort: 80
I want to know how I would change the zuul routes and the user-info-uri so that I don't have to specify the urls and all this can be done by using the service-id. I looked at the tutorial for using eureka here:
https://spring.io/blog/2015/01/20/microservice-registration-and-discovery-with-spring-cloud-and-netflix-s-eureka
but I don't quite understand how I can achieve my goal without adding all the java to my code, because the basic eureka server already seems to register all my services.
Had issue with using eureka service id, userInfoUri used to throw UnknownHost Exception all the time, #LoadBallanced restTemplate did not solve my issue, solution was to set prefer-token-info to false ( if true - no load ballancing for oauth )
security.oauth2.resource.service-id={Service ID as at eureka server registered}
security.oauth2.resource.userInfoUri= http://${security.oauth2.resource.service-id}/user/me
security.oauth2.resource.loadBalanced=true
security.oauth2.resource.prefer-token-info=false
no port number needed if service ID used , but needed if ip or host used
If I do understand your question correct you can just use the config file in this pattern:
zuul:
routes:
<service_id>:
path: /path/**
For example (if your oauth-service is registered as auth):
zuul:
routes:
auth:
path: /user/**
Zuul will leverage Eureka and find the endpoints for the services. In addition to that it will provide client-side load-balancing.