Spring mvc 3 - HTTPS access - spring

How can I force a page to be accessed via HTTPS only. Need to do this via Spring MVC 3 configuration file.

Spring-security has such a configuration. see here for how to do it. In short - you force the channel to use https:
<http>
<intercept-url pattern="/secure/**" access="ROLE_USER"
requires-channel="https"/>
<intercept-url pattern="/**" access="ROLE_USER"
requires-channel="any"/>
</http>
If you don't want to use spring-security, here's an interceptor that I wrote:
#Component
public class SslInterceptor extends HandlerInterceptorAdapter {
// no need to inject it for now..
private PathMatcher pathMatcher = new AntPathMatcher();
#Value("${base.url.secure}")
private String secureRoot;
#Resource(name="secureLocations")
private List<String> secureLocations;
#Value("${use.ssl}")
private boolean useSsl;
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
if (useSsl && !request.isSecure() && shouldForceSecure(request.getRequestURI())) {
String redirectUrl = secureRoot + request.getRequestURI();
if (request.getQueryString() != null) {
redirectUrl += "?" + request.getQueryString();
}
// force session creation - thus it will be accessible to both the
// secure and the insecure contexts
request.getSession(true);
response.sendRedirect(redirectUrl);
return false;
}
return true;
}
private boolean shouldForceSecure(String path) {
for (String pattern : secureLocations) {
if (pathMatcher.match(pattern, path)) {
return true;
}
}
return false;
}
}

For an annotation based approach without spring security I wrote an interceptor and a new annotation:
/**
* Request mapping annotation to enforce secure or insecure requests.
* Per default the annotated mapping is enforced to be secure.
*
* #see org.springframework.web.bind.annotation.RequestMapping
*/
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Mapping
public #interface RequestProtocol {
boolean secure() default true;
}
So you can simply declare a (here for REST) controller method like this:
#RequestMapping(value = "/secret", method = RequestMethod.GET)
#RequestProtocol(secure = true)
#ResponseBody
public Result doSecure(#Valid Model model) {
return doSomething(model));
}
To enable the mapping use an interceptor redirecting on the wrong protocol. You could also do a more simple handling by just sending a FORBIDDEN response.
/**
* Interceptor to send a redirect on security enforced mappings with wrong type of request.
*
* #see RequestProtocol
*/
class RequestProtocolInterceptor extends HandlerInterceptorAdapter {
private static final int PORT_DIFF = 443 - 80;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
Boolean secure = checkSecure(handler);
if (secure != null && request.isSecure() != secure) {
response.sendRedirect(switchSecure(secure, request.getRequestURL()));
return false;
}
return true;
}
private Boolean checkSecure(Object handler) {
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod)handler;
RequestProtocol annotation = method.getMethodAnnotation(RequestProtocol.class);
if (annotation == null) {
annotation = AnnotationUtils.findAnnotation(method.getBeanType(), RequestProtocol.class);
}
return annotation == null ? null : annotation.secure();
}
return null;
}
private String switchSecure(boolean secure, StringBuffer url) {
int endSchema = url.indexOf("://");
url.replace(0, endSchema, secure ? "https" : "http");
int startPort = url.indexOf(":", endSchema + 3);
if (startPort != -1) {
int endPort = url.indexOf("/", startPort);
int port = Integer.parseInt(url.substring(startPort + 1, endPort));
port += secure ? PORT_DIFF : -PORT_DIFF;
url.replace(startPort + 1, endPort, String.valueOf(port));
}
return url.toString();
}
}
To enable the interceptor on a pure annotation based Spring config, use the WebMvcConfigurerAdapter:
#Configuration
#EnableWebMvc
public class MyConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestProtocolInterceptor());
}
}

You can do it in your Tomcat config.
try adding redirectPort="" in server.xml to the HTTP connector.
Hope it helps.
Update:
This article will explain you how to deal with SSL and has a lot of an examples.
http://tomcat.apache.org/tomcat-6.0-doc/ssl-howto.html

Related

Vaadin + Spring Boot returns 403 Forbidden error on PUT, POST, DELETE requests

