A web.xml makes registering the order of an application's filters obvious/explicit. But, I'm using Java Config. I define a filter, MyProcessingFilter.java, which extends AbstractAuthenticationProcessingFilter. In the filter chain, I need to make sure that it comes after Spring Security's SecurityContextPersistenceFilter. See essential Spring Security filter ordering.
I'm using Spring Boot. This is how I declare my filter
#Configuration
public class Config {
/*...*/
#Bean
public Filter myProcessingFilter() {
MyProcessingFilter myProcessingFilter = new MyProcessingFilter(AnyRequestMatcher.INSTANCE);
myProcessingFilter.setAuthenticationManager(authenticationManager());
return myProcessingFilter;
}
}
Spring Boot orders this custom filter first.
Basically, I have a custom authentication filter and I need it to come after Spring Security's SecurityContextPersistenceFilter. Any suggestions?
Related
I have a Spring Boot app with a few Controllers I want to track their dependencies (including outbound Http requests). That all works as expected. However, I have one controller for a health check (returning 204) that I do not want telemetry for. All other responses mention custom code components, but according to the documentation, this should be doable within the AI-Agent.xml config.
<BuiltInProcessors>
<Processor type="RequestTelemetryFilter">
<Add name="NotNeededResponseCodes" value="204" />
</Processor>
</BuiltInProcessors>
I notice on the classpath that there are two RequestTelemtryFilter instances (one from ai-core and one from ai-web, neither of which get hit when i debug).
Configuring the Agent (via AI-Agent.xml) is different than configuring custom telemetry (via Applicationinsights.xml). Spring boot + the agent requires the use of a custom Telemetry Processor and pulling into your configuration via #Bean. No additional XML in the AI-Agent is necessary.
public class HealthCheckTelemetryFilter implements TelemetryProcessor
{
public HealthCheckTelemetryFilter()
{
// TODO Auto-generated constructor stub
}
#Override
public boolean process(Telemetry telemetry)
{
RequestTelemetry reqTel = (RequestTelemetry) telemetry;
if(reqTel.getResponseCode().equals(HttpStatus.NO_CONTENT.toString()))
return false;
else
return true;
}
}
NOTE: dont forget appropriate type check
public class Startup
{
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
loggerFactory.AddFile(logFilePath1);
services.AddSingleton<ILoggerFactory>(loggerFactory);
loggerFactory.AddFile(logFilePath2);
services.AddSingleton<ILoggerFactory>(loggerFactory);
}
}
With in the startup.cs class, I create two loggers . Since it has two loggers how can I set the Ilogger data in the controller? can it do using normal way? Or is there any different way to pass logger filename when logged within the controller?
OK, so you want to have two different loggers in a single controller and you want these two loggers to log to different files. The .NET Core logging does not have good support for this scenario so it requires a bit of hacking to achieve this. Whenever I find myself in a situation where I get a a lot of resistance from the framework I'm using I reconsider if what I'm trying to do is good idea and if it is whether I should use another framework so you might want to do the same. With that in mind here is a way to achieve what you want.
Loggers can be identified by a category. In your case you want a single controller to have two different loggers so you have to use ILoggerFactory to create the loggers (you could use the generic ILogger<T> interface but it becomes a bit weird because you need two different types for T):
public class MyController : Controller
{
private readonly ILogger logger1;
private readonly ILogger logger2;
public Controller1(ILoggerFactor loggerFactory)
{
logger1 = loggerFactory.Create("Logger1");
logger2 = loggerFactory.Create("Logger2");
}
}
The categories of the loggers are Logger1 and Logger2.
Each logger will by default log to all the configured providers. You want a logger with one category to log to one provider and a logger with another category to log to another provider.
While you can create filters that are based on category, provider and log level the problem is that you want to use the same provider for both categories. Providers are identified by their type so you cannot create a rule that targets a specific instance of a provider. If you create a rule for the file provider it will affect all configured file providers.
So this is where the hacking starts: You have to create your own provider types that are linked to the files to be able to filter on each file.
.NET Core does not have support for logging to files so you need a third party provider. You have not specified which provider you use so for this example I will use the Serilog file sink together with the Serilog provider that allows you to plug a Serilog logger into the .NET Core logging framework.
To be able to filter on provider you have to create your own provider. Luckily, that is easily done by deriving from the SerilogLoggerProvider:
class SerilogLoggerProvider1 : SerilogLoggerProvider
{
public SerilogLoggerProvider1(Serilog.ILogger logger) : base(logger) { }
}
class SerilogLoggerProvider2 : SerilogLoggerProvider
{
public SerilogLoggerProvider2(Serilog.ILogger logger) : base(logger) { }
}
These two providers does not add any functionality but allows you to create filter that targets a specific provider.
Next step is crating two different Serilog loggers that log to different files:
var loggerConfiguration1 = new LoggerConfiguration()
.WriteTo.File("...\1.log");
var loggerConfiguration2 = new LoggerConfiguration()
.WriteTo.File("...\2.log");
var logger1 = loggerConfiguration1.CreateLogger();
var logger2 = loggerConfiguration2.CreateLogger();
You configure your logging in Main by calling the extension method .ConfigureLogging:
.ConfigureLogging((hostingContext, loggingBuilder) =>
{
loggingBuilder
.AddProvider(new SerilogLoggerProvider1(logger1))
.AddFilter("Logger1", LogLevel.None)
.AddFilter<SerilogLoggerProvider1>("Logger1", LogLevel.Information)
.AddProvider(new SerilogLoggerProvider2(logger2))
.AddFilter("Logger2", LogLevel.None)
.AddFilter<SerilogLoggerProvider2>("Logger2", LogLevel.Information);
})
Each provider (which is associated with a specific file) are added and then two filters are configured for each provider. I find the filter evaluation rules hard to reason about but the two filters added - one with LogLevel.None and another with LogLevel.Information - actually achieves the desired result of ensuring that log messages for the two different categories are routed correctly to the two different providers. If a third provider is added it will not be affected by these filters and messages from both categories will be logged by the third provider.
Having upgraded to spring security 4.2.4 I discovered that StrictHttpFirewall is now the default.
Unfortunately it doesn't play well with spring MVC #MatrixVariable since ";" are not allowed anymore.
How to get around that?
Example:
#GetMapping(path = "/{param}")
public void example(#PathVariable String param,
#MatrixVariable Map<String, String> matrix) {
//...
}
This could be called like this:
mockMvc.perform(get("/someparam;key=value"))
And the matrix map would be populated.
Now spring security blocks it.
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"
at org.springframework.security.web.firewall.StrictHttpFirewall.rejectedBlacklistedUrls(StrictHttpFirewall.java:140)
I could use a custom HttpFirewall that would allow semicolons.
Is there a way to use #MatrixVariable without using forbidden characters?
BTW: the javadoc is incorrect https://docs.spring.io/autorepo/docs/spring-security/4.2.x/apidocs/index.html?org/springframework/security/web/firewall/StrictHttpFirewall.html
Since:
5.0.1
I guess it was backported?
You can dilute the default spring security firewall using your custom defined instance of StrictHttpFirewall (at your own risk)
#Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedSlash(true);
firewall.setAllowSemicolon(true);
return firewall;
}
And then use this custom firewall bean in WebSecurity (Spring boot does not need this change)
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
// #formatter:off
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
...
}
That shall work with Spring Security 4.2.4+, but of-course that brings some risks!
As mentioned by Крис in a comment if you prefer to use a XML approach, you can add the following part to your securityContext.xml (or whatever your spring-security related xml-config is called):
<bean id="allowSemicolonHttpFirewall"
class="org.springframework.security.web.firewall.StrictHttpFirewall">
<property name="allowSemicolon" value="true"/>
</bean>
<security:http-firewall ref="allowSemicolonHttpFirewall"/>
The <bean> part defines a new StrictHttpFirewall bean with the id allowSemicolonHttpFirewall which is then set as default http-firewall in the <security> tag by referencing the id.
I used combination of following two
https://stackoverflow.com/a/48636757/6780127
https://stackoverflow.com/a/30539991/6780127
First one resolved the
The request was rejected because the URL contained a potentially malicious String ";"
Second one Resolved the
Spring MVC Missing matrix variable
As I am using Spring Security with Spring Web I had to do both And the issue is now Resolved.
I found using #MatrixVariable Following Pattern is useful. First in Url {num} has to be mentioned to use it as #MatrixVariable
#RequestMapping(method = RequestMethod.GET,value = "/test{num}")
#ResponseBody
public ResponseEntity<String> getDetail(#MatrixVariable String num){
return new ResponseEntity<>("test"+num, HttpStatus.OK);
}
I've been banging my head over this one for a while now. I've done everything I could in order to find an appropriate solution and followed a lot of Stackoverflow examples and solutions.
First, I'm using annotation based solution. When I annotate my services, prePostEnabled works, but not when I annotate the controllers, it doesn't. Also, even on my services, jsr250Enabled doesn't work.
I've found a lot of case closed by moving the annotation from the security config to the MVC config, which in my case doesn't work.
I've a setup that looks like this: https://github.com/spring-projects/spring-security-oauth-javaconfig/tree/master/samples/oauth2-sparklr
But I use Servlet 3.0 and doesn't have anything in my web.xml.
My SecurityInitializer looks like this:
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
My MVC initializer looks like this:
public class MvcWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{WebSecurityConfig.class, MethodSecurityConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{SpringMvcConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{ApiPaths.API + "/*", "/res.jsp"};
}
My WebSecurity config is initialized like this:
#Configuration
#EnableWebSecurity
#ComponentScan(value = {"com.roler.res.**.server"}, excludeFilters = {
#Filter(type = FilterType.ASSIGNABLE_TYPE, value = SpringMvcConfig.class),
#Filter(type = FilterType.ASSIGNABLE_TYPE, value = MethodSecurityConfig.class),
#Filter(type = FilterType.REGEX, pattern = "com.xyz.*.controller.*")})
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
And my SpringMvcConfig is initialized like this:
#Configuration
#EnableWebMvc
#ComponentScan(value = "com.xyz.**.controller")
public class SpringMvcConfig extends WebMvcConfigurerAdapter {
If you have any ideas, I'm out of juice, Thanks!
The symptoms you describe make me think to a problem of proxying. Annotations works fine on service layer, because services generally implements interfaces, and Spring can easily use a JDK proxy to put the AOP authorizations.
But controllers generally do not implement interfaces. That's the reason why PreAuthorize annotation are more frequently used in service layer. IMHO, you'd better try to use URL pattern based authorization instead of PreAuthorize annotations on controller. The alternative would be to use target class proxying with CGLIB.
To use PreAuthorize and JSR-250 annotations, you must
annotate you spring security configuration class with :
#EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
if you use anywhere else in your application Spring AOP with JDK proxies, make all controller classes in which you want to use method security implement interfaces declaring all protected methods
if you use anywhere else in your application Spring AOP with CGLIB proxies, add proxyTargetClass = true to #EnableGlobalMethodSecurity :
#EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true,
proxyTargetClass = true)
if you want to use CGLIB proxies with Spring version under 3.2, add CGLIB library to your classpath (CGLIB classes are included in Spring 3.2+)
avoid mixing CGLIB and JDK proxying as it is not recommended by Spring documentation : Multiple sections are collapsed into a single unified auto-proxy creator at runtime, which applies the strongest proxy settings that any of the sections (typically from different XML bean definition files) specified. This also applies to the and elements.
To be clear: using 'proxy-target-class="true"' on , or elements will force the use of CGLIB proxies for all three of them.
But anyway, my advice is to try to move method security to service layer which normally already supports AOP.
Two things I noticed (as mentioned on this thread):
prePostEnabled in the annotation to enable Pre/Post annotations
use of CGLib proxies (Serge mentioned this too)
Does your #EnableGlobalMethodSecurity have those two attributes?
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
I have some business validation logic of the form "X is valid IFF Service Y returns Z", where X and Z are known at compile time, and Y's location is loaded from a Spring configuration file.
I'd like to use JSR-303 annotation-based validation, together with the Spring config, so I can write code like the following:
Custom class level constraint annotation:
#MyValidation
public class X { .... }
ConstraintValidator for #MyValidation:
public class MyValidationValidator implements ConstraintValidator<MyValidation, X> {
private MyService service;
public MyService getService() { return service; }
public void setService(MyService serv) { this.service = serv; }
//Validation Logic...
}
Spring config:
<bean id="ServiceY" class="...">
...
</bean>
<bean id="mvv" class="MyValidationValidator">
<property name="service" value="ServiceY" />
</bean>
But my attempts at combining these in that fashion are failing, as the validator's property is not getting set.
Right now, I'm using Spring AOP Interceptors as a workaround, but that's not ideal in my mind.
One of the other questions here, made me think of using a properties file/property, but wouldn't that require me to repeat the service's configuration?
Another mentioned defining the constraint mapping programmatically, but if I'm doing that, I'm probably better-off with my workaround.
Any clues on how to do that dynamic configuration?
You should use Spring's LocalValidatorFactoryBean to set up a Bean Validation validator:
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
A validator set up that way will internally use a ConstraintValidatorFactoryimplementation that performs dependency injection on the created validator instances, just mark the service field in your validator with #Inject or #Autowired. Note that it's not required to set up the constraint validator itself as Spring bean.
You can find more details in the Spring reference guide.