There seems to be weird behavior which I can't seem to pinpoint the reason for. When I access a particular url I get a 404 response while other urls that are handled by the same controller class works. I have to add a trailing / to the end of the url in order for the method to be called.
This method DOES NOT get called when accessing localhost:8080/newprofile
#RequestMapping(value="/newprofile", method=RequestMethod.GET)
public String newProfile(Model model, Principal principal) {
return "newprofile";
}
However, this one DOES get called when accessing localhost:8080/login
#GetMapping("/login")
public String login() {
return "login";
}
I have tried both GetMapping and RequestMapping but the methods are never called.
Both methods are contained in my controller class
#Controller
public class HomeResources {
//login
//new profile
}
There is a setting responsible for such behavior:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.html#setUseTrailingSlashMatch-boolean-
Just turn it off:
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseTrailingSlashMatch(false);
}
}
Note that the default behaviour of setUseTrailingSlashMatch changed from true to false since 6.0 in order to support the deprecation of the property.
If you want this to be enabled you have to set it to true now. But as it is marked deprecated, probably best not to do it and instead follow the advice in this Spring Boot 3.0.0 M4 Release Notes
Developers should instead configure explicit redirects/rewrites through a proxy, a Servlet/web filter, or even declare the additional route explicitly on the controller handler (like #GetMapping("/some/greeting", "/some/greeting/") for more targeted cases.
Spring Boot 3+ TLDR
#Configuration
public class WebConfiguration implements WebMvcConfigurer {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseTrailingSlashMatch(true);
}
}
}
Be sure to use as above for Spring Boot 3+ (that uses Spring 6)
to have URL ending with '/' still being processed by mapping in Controllers without ending '/'.
Longer answer:
Note for the snippet the value is exactly true
and #EnableWebMvc is not used with Spring Boot
(as it would in fact disable autoconfiguration for web MVC)
That was exactly recommended in
https://github.com/spring-projects-experimental/spring-boot-migrator/issues/206
"Spring Boot 3.0.0 M4 Release Notes"
And because there are reasons why Spring became less forgiving,
( see "Deprecate trailing slash match and change default value from true to false"
https://github.com/spring-projects/spring-framework/issues/28552 )
I think we should better to think as bad habit to use URLs ending with '/', that now require extra attention.
Related
I have a created an annotation that verifies whether certain security aspects are correct.
For example, #RequireClientCertificate, with an Aspect implementation RequireClientCertificateAspect that verifies whether the correct HTTP header is indeed passed in to the Spring REST controller.
This works totally fine, IF the RequireClientCertificateAspect is actually loaded, i.e. if its package is mentioned somewhere in #ComponentScan().
However, if someone forgets to add this package to #ComponentScan, or the aspect is moved to another package, or someone (accidentally) removes the package from #ComponentScan, the aspect bean isn't loaded, and the aspect is completely not applied.
I have this annotation in a common library, shared by several microservices, so it's easy for one of the microservices to accidentally get it wrong. In that case, no checking of the client certificate would be performed.
Question: How can I enforce that, if the #RequireClientCertificate annotation is used, its corresponding Aspect implementation is also loaded?
Simplified usage example:
#Controller
#RequestMapping(value = "/v1.0", produces = MediaType.APPLICATION_JSON_VALUE)
#RequireClientCertificate
public class SomeApiController {
#ResponseBody
#PostMapping("/get-token/")
public ResponseEntity<Token> getToken() {
return ResponseEntity.ok(...get token...);
}
}
Simplified version of the aspect:
#Aspect
#Component
public class RequireClientCertificateAspect {
#Around("execution(* (#RequireClientCertificate *).*(..))")
public Object requireClientCertificateAspectImplementation(ProceedingJoinPoint joinPoint) throws Throwable {
... verify request header ...
try {
return joinPoint.proceed();
finally {
... some other things I need to check ...
}
}
}
Things I've tried/considered:
I can detect 'usage' of the annotation by adding a static field with an initializer to the interface. For example:
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface RestFactoryGatewaySecurityContext {
static public final boolean dummy = SomeClass.checkAspectIsLoaded();
}
However, such initializers are called very early, and I don't think Spring DI is 'up and running' far enough at that stage that I could even reliably determine whether the aspect bean is loaded.
Another option is to use #Autowired to inject the RequireClientCertificateAspect bean on the main app class explicitly. If somehow the bean isn't on the component scan, this will prevent Spring from instantiating the app.
So that does work, but requires someone to explicitly add this 'dummy' autowire, which in itself is easy to forget, in addition to being a bit 'ugly'.
If you use spring boot you can create your own starter.
Create file META-INF/spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.MyCustomConfiguration
Then just add any validation you want to your configuration
#Configuration
public class MyCustomConfiguration{
}
You can #Autowired your RequireClientCertificateAspect into it, which will cause error if it isn't defined.
You can create method with #PostConstruct and do any validation you want.
If you went so far as creating custom starter, you can just initialize your bewns there.
More about it you can read here
I am using Spring Boot with Jetty.
I configure the context-path:
server.servlet.context-path=/test
When accessing http://localhost:8080/test, it doesn't work. But going to http://localhost:8080/test/ works.
Is /test and /test/ different? How can I access http://localhost:8080/test
I think you are looking for something similar to setUseTrailingSlashMatch
Docs:
Whether to match to URLs irrespective of the presence of a trailing slash. If enabled a method mapped to "/users" also matches to "/users/".
The default value is true.
Code:
public class Config extends WebMvcConfigurationSupport {
#Override
protected void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false)
.setUseTrailingSlashMatch(false);
}
}
I added #preAuthorize("#securityService.hasPermission()") on top of my controller method and created related bean with given method-
#Configuration
public class SecurityConfig {
#Bean
public PreAuthorizationSecurityService securityService() {
return new PreAuthorizationSecurityService();
}
}
Now, I'm using MockMvc for testing my controller. When I add SecurityConfig.class to ContextConfiguration of the test class,controller method checks for permission. But, I want to mock PreAuthorizationSecurityService in a way that my class doesn't have to import SecurityConfig.class for testing purposes.
If you just want to declare a mock of your service instead of importing the entire SecurityConfig, you can easily do so by declaring this in your test config :
#Configuration
public class TestConfig {
#Bean
public PreAuthorizationSecurityService mockedSecurityService() {
//providing you use Mockito for mocking purpose
return mock(PreAuthorizationSecurityService.class);
}
}
And then set your mock to return true when needed. You can also provide your own mocked implementation that always return true.
That said, this is not the usual way to use Spring Security. And you should consider refactoring your app to use the standard role-based system, it will save you some trouble.
I don't know what your PreAuthorizationSecurityService looks like and if this can apply in your situation but in most cases, it should and that's what you should aim for.
With this standard role based approach, Spring Security Test (v4+ I think) easily allows you to mock connected user with given roles with annotation like #WithMockUser.
I have a MVC Java configuration but the HandlerInterceptor is not excluding some patterns.
At the line marked with xxx, if
1) I add both addPatterns("/**") and excludePathPatterns("*.ecxld") to the HandlerInterceptor's InterceptorRegistration, the HandlerInterceptor.preHanlde() is NOT invoked at all. e.g .addPathPatterns("/**").excludePathPatterns("*.ecxld")
2) I add only excludePathPatterns("*.ecxld") to the HandlerInterceptor's InterceptorRegistration, the HandlerInterceptor.preHanlde() is still executed.
(the other interceptors are invoked fine).
Any pointers appreciated.
Thanks
#Configuration
public class MyMVCConfigurerAdapter extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(getInterceptorOne());
registry.addInterceptor(getMyHandlerInterceptor())
.excludePathPatterns("*.ecxld"); // **xxx**
registry.addInterceptor(getInterceptorTwo()
);
}
The patterns you specify for include and exclude are ant bases path expressions and not normal URL expressions as you would express in web.xml to map a servlet or filter for instance.
To make an exclude work you have to also include an include path (as you already noticed with your second remark). Next change your exclude pattern to /**/*.ecxld.
Your current expression *.ecxld would match file.ecxld but it will not match /file.ecxld or even /foo/file.ecxld. The /**/ part takes care of that. However to make it work it also requires an includePathExpression (the code checks if there is an includePathExpression when not it is ignoring the excludePathExpression).
So in short change your configuration to the following should solve your problem.
#Configuration
public class MyMVCConfigurerAdapter extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(getInterceptorOne());
registry.addInterceptor(getMyHandlerInterceptor())
.includePathPatterns("/**")
.excludePathPatterns("/**/*.ecxld");
registry.addInterceptor(getInterceptorTwo()
);
}
I know this was a long while ago but I just stumbled over the same problem. During my search I found the following blog. There it is mentioned that if the interceptors are configured as beans they will be automatically added to the chain.
I am now using Spring 4.1.x so there might be a difference but what solved it for me was the following:
(I tried to avoid defining them as a spring beans. It didn't help.)
I configured the interceptors as spring beans (so I could autowire stuff into them see here)
I changed my definition as follows:
registry.addInterceptor(getMyHandlerInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/user/login");
By putting the addPathPatterns before the excludePathPatterns the behavior of the interceptor suddenly worked fine.
After debugging, the interceptors are not executed in the order they were added. In the above example, interceptorOne, then interceptorTwo, then the handler (with the excluded pattern) was executed.
I run into this trouble, can't exclude the path.
After I debug, found out is because Spring security redirect to "/login", because of "/login" is included in "/**", that why cannot access.
Solution is add the login & logout link as exclude paths too!
I've faced a similar problem while working with SpringBoot.
How I solved this problem?
I made a method to return a new instance of the Interceptor. And you will have to write the excludePathPatters after the addPathPattern method of the registry.
Here's the code snippet:
#Bean
public AuthInterceptor getAuthInterceptor() {
return new AuthInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(**getAuthInterceptor()**)
.addPathPatterns("/**")
.excludePathPatterns("/login/**");
}
I hope this helps.
I am having trouble getting multiple aspects to fire in a specific order. I am using the RequestProcessor to do certain things on every incoming request on my controllers, which have a certain parameter
Then I have some specific annotations that I will be adding to only certain methods within my controllers.
FYI I am using Eclipse, Tomcat, Maven and spring with java/annotation based configs. I use Tomcat and the WebApplicationInitializer to load my context, dispatcher, listeners etc. I have no web.xml. I can post that or the pom.xml if needed too.
The problem I am getting is that a method that satisfies both the ProcessRequest pointcut and the someAnnotation pointcut is firing the someAnnotation method first even though the order is specified to fire ProcessRequest first. There is some properties set in the ProcessRequest that is needed in some other annotations.
Here is a simplified version of my code.
Spring Config Class
#Configuration // Enable Spring Annotation Configuration. Equivalent to <context:annotation-config/>
#EnableAspectJAutoProxy
#EnableCaching // Enable Spring caching
#EnableWebMvc // Enable Spring MVC Annotation. Equivalent to <mvc:annotation-driven />.
#ComponentScan(basePackages = {"xxx.yyy.zzz"}) // Scan for Spring Components. Equivalent to <context:component-scan>
public class WebAppConfig extends WebMvcConfigurerAdapter {
// Other Bean logic here
#Bean
public RequestProcessor requestProcessor() {
return new RequestProcessor();
}
#Bean
public AnnotationAspect annotationAspect() {
return new AnnotationAspect();
}
}
Aspect #1
#Aspect
#Order(0)
public class RequestProcessor {
#Pointcut("execution(* xxx.yyy.zzz.api..*.*(xxx.yyy.zzz.objects.api.Request,..)) && args(request)")
public void pointcut(Request<?> request) {}
#Before("pointcut(request)")
public void processRequest(Request<?> request) throws IOException, BadSignatureException {
// Some logic here that is independent of other and needs to run before other aspect which references annotation
}
}
Aspect #2
#Aspect
#Order(1)
public class AnnotationAspect {
#Before("#annotation(xxx.yyy.zzz.annotation.SomeAnnotation)")
public void someAnnotation() {
// Log for this annotation
}
// Some other annotation methods here
}
Also tried this format implements Ordered
#Aspect
public class RequestProcessor implements Ordered {
#Override
public int getOrder() {
return 0;
}
#Pointcut("execution(* xxx.yyy.zzz.api..*.*(xxx.yyy.zzz.objects.api.Request,..)) && args(request)")
public void pointcut(Request<?> request) {}
#Before("pointcut(request)")
public void processRequest(Request<?> request) throws IOException, BadSignatureException {
// Some logic here that is independent of other and needs to run before other aspect which references annotation
}
}
I read over this post and some others but couldn't find anything relevant that worked.
Ordering aspects with Spring AOP && MVC
****UPDATE****
So I have been reading through the AspectJ docs on declaring precedence so I thought I would give it a whirl. I created a simple aspect that only declares precedence and it works just fine.
Here is my Precedence Aspect:
public aspect AspectPrecedence {
declare precedence : RequestProcessor, SomeAspect;
}
I am not going to submit this as answer just yet because I would like to understand why the annotation or "implements Ordered" are not functioning properly in my project.
Any insight would be greatly appreciated. Thanks!
****UPDATE 2****
For reference this works locally in my eclipse environment and seemed to work when deployed to AWS via WAR file.
#Aspect
#DeclarePrecedence("RequestProcessor, SomeAspect")
public class RequestProcessor {
#Pointcut("execution(* xxx.yyy.zzz.api..*.*(xxx.yyy.zzz.objects.api.Request,..)) && args(request)")
public void pointcut(Request<?> request) {}
#Before("pointcut(request)")
public void processRequest(Request<?> request) throws IOException, BadSignatureException {
// Some logic here that is independent of other and needs to run before other aspect which references annotation
}
}
When using Eclipse with AspectJ support that automatically enables compile time weaving in your IDE. Which means the aspects get woven into your byte code, opposed to Spring which uses proxies to apply aspects.
Using an aspect to declare precedence or using #DeclarePrecedence will only work when using either compile or load time weaving (the latter can be enabled by specifying <context:load-time-weaver/> depending on your container might require some additional configuration). However both should work (you might need to specify the AspectJ compiler as a compiler for the #Aspect classes instead of the default java compiler).
#Order and Ordered only work for the proxy based solution and are ignored by AspectJ.
I think the problem could be that you're using LTW with AspectJ instead AOP with Spring, as #Order is defined for Spring, the container(AspectJ) is not able to determine the ordering of both the advices, so try one of these:
Try flipping the order of #Aspect and #Order annotations
You can try the annotation #DeclarePrecedence from AspectJ and then configuring your aspects into the aop.xml
<aspects>
<aspect name="your.package.RequestProcessor"/>
<aspect name="your.package.AnnotationAspect"/>
<concrete-aspect name="my_aspect_configuration_precedence"
precedence="*..*RequestProcessor, *"/>
</aspects>
I don't know if it's going to work but expect to give you a pointer on that