Implementing a simple web application using REST Api using Spring Boot + Vaadin. Also, Security is connected in the project, a simple login with a login-password is carried out. Get() requests work fine, but a 403 "Forbidden" error occurs on PUT, POST, DELETE requests.
I tried disabling csrf using the http.httpBasic().and().csrf().disable() method, it does not help, and this is not recommended in production either.
I also tried adding to antMatchers() specifically a request type like this: http.httpBasic().and().authorizeRequests().antMatchers(HttpMethod.POST,"/**").permitAll(), also not helps.
Configuration class:
#EnableWebSecurity
#Configuration
public class SecurityConfig extends VaadinWebSecurity {
private static class SimpleInMemoryUserDetailsManager extends InMemoryUserDetailsManager {
public SimpleInMemoryUserDetailsManager() {
createUser(Manager.withUsername("manager1")
.password("{noop}123")
.roles(ROLE_MANAGER)
.build());
createUser(Manager.withUsername("manager2")
.password("{noop}123")
.roles(ROLE_MANAGER)
.build());
}
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests().antMatchers("/enterprises/\*\*").hasRole(ROLE_MANAGER);
super.configure(http);
setLoginView(http, LoginView.class);
}
#Bean
public InMemoryUserDetailsManager enterprisesService() {
return new SimpleInMemoryUserDetailsManager();
}
}
Rest-controller:
#org.springframework.web.bind.annotation.RestController
#RequestMapping(path = "/")
public class RestController {
#Autowired
private VehiclesRepository vehiclesRepository;
#Autowired
private EnterprisesRepository enterprisesRepository;
#Autowired
private DriversRepository driversRepository;
#Autowired
private ManagersRepository managersRepository;
#GetMapping(
path = "/vehicles",
produces = "application/json")
public VehiclesDto getVehicles() {
VehiclesDto vehiclesDto = new VehiclesDto();
for (Vehicle vehicle : vehiclesRepository.findAll()) {
vehiclesDto.getVehicles().add(vehicle);
}
return vehiclesDto;
}
#GetMapping(
path = "/enterprises",
produces = "application/json")
public #ResponseBody EnterprisesDto getEnterprises(#RequestParam("managerId") String managerId) {
Manager manager = null;
for (Manager managerFromRepo : managersRepository.findAll()) {
if (managerFromRepo.getId().equals(Long.parseLong(managerId))) {
manager = managerFromRepo;
break;
}
}
EnterprisesDto enterprisesDto = new EnterprisesDto();
if (manager == null) return enterprisesDto;
for (Enterprise enterprise : enterprisesRepository.findAll()) {
if (manager.getEnterprises().contains(enterprise.getId()))
enterprisesDto.getEnterprises().add(enterprise);
}
return enterprisesDto;
}
#GetMapping(
path = "/drivers",
produces = "application/json")
public DriversDto getDrivers() {
DriversDto driversDto = new DriversDto();
for (Driver driver : driversRepository.findAll()) {
driversDto.getDrivers().add(driver);
}
return driversDto;
}
#PostMapping("/createVehicle")
public #ResponseBody String createVehicle(#RequestBody String info) {
return "it works!!!";
}
#DeleteMapping("/deleteVehicle")
public #ResponseBody String deleteVehicle(){
return "it works!!!";
}
}
Testing requests through Postman using Basic Authentication.
You can disable CSRF just for your API:
http.csrf().ignoringRequestMatchers(new AntPathRequestMatcher("/enterprises/**"));

How to make Zuul dynamic routing based on HTTP method and resolve target host by 'serviceId'?

