I have 3 endpoints:
/api/dummy/{id}
/api/dummy/local
/api/dummy
I would like to allow access to #1 (with path variable) only and forbid for all others.
How to differentiate between path and path variable using AntPathMatcher?
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.csrf()
.disable()
.addFilterAt(new OttWebFilter(myService), SecurityWebFiltersOrder.FIRST)
.authorizeExchange()
....
.pathMatchers(HttpMethod.GET, "/api/dummy/{id:[a-z]+}")
.authenticated()
....
return http.build();
I would say thats not posible: endpoints 1 and 2 collide. If I call endpoint 2, server could think I'm calling endpoint 1 with id = "local"
Related
I'm updating old jHipster gateway to 7.5.0 version. New version uses Spring Cloud Gateway (with Eureka), while the old one used Zuul. In previous version working with Service Discovery having service named 'foo' and path 'bar' would register it without any prefix on the gateway so it could be accessed as:
GATEWAY_URL/foo/bar
right now all services register with 'services/' prefix which results requires to call following url:
GATEWAY_URL/services/foo/bar
I can't find configuration responsible for that. I found a property spring.webservices.path, but changing this to other value does not make any change and in Spring Boot 2.6.3 its value cannot be empty or '/' (but Im not sure if this is a property I should be checking). I also experimented with spring.cloud.gateway.routes in form:
spring:
webservices:
path: /test
main:
allow-bean-definition-overriding: true
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: user-service-route
uri: lb://user
predicates:
- Path=/user/**
but without any luck. Also Im not sure if this is jHipster issue or SCG
I need to change that so that other systems using my API won't need to update their paths, I know I can always add nginx before so that it will rewrite te path but that feels not correct.
This behavior is done by SCG autoconfiguration - GatewayDiscoveryClientAutoConfiguration, it registers DiscoveryLocatorProperties bean with predicate:
PredicateDefinition{name='Path', args={pattern='/'+serviceId+'/**'}}
I didnt want to change autoconfigration, so I did WebFilter that is executed as first one and mutates request path
public class ServicesFilter implements WebFilter {
private final ServicesMappingConfigration mapping;
public ServicesFilter(ServicesMappingConfigration mapping) {
this.mapping = mapping;
}
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
RequestPath path = exchange.getRequest().getPath();
if (path.elements().size() > 1) {
PathContainer pathContainer = path.subPath(1, 2);
if (mapping.getServices().contains(pathContainer.value())) {
ServerHttpRequest mutatedRequest = exchange
.getRequest()
.mutate()
.path("/services" + exchange.getRequest().getPath())
.build();
ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();
return chain.filter(mutatedExchange);
}
}
return chain.filter(exchange);
}}
A spring integration based converter consumes the messages from one system, checks, converts and sends it to the other one.
Should the target system be down, we stop the inbound adapters, but would also like to persist locally or forward the currently "in-flight" converted messages. For that would simply like to reroute the messages from the normal output channel to some "backup"-channel dynamically.
In the docs I have found only the option to route the messages based on their headers ( so on some step before in flow I would have to add those dynamically once the targer system is not availbale), or based on the payload type, which is not really my case. The case with adding dynamically some header, and then filtering it out down the pipe, or during de-/serializing still seems not the best approach for me. I would like rather to be able to turn a switch(on some internal Event) that would then reroute those "in-flight" messages to the "backup"-channel.
What would be a best SI approach to achive this? Thanks!
The router could not only be based on the the payload type or some header. You really can have a general POJO method invocation to return a channel, its name or some routing key which is mapped. That POJO method indeed can check some internal system state and produce this or that routing key.
So, you may have something like this in the router configuration:
.route(myRouter())
where your myRouter is something like this:
#Bean
MyRouter myRouter() {
return;
}
and its internal code might be like this:
public class MyRouter {
#Autowired
private SystemState systemState;
String route(Object payload) {
return this.systemState.isActive() ? "successChannel" : "backupChannel";
}
}
The same can be achieved a simple lambda definition:
.<Object, Boolean>route(p -> systemState().isActive(),
m -> m.channelMapping(true, "sucessChannel")
.channelMapping(false, "backupChannel"))
Also...
private final AtomicBoolean switcher = new AtomicBoolean();
#Bean
public IntegrationFlow flow() {
return IntegrationFlows.from(() -> "foo", e -> e.poller(Pollers.fixedDelay(Duration.ofSeconds(5))))
.route(s -> switcher.get() ? "foo" : "bar")
.get();
}
I can't remember where I saw this but I followed the advice on a blog when setting up my app configuration for my .NET Core MVC application. I created a model like this to hold some settings my app needed:
public class BasePathSettings
{
public string BaseImageFolder { get; set; }
public string BaseApiUrl { get; set; }
}
My StartUp has this...
public void ConfigureServices(IServiceCollection services)
{
...
// this adds the base paths to container
services.Configure<BasePathSettings>(Configuration.GetSection("BasePathSettings"));
....
}
And the appsettings.json has this in it:
"BasePathSettings": {
"BaseImageFolder": "D:\\Images\\",
"BaseApiUrl": "http://localhost:50321/"
},
I inject the controllers that need this info like so....
private readonly BasePathSettings _settings;
public ClientsController(IOptions<BasePathSettings> settings)
{
_settings = settings.Value;
_client = new HttpClient();
_client.BaseAddress = new Uri(_settings.BaseApiUrl);
}
Running this on my localhost everything runs fine.
However, when I deploy this application to Azure I assumed I needed to create an application setting in the General Settings of the app service. So I made an app setting called BasePathSettings and copied the json for the setting into the value:
{ "BaseImageFolder": "imagePath", "BaseApiUrl": "apiUrl" }
It appears that Azure barfs when it's in the ConfigureServices code claiming that the web.config does not have the correct permissions in NTFS. I'm guessing the real culprit is how the json value is being read from the Azure application settings.
Can I even use json there? If so, does it need formatted differently?
Can I even use json there? If so, does it need formatted differently?
To add hierarchical structure settings to Azure web app, we could place a colon between the section name and the key name. For example,
use BasePathSettings:BaseImageFolder to set your folder
use BasePathSettings:BaseApiUrl to set your url
I made an app setting called BasePathSettings and copied the json for the setting into the value
Format should be -
basepathsettings:baseimagefolder (just single slash)
basepathsettings:baseapiurl
If you try to define "BasePathSettings" in a single WebApp setting that takes a json value, the GetSection will return null.
As a workarround, I use this extension method as a replacement of GetSection() :
public static T GetWebAppSection<T>(this IConfiguration config, string key)
where T:class
{
T configValue = config.GetSection(key).Get<T>();
if(configValue == default(T))
{
configValue = JsonConvert.DeserializeObject<T>(config[key]);
}
return configValue;
}
...guess I'm the first to ask about this one?
Say you have the following routes, each declared on a different controller:
[HttpGet, Route("sign-up/register", Order = 1)]
[HttpGet, Route("sign-up/{ticket}", Order = 2)]
... you could do this in MVC 5.0 with the same code except for the Order parameter. But after upgrading to MVC 5.1, you get the exception message in the question title:
Multiple controller types were found that match the URL. This can
happen if attribute routes on multiple controllers match the requested
URL.
So the new RouteAttribute.Order property is only controller-level? I know in AttributeRouting.NET you can do SitePrecedence too. Is the only way to have routes like the above when all actions are in the same controller?
Update
Sorry, I should have mentioned these routes are on MVC controllers, not WebAPI. I am not sure how this affects ApiControllers.
If you know that ticket will be an int you can specify that type in the route to help resolve the route:
[HttpGet, Route("sign-up/register")]
[HttpGet, Route("sign-up/{ticket:int}")]
This approach worked for me, per user1145404's comment that includes a link to Multiple Controller Types with same Route prefix ASP.NET Web Api
In case of Attribute routing, Web API tries to find all the controllers which match a request. If it sees that multiple controllers are able to handle this, then it throws an exception as it considers this to be possibly an user error. This route probing is different from regular routing where the first match wins.
As a workaround, if you have these two actions within the same controller, then Web API honors the route precedence and you should see your scenario working.
There are two ways to fix this:
A regex constraint, like here: MVC Route Attribute error on two different routes
Or a custom route constraint, like here: https://blogs.msdn.microsoft.com/webdev/2013/10/17/attribute-routing-in-asp-net-mvc-5/
You can create custom route constraints by implementing the IRouteConstraint interface. For example, the following constraint restricts a parameter to set of valid values:
public class ValuesConstraint : IRouteConstraint
{
private readonly string[] validOptions;
public ValuesConstraint(string options)
{
validOptions = options.Split('|');
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
object value;
if (values.TryGetValue(parameterName, out value) && value != null)
{
return validOptions.Contains(value.ToString(), StringComparer.OrdinalIgnoreCase);
}
return false;
}
}
The following code shows how to register the constraint:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
var constraintsResolver = new DefaultInlineConstraintResolver();
constraintsResolver.ConstraintMap.Add("values", typeof(ValuesConstraint));
routes.MapMvcAttributeRoutes(constraintsResolver);
}
}
Now you can apply the constraint in your routes:
public class TemperatureController : Controller
{
// eg: temp/celsius and /temp/fahrenheit but not /temp/kelvin
[Route("temp/{scale:values(celsius|fahrenheit)}")]
public ActionResult Show(string scale)
{
return Content("scale is " + scale);
}
}
In my opinion, this isn't great design. There are no judgments about what URL you intended and no specificity rules when matching unless you explicitly set them yourself. But at least you can get your URLs looking the way you want. Hopefully your constraint list isn't too long. If it is, or you don't want to hard-code the route string parameter and its constraints, you could build it programmatically outside the action method and feed it to the Route attribute as a variable.
I have controller
public class BilingController : Controller
{
…
[HttpPost]
public string Result (string data)
{
…
}
…
}
Method Result can be caused only by foreign service process.anypayservice.com
How can I check url, is request from service process.anypayservice.com or other service?
Or allow only this url - process.payservice.com for method Result call
Any attribute or I should write custom?
You can check the HTTP_REFERER header, but note that it can be easily spoofed.
A better approach is to use some sort of token that can be passed in to your service for authentication.