Spring boot , i18n error, wrong implementation of LocaleResolver is choosen - java

I am developing a spring boot based application that supports internationalisation
My env:
Jdk11
Spring boot 2.7.0
Spring Security
Here is my web mvc conf
WebMvcConf.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import java.util.Locale;
#Configuration
public class WebMvcConf implements WebMvcConfigurer {
public static final Locale idnLocale = Locale.forLanguageTag("id-ID");
#Bean
public LocaleResolver defaultLocaleResolver(){
var resolver = new SessionLocaleResolver();
resolver.setDefaultLocale(idnLocale);
return resolver;
}
#Bean
public LocaleChangeInterceptor localeChangeInterceptor(){
var interceptor = new LocaleChangeInterceptor();
interceptor.setIgnoreInvalidLocale(false);
interceptor.setParamName("lang");
return interceptor;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
}
And here is my security config:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.header.HeaderWriterFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper;
#Configuration
#EnableWebSecurity
public class SecurityConf{
#Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors()//enable cors
.and()
.csrf().disable()//disable csrf
.sessionManagement(session->session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))//stateless session (Rest)
.authorizeHttpRequests(authz->authz
.antMatchers(HttpMethod.GET,"/").permitAll()
.antMatchers(HttpMethod.POST,"/users/login","users/register","users/forgot-password").permitAll()
.antMatchers(HttpMethod.PATCH,"/users/password").permitAll()
.anyRequest().authenticated());//authorize any request except ignored endpoint above
return httpSecurity.build();
}
}
Here is the structure of the message properties:
Here is my application.yml content:
Here is the content of messages_en.properties:
rest.welcome.ok=Welcome to ABC Backend Service, have fun!!!
rest.users.login.ok=Successfully Logged in
#Error Message
rest.500.err=Internal Server Error
rest.422.err=Constraint Violation Error
rest.400.err=Invalid request body
rest.required-req-body-missing.err=Required request body is missing
Here is content of messages.properties:
rest.welcome.ok=Selamat Datang di Service ABC, selamat bersenang-senang!!!
rest.users.login.ok=Login sukses
#Error message
rest.500.err=Kesalahan Internal di sistem
rest.422.err=Error pelanggaran constraint
rest.400.err=Kesalahan pada request body
rest.required-req-body-missing.err=Request body (payload) yang dibutuhkan tidak ditemukan
Here is my test to test the message source:
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.MessageSource;
import java.util.Locale;
#SpringBootTest
public class MessageSourceTest {
#Autowired
private MessageSource messageSource;
#BeforeEach
void init(){
}
#Test
public void message_provided_should_correctly_localized(){
String msgEnglish = messageSource.getMessage("rest.500.err",null, new Locale("en"));
String msgIndo = messageSource.getMessage("rest.500.err",null,Locale.forLanguageTag("id-ID"));
Assertions.assertEquals("Internal Server Error",msgEnglish);
Assertions.assertEquals("Kesalahan Internal di sistem",msgIndo);
}
}
And the test is pass:
But when I try to hit a mvc rest controller and add query parameter lang=en , I always got an error response saying Cannot change HTTP accept header - use a different locale resolution strategy
So I debug the LocaleChangeInterceptor and found that spring boot provide a wrong implementation of the LocaleResolver, they provide AcceptHeaderResolver, which what I want is the SessionLocaleResolver as I state it in my WebMvcConf class. See following picture:
Anyone knows what is wrong and how to fix it?any response will be appreciated

Related

Spring MVC - run application in absolute path

I have a demo application of Spring MVC - I just follow udemy course for it.
I have created the first controller and view. All is working fine, however I have one doubt about.
The pom file of application contains:
<groupId>eu.smartgroup</groupId>
<artifactId>spring-demo-mvc</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
So when I run application on tomcat server it starts, but it is available with url: http://localhost:8080/spring_demo_mvc_war
Is there possibility to configure application so it will be available in the root path: http://localhost:8080/ (without project name after slash)?
Edit:
Here is full application.yml
server:
servlet:
contextPath: /
HelloController.java
package eu.test.springdemo.mvc;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
#Controller
#RequestMapping("/")
public class HelloController {
#GetMapping("/")
public String showPage() {
return "main-menu";
}
}
DemoAppConfig.java
package eu.test.springdemo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages="eu.test.springdemo")
public class DemoAppConfig implements WebMvcConfigurer {
// define a bean for ViewResolver
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/view/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
}
Add this line to your application.properties:
server.servlet.contextPath=/
I've tried it on my project and it works.
Write back is it working on your.
Check if you have a controller class that is annotated as such:
#Controller
#RequestMapping("spring_demo_mvc_war")
or
#RestController
#RequestMapping("spring_demo_mvc_war")
If that is the case, then make the string empty or remove the #RequestMapping (you can also change it to whatever else you like).

How to ignore some resources/urls for everyone in spring security?

I have some urls (one as of now) which I would like to allow for every users like registration and login urls. I only want to exclude any url path after register and secure or restrict any other urls.
Please refer the code below:
package com.travelplanner.rest.config;
//COPY
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.User.UserBuilder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.provisioning.UserDetailsManager;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource securityDataSource;
#Bean
public UserDetailsManager userDetailsManager() {
JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager();
jdbcUserDetailsManager.setDataSource(securityDataSource);
return jdbcUserDetailsManager;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(securityDataSource);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().httpBasic().and().csrf().disable()
.authorizeRequests().antMatchers("/register/**").permitAll();
}
}
It is a spring mvc rest in google app engine. I have used the above snippet and it says unauthorised even for the antmatchers. Please help! Thanks in advance.
It's probably the httpBasic() part which is requiring authentication. You should put specific instructions first and more general ones at last, so try with this:
http.cors().and().csrf().disable()
.authorizeRequests().antMatchers("api/users/register/user").permitAll()
.and()
.anyRequest().httpBasic();
This allows everyone to access "api/users/register/user", but requires http authentication for other URLs.