How to make Zuul dynamic routing based on HTTP method (GET/POST/PUT...)?
For example, when you need to route the POST request to the different host instead of the default one described in 'zuul.routes.*'...
zuul:
routes:
first-service:
path: /first/**
serviceId: first-service
stripPrefix: false
second-service:
path: /second/**
serviceId: second-service
stripPrefix: false
I.e. when we request 'GET /first' then Zuul route the request to the 'first-service', but if we request 'POST /first' then Zuul route the request to the 'second-service'.
To implement dynamic routing based on HTTP method we can create a custom 'route' type ZuulFilter and resolve 'serviceId' through DiscoveryClient. Fore example:
#Component
public class PostFilter extends ZuulFilter {
private static final String REQUEST_PATH = "/first";
private static final String TARGET_SERVICE = "second-service";
private static final String HTTP_METHOD = "POST";
private final DiscoveryClient discoveryClient;
public PostOrdersFilter(DiscoveryClient discoveryClient) {
this.discoveryClient = discoveryClient;
}
#Override
public String filterType() {
return "route";
}
#Override
public int filterOrder() {
return 0;
}
#Override
public boolean shouldFilter() {
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
String method = request.getMethod();
String requestURI = request.getRequestURI();
return HTTP_METHOD.equalsIgnoreCase(method) && requestURI.startsWith(REQUEST_PATH);
}
#Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
List<ServiceInstance> instances = discoveryClient.getInstances(TARGET_SERVICE);
try {
if (instances != null && instances.size() > 0) {
context.setRouteHost(instances.get(0).getUri().toURL());
} else {
throw new IllegalStateException("Target service instance not found!");
}
} catch (Exception e) {
throw new IllegalArgumentException("Couldn't get service URL!", e);
}
return null;
}
}
#Cepr0's solution is right. Here I am proposing just a simpler way (without service discovery). Assuming you have that route:
zuul:
routes:
first:
path: /first/**
# No need for service id or url
Then you can route requests for '/first' route in 'route' type filter just by setting location to request context.
#Component
public class RoutingFilter extends ZuulFilter {
#Override
public String filterType() {
return ROUTE_TYPE;
}
#Override
public int filterOrder() {
return 0;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() throws ZuulException {
/* Routing logic goes here */
URL location = getRightLocationForRequest();
ctx.setRouteHost(location);
return null;
}
}

login intercepter do not work in spring

all. i was using spring4 in my project. and add and interceptor extends HandlerInterceptorAdapter, then overwrite prehandle method. but i found it does not work when i was doing spring mock test.
i have configure it in springmvc-servlet.xml , like this:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.suerpay.common.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
and here is code of LoginInteceptor:
public class LoginInterceptor extends HandlerInterceptorAdapter {
#Autowired
LoginServiceRedis loginServiceRedis;
#Autowired
UserServiceDB userServiceDB;
Logger logger = LoggerFactory.getLogger(getClass());
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
logger.info("start login interceptor");
if (isLoginRequired(handler)) {
String ticket = request.getHeader(GlobalConstants.TICKET_HEADER);
if (StringUtils.isEmpty(ticket)) {
throw new UnAuthorizedException(ResultCodeConstants.USER_NOT_LOGIN);
}
String userName = loginServiceRedis.getUserNameByTicket(ticket);
Long userId = userServiceDB.getUserIdByName(userName);
if (null == userId) {
throw new UnAuthorizedException(ResultCodeConstants.USER_NOT_LOGIN);
}
ThreadContextHolder.setCurrentUserId(userId);
}
logger.info("finish login interceptor");
return true;
}
private boolean isLoginRequired(Object handler) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
LoginRequired loginRequired = method.getAnnotation(LoginRequired.class);
if (null != loginRequired) {
return true;
}
return false;
}
}
i think i have do everything , but just can not get into breakpoint.
who can tell me why?:(

Spring CookieLocaleResolver: set cookiePath

Is it possible to set cookiePath with the value of application name (automatically)?
For example I have a test.war so it will be available at bla.com/test/ so I want my cookie's path be /test/ and not / that is default value.
Thank you
When you create the CookieLocaleResolver you can set the path, but it will be hard coded.
Ex
<bean id="localeResolver" class="CookieLocaleResolver">
<property name="cookiePath" value="test" />
</bean>
Another possible solution is to override the LocaleResolver
public class MyCookieLocaleResolver extends CookieLocaleResolver {
#Override
public void setLocale(HttpServletRequest request,
HttpServletResponse response, Locale locale) {
if (locale != null) {
// Set request attribute and add cookie.
request.setAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME, locale);
addCookie(response, locale.toString());
} else {
// Set request attribute to fallback locale and remove cookie.
request.setAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME,
determineDefaultLocale(request));
removeCookie(response);
}
}
public void addCookie(HttpServletRequest request,
HttpServletResponse response, String cookieValue) {
Cookie cookie = createCookie(request, cookieValue);
Integer maxAge = getCookieMaxAge();
if (maxAge != null) {
cookie.setMaxAge(maxAge);
}
if (isCookieSecure()) {
cookie.setSecure(true);
}
response.addCookie(cookie);
if (logger.isDebugEnabled()) {
logger.debug("Added cookie with name [" + getCookieName()
+ "] and value [" + cookieValue + "]");
}
}
protected Cookie createCookie(HttpServletRequest request, String cookieValue) {
Cookie cookie = new Cookie(getCookieName(), cookieValue);
if (getCookieDomain() != null) {
cookie.setDomain(getCookieDomain());
}
cookie.setPath(request.getContextPath());
return cookie;
}
}
If you use a ServletContainer >= 2.5 and spring annotation, you can use the following code to get the ContextPath, so you don't have to hardcode:
#EnableWebMvc
#Configuration
#ComponentScan("com.controller")
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired
private ServletContext servletContext;
#Bean
public LocaleResolver localeResolver(){
CookieLocaleResolver resolver = new CookieLocaleResolver();
resolver.setDefaultLocale(new Locale("en"));
resolver.setCookieName("locale");
resolver.setCookiePath(servletContext.getContextPath());
resolver.setCookieMaxAge(31536000);
return resolver;
}
}