SpringMVC & Spring in Action: can't redirect to home.jsp in chapter 5

I'm learning SpringMVC and maven these days with the book Spring in Action but i have a question now. The default request to "/" should be mapped to "home.jsp" but not. You can also see the same question described in the book forum.
https://forums.manning.com/posts/list/38046.page
Here are the codes:
package spittr.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class SpittrWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{
#Override
protected String[] getServletMappings(){
return new String[]{ "/" };
}
#Override
protected Class<?>[] getRootConfigClasses(){
return new Class<?>[]{ RootConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses(){
return new Class<?>[]{ WebConfig.class };
}
}
package spittr.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Configuration
#EnableWebMvc
#ComponentScan("spittr.web")
public class WebConfig extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
configurer.enable();
}
}
package spittr.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
#Configuration
#ComponentScan(basePackages={"spitter"}, excludeFilters={
#Filter(type=FilterType.ANNOTATION, value=EnableWebMvc.class)
})
public class RootConfig {
}
package spittr.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
public class HomeController {
#RequestMapping(value="/", method=RequestMethod.GET)
public String home(){
return "home";
}
}
When i run this on tomcat 7.0, it should show home.jsp. However it still shows index.jsp.
-------------------- update -------------------------
The following test class indicates the controller class is right and this controller can response to the request "/" with home.jsp. So, where is wrong?
package spittr.web;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
import org.junit.Test;
import org.springframework.test.web.servlet.MockMvc;
public class HomeControllerTest {
#Test
public void testHomePage() throws Exception{
HomeController controller = new HomeController();
MockMvc mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get("/")).andExpect(view().name("home"));
}
}
update or add in your web.xml
<welcome-file-list>home.jsp</welcome-file-list>
If you do not have web.xml you can generate by
Dynamic Web Project –> RightClick –> Java EE Tools –> Generate
Deployment Descriptor Stub.
Also you can do JSP redirect using JSTL libraries in index.jsp to redirect to home.jsp
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:redirect url="/home.jsp"/>
seems to be a path problem.
First check your path mapping in web.xml file for exact url-pattern for which you are redirecting to dispatcher servlet.Assuming that you have home.jsp views created under
/WEB-INF/views/
folder.
I had the same issue while working on Eclipse, Tomcat 9.0 on my Mac. I have spend hours to see (this small code) that where I was wrong.
However, I was able to make it run on Windows machine with Eclipse and Tomcat 8.5 and 9.0 .
I have the code hosted on GitHub at https://github.com/shortduck/ManningChapter5_SpringMVC
This is a Maven project and not Gradle. Also see the HomeController has the value as value = "/home", this is working as well as '/' will work too. If you have having value as '/' make sure index.jsp or any other "home" page is not on the root.
My next target to find out why is this code not working on Mac.

Spring ErrorController issue with Spring security

I am stuck configuring Spring Error mapping with Spring security in Spring Boot 1.3.5 app.
I have follow security config class:
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password("123").roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/admin").hasRole("ADMIN").and()
.csrf().disable();
}
}
And ExceptionController :
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
#Controller
public class ExceptionController implements ErrorController {
private static final String ERROR_PATH = "/error";
#RequestMapping(value = ERROR_PATH)
#ResponseStatus(HttpStatus.NOT_FOUND)
public String error(HttpServletResponse response) {
response.setCharacterEncoding("UTF-8");
return "redirect:/";
}
#Override
public String getErrorPath() {
return ERROR_PATH;
}
}
/admin secure page works fine without ExceptionController . But when I adding it, login form not appearing, I have tried a lot of things, such as ControllerAdvice, but nothing seems work for me. What's proper solution here ?

Spring MVC 4 configuration not finding my controllers

Hi I am new to Spring MVC 4 I am trying to do the java configuration setup but it seems Spring is not finding my Controller it is running fine no errors on startup I can even explicitly call a jsp but if I try to call my controller it does nothing
e.g.
localhost:8080/apollo/hello.jsp <-- this renders fine if I put my JSP in the webapp directory
What I want is to call my login.jsp using my controller
My project structure is
com
+apollo
-WebAppInitializer.java
-WebConfig.java
src
+main
+webapp
-**hello.jsp**
+WEB-INF
+view
-**login.jsp**
here is my Configuration
package com.apollo;
import java.util.Locale;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.apollo")
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
#Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/view/");
resolver.setSuffix(".jsp");
return resolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
/*registry.addResourceHandler("/pdfs/**").addResourceLocations("/WEB-INF/pdf/");
registry.addResourceHandler("/css/**").addResourceLocations("/WEB-INF/css/");*/
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
}
}
Here is my Initializer Class
package com.apollo;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
WebApplicationContext context = getContext();
servletContext.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("*.html");
}
private AnnotationConfigWebApplicationContext getContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(WebConfig.class);
return context;
}
}
here is my Controller
package com.apollo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class LoginController {
#RequestMapping(value="/login")
public String greeting (Model model){
System.out.println("controller???");
model.addAttribute("greeting" , "Hello World");
return "login";
}
}
The basePackages mean the package contains the controller
Here is the problem:
#ComponentScan(basePackages = "com.apollo")
You should change to
#ComponentScan(basePackages = "com.apollo.controller")
If you put controller to modules, you should scan like this
#ComponentScan(basePackages = "com.apollo.**.controller")

Categories

Resources