Spring MVC Annotated Controller Interface with #PathVariable

Is there any reason not to map Controllers as interfaces?
In all the examples and questions I see surrounding controllers, all are concrete classes. Is there a reason for this? I would like to separate the request mappings from the implementation. I hit a wall though when I tried to get a #PathVariable as a parameter in my concrete class.
My Controller interface looks like this:
#Controller
#RequestMapping("/services/goal/")
public interface GoalService {
#RequestMapping("options/")
#ResponseBody
Map<String, Long> getGoals();
#RequestMapping(value = "{id}/", method = RequestMethod.DELETE)
#ResponseBody
void removeGoal(#PathVariable String id);
}
And the implementing class:
#Component
public class GoalServiceImpl implements GoalService {
/* init code */
public Map<String, Long> getGoals() {
/* method code */
return map;
}
public void removeGoal(String id) {
Goal goal = goalDao.findByPrimaryKey(Long.parseLong(id));
goalDao.remove(goal);
}
}
The getGoals() method works great; the removeGoal(String id) throws an exception
ExceptionHandlerExceptionResolver - Resolving exception from handler [public void
todo.webapp.controllers.services.GoalServiceImpl.removeGoal(java.lang.String)]:
org.springframework.web.bind.MissingServletRequestParameterException: Required
String parameter 'id' is not present
If I add the #PathVariable annotation to the concrete class everything works as expected, but why should i have to re-declare this in the concrete class? Shouldn't it be handled by whatever has the #Controller annotation?
Apparently, when a request pattern is mapped to a method via the #RequestMapping annotation, it is mapped to to the concrete method implementation. So a request that matches the declaration will invoke GoalServiceImpl.removeGoal() directly rather than the method that originally declared the #RequestMapping ie GoalService.removeGoal().
Since an annotation on an interface, interface method, or interface method parameter does not carry over to the implementation there is no way for Spring MVC to recognize this as a #PathVariable unless the implementing class declares it explicitly. Without it, any AOP advice that targets #PathVariable parameters will not be executed.
The feature of defining all bindings on interface actually got implement recently in Spring 5.1.5.
Please see this issue: https://github.com/spring-projects/spring-framework/issues/15682 - it was a struggle :)
Now you can actually do:
#RequestMapping("/random")
public interface RandomDataController {
#RequestMapping(value = "/{type}", method = RequestMethod.GET)
#ResponseBody
RandomData getRandomData(
#PathVariable(value = "type") RandomDataType type, #RequestParam(value = "size", required = false, defaultValue = "10") int size);
}
#Controller
public class RandomDataImpl implements RandomDataController {
#Autowired
private RandomGenerator randomGenerator;
#Override
public RandomData getPathParamRandomData(RandomDataType type, int size) {
return randomGenerator.generateRandomData(type, size);
}
}
You can even use this library: https://github.com/ggeorgovassilis/spring-rest-invoker
To get a client-proxy based on that interface, similarly to how RestEasys client framework works in the JAX-RS land.
It works in newer version of Spring.
import org.springframework.web.bind.annotation.RequestMapping;
public interface TestApi {
#RequestMapping("/test")
public String test();
}
Implement the interface in the Controller
#RestController
#Slf4j
public class TestApiController implements TestApi {
#Override
public String test() {
log.info("In Test");
return "Value";
}
}
It can be used as:
Rest client
Recently I had the same problem. Following has worked for me:
public class GoalServiceImpl implements GoalService {
...
public void removeGoal(#PathVariableString id) {
}
}
i resolved this problem.
ON CLIENT SIDE:
I'm using this library https://github.com/ggeorgovassilis/spring-rest-invoker/. This library generate a proxy from interface to invoke spring rest service.
I extended this library:
I created an annotations and a factory client class:
Identify a Spring Rest Service
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface SpringRestService {
String baseUri();
}
This class generates a client rest from interfaces
public class RestFactory implements BeanFactoryPostProcessor,EmbeddedValueResolverAware {
StringValueResolver resolver;
#Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver = resolver;
}
private String basePackage = "com";
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
createBeanProxy(beanFactory,SpringRestService.class);
createBeanProxy(beanFactory,JaxrsRestService.class);
}
private void createBeanProxy(ConfigurableListableBeanFactory beanFactory,Class<? extends Annotation> annotation) {
List<Class<Object>> classes;
try {
classes = AnnotationUtils.findAnnotatedClasses(basePackage, annotation);
} catch (Exception e) {
throw new BeanInstantiationException(annotation, e.getMessage(), e);
}
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
for (Class<Object> classType : classes) {
Annotation typeService = classType.getAnnotation(annotation);
GenericBeanDefinition beanDef = new GenericBeanDefinition();
beanDef.setBeanClass(getQueryServiceFactory(classType, typeService));
ConstructorArgumentValues cav = new ConstructorArgumentValues();
cav.addIndexedArgumentValue(0, classType);
cav.addIndexedArgumentValue(1, baseUri(classType,typeService));
beanDef.setConstructorArgumentValues(cav);
registry.registerBeanDefinition(classType.getName() + "Proxy", beanDef);
}
}
private String baseUri(Class<Object> c,Annotation typeService){
String baseUri = null;
if(typeService instanceof SpringRestService){
baseUri = ((SpringRestService)typeService).baseUri();
}else if(typeService instanceof JaxrsRestService){
baseUri = ((JaxrsRestService)typeService).baseUri();
}
if(baseUri!=null && !baseUri.isEmpty()){
return baseUri = resolver.resolveStringValue(baseUri);
}else{
throw new IllegalStateException("Impossibile individuare una baseUri per l'interface :"+c);
}
}
private static Class<? extends FactoryBean<?>> getQueryServiceFactory(Class<Object> c,Annotation typeService){
if(typeService instanceof SpringRestService){
return it.eng.rete2i.springjsonmapper.spring.SpringRestInvokerProxyFactoryBean.class;
}else if(typeService instanceof JaxrsRestService){
return it.eng.rete2i.springjsonmapper.jaxrs.JaxRsInvokerProxyFactoryBean.class;
}
throw new IllegalStateException("Impossibile individuare una classe per l'interface :"+c);
}
}
I configure my factory:
<bean class="it.eng.rete2i.springjsonmapper.factory.RestFactory">
<property name="basePackage" value="it.giancarlo.rest.services" />
</bean>
ON REST SERVICE SIGNATURE
this is an example interface:
package it.giancarlo.rest.services.spring;
import ...
#SpringRestService(baseUri="${bookservice.url}")
public interface BookService{
#Override
#RequestMapping("/volumes")
QueryResult findBooksByTitle(#RequestParam("q") String q);
#Override
#RequestMapping("/volumes/{id}")
Item findBookById(#PathVariable("id") String id);
}
ON REST SERVICE IMPLEMENTATION
Service implementation
#RestController
#RequestMapping("bookService")
public class BookServiceImpl implements BookService {
#Override
public QueryResult findBooksByTitle(String q) {
// TODO Auto-generated method stub
return null;
}
#Override
public Item findBookById(String id) {
// TODO Auto-generated method stub
return null;
}
}
To resolve annotation on parameters I create a custom RequestMappingHandlerMapping that looks all interfaces annotated with #SpringRestService
public class RestServiceRequestMappingHandlerMapping extends RequestMappingHandlerMapping{
public HandlerMethod testCreateHandlerMethod(Object handler, Method method){
return createHandlerMethod(handler, method);
}
#Override
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
HandlerMethod handlerMethod;
if (handler instanceof String) {
String beanName = (String) handler;
handlerMethod = new RestServiceHandlerMethod(beanName,getApplicationContext().getAutowireCapableBeanFactory(), method);
}
else {
handlerMethod = new RestServiceHandlerMethod(handler, method);
}
return handlerMethod;
}
public static class RestServiceHandlerMethod extends HandlerMethod{
private Method interfaceMethod;
public RestServiceHandlerMethod(Object bean, Method method) {
super(bean,method);
changeType();
}
public RestServiceHandlerMethod(Object bean, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
super(bean,methodName,parameterTypes);
changeType();
}
public RestServiceHandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
super(beanName,beanFactory,method);
changeType();
}
private void changeType(){
for(Class<?> clazz : getMethod().getDeclaringClass().getInterfaces()){
if(clazz.isAnnotationPresent(SpringRestService.class)){
try{
interfaceMethod = clazz.getMethod(getMethod().getName(), getMethod().getParameterTypes());
break;
}catch(NoSuchMethodException e){
}
}
}
MethodParameter[] params = super.getMethodParameters();
for(int i=0;i<params.length;i++){
params[i] = new RestServiceMethodParameter(params[i]);
}
}
private class RestServiceMethodParameter extends MethodParameter{
private volatile Annotation[] parameterAnnotations;
public RestServiceMethodParameter(MethodParameter methodParameter){
super(methodParameter);
}
#Override
public Annotation[] getParameterAnnotations() {
if (this.parameterAnnotations == null){
if(RestServiceHandlerMethod.this.interfaceMethod!=null) {
Annotation[][] annotationArray = RestServiceHandlerMethod.this.interfaceMethod.getParameterAnnotations();
if (this.getParameterIndex() >= 0 && this.getParameterIndex() < annotationArray.length) {
this.parameterAnnotations = annotationArray[this.getParameterIndex()];
}
else {
this.parameterAnnotations = new Annotation[0];
}
}else{
this.parameterAnnotations = super.getParameterAnnotations();
}
}
return this.parameterAnnotations;
}
}
}
}
I created a configuration class
#Configuration
public class WebConfig extends WebMvcConfigurationSupport{
#Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RestServiceRequestMappingHandlerMapping handlerMapping = new RestServiceRequestMappingHandlerMapping();
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors());
handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
PathMatchConfigurer configurer = getPathMatchConfigurer();
if (configurer.isUseSuffixPatternMatch() != null) {
handlerMapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
}
if (configurer.isUseRegisteredSuffixPatternMatch() != null) {
handlerMapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch());
}
if (configurer.isUseTrailingSlashMatch() != null) {
handlerMapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
}
if (configurer.getPathMatcher() != null) {
handlerMapping.setPathMatcher(configurer.getPathMatcher());
}
if (configurer.getUrlPathHelper() != null) {
handlerMapping.setUrlPathHelper(configurer.getUrlPathHelper());
}
return handlerMapping;
}
}
and I configurated it
<bean class="....WebConfig" />

Resources