I using Spring 5. Have problems with encoding, like this:
ÐÐµÑ Ñакого бÑенда: 1000. ÐожалÑйÑÑа, пÑовеÑÑÑе запÑоÑ.
I tried to fix encoding this way:
#Configuration
public class AppInit extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
addEncodingFilter(servletContext);
super.onStartup(servletContext);
}
...
private void addEncodingFilter(ServletContext servletContext) {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
EnumSet<DispatcherType> dispatcherTypes
= EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ERROR);
FilterRegistration.Dynamic characterEncoding
= servletContext.addFilter("characterEncoding", characterEncodingFilter);
characterEncoding.addMappingForUrlPatterns(dispatcherTypes, true, "/*");
characterEncoding.setAsyncSupported(true);
}
}
but have no result. Please, give me ready solution how to set encoding for Spring 5.
Try using spring Beans.
#Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
encodingFilter.setEncoding("UTF-8");
encodingFilter.setForceEncoding(true);
registrationBean.setFilter(encodingFilter);
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
Solution was:
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasename("i18n/messages");
source.setDefaultEncoding("UTF-8");
return source;
}
I needed simply to set default encoding for i18n files.
Related
I'm trying to pass some information from a browser to my server and end up having '????' symbols. I thoroughly searched for this problem on the Internet and StackOverFlow but in the end nothing helped me. My assumption is Thymeleaf/Spring continues to use ISO_8859_1 charset instead of UTF-8.
DispatcherServlet(CharacterEncodingFilter):
public class DispatcherServlet extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void onStartup(ServletContext aServletContext) throws ServletException {
super.onStartup(aServletContext);
registerCharacterEncodingFilter(aServletContext);
registerHiddenFieldFilter(aServletContext);
}
private void registerHiddenFieldFilter(ServletContext aContext) {
aContext.addFilter("hiddenHttpMethodFilter",
new HiddenHttpMethodFilter()).addMappingForUrlPatterns(null, true, "/*");
}
private void registerCharacterEncodingFilter(ServletContext aContext) {
EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST,
DispatcherType.FORWARD);
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
FilterRegistration.Dynamic characterEncoding = aContext.addFilter("characterEncoding",
characterEncodingFilter);
characterEncoding.addMappingForUrlPatterns(dispatcherTypes, true, "/*");
}
}
My Configuration:
public class ApplicationConfiguration implements WebMvcConfigurer {
private final ApplicationContext applicationContext;
private final Environment environment;
#Autowired
public ApplicationConfiguration(ApplicationContext applicationContext,
Environment environment) {
this.applicationContext = applicationContext;
this.environment = environment;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry resourceHandlerRegistry) {
resourceHandlerRegistry.addResourceHandler("/resources/**").
addResourceLocations("/resources/");
}
// Themyleaf resolver,engine etc
#Bean
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("/WEB-INF/view/");
resolver.setSuffix(".html");
resolver.setCharacterEncoding("UTF-8");
resolver.setTemplateMode("HTML5");
return resolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver());
engine.addDialect(new SpringSecurityDialect());
engine.setEnableSpringELCompiler(true);
return engine;
}
#Override
public void configureViewResolvers(ViewResolverRegistry registry) {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setContentType("text/html; charset=UTF-8");
resolver.setCharacterEncoding("UTF-8");
registry.viewResolver(resolver);
}
}
As I've already said, I'm trying to make Spring/Thymeleaf use UTF-8 charset instead of the standard one. Certainly I could use something like
byte[] bytes = content.getBytes(StandardCharsets.ISO_8859_1);
content = new String(bytes, StandardCharsets.UTF_8);
but I'd like to have it done by Spring/Thymeleaf if it's possible.
UPD: Whatever the input data is I always see '???' in the console. For instance: 'Текстовая строка123' produces '???µ???????????°?? ???????????°123' (except for Non Cyrillic symbols)
I want to post http://localhost:8080/TestSpringMVCAndStuff/hi/zzz?input=abcdef&crap=1234567 to a /hi/* address.
However, I keep getting 404 error.
Is there a way to make this work?
WebAppInitializer
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(WebAppConfiguration.class);
ctx.setServletContext(container);
{
ServletRegistration.Dynamic servlet = container.addServlet(
"dispatcherHi", new DispatcherServlet(ctx));
servlet.setLoadOnStartup(1);
servlet.addMapping("/hi/*");
}
}
}
AnotherController
#Controller
#RequestMapping("/hi/*")
public class AnotherController {
#Autowired(required=false)
#RequestMapping(method = RequestMethod.POST)
public void processRequest(HttpServletRequest request, HttpServletResponse response) {
try{
response.getOutputStream().write("hello world!".getBytes());
}
catch(Exception e){
e.printStackTrace();
}
}
}
WebAppConfiguration
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.test.springmvc")
public class WebAppConfiguration extends WebMvcConfigurerAdapter {
/*
* Configure View Resolver
*/
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
/*
* Configure MessageSource to provide internationalized messages
*
*/
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
/*
* Configure ResourceHandlers to serve static resources like CSS/ Javascript etc...
*
*/
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
}
}
In this Context Initializer servlet mapping is /hi/* so each and every request to the server will be like http://localhost:8080/TestSpringMVCAndStuff/hi
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(WebAppConfiguration.class);
ctx.setServletContext(container);
{
ServletRegistration.Dynamic servlet = container.addServlet(
"dispatcherHi", new DispatcherServlet(ctx));
servlet.setLoadOnStartup(1);
servlet.addMapping("/hi/*");
}
}
}
so that there is no need of #RequestMapping("/hi/*") this because /hi is already appended in context path so after hi/ only need a request Mapping #RequestMapping( value="/zzz")
#Controller
#RequestMapping("/**")
public class AnotherController {
#RequestMapping(value="/zzz",method = RequestMethod.POST)
#ResponseBody
public String processRequest(HttpServletRequest request, HttpServletResponse response) {
String result="";
try{
result="hello world";
}
catch(Exception e){
e.printStackTrace();
}
return result;
}
}
inCase he change servlet Mapping like this means servlet.addMapping("/") he should Map Request for /hi in Controller and futher request also
I have configured Basic Authentication my Spring-Boot application. Everything is Java Config, no xml.
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// Authenticate username -> admin, password -> admin & set role as "ROLE_USER"
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
// All Requests should be Authenticated
.anyRequest().authenticated()
.and()
// Enable Basic Authentication
.httpBasic()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/main", true)
.loginProcessingUrl("/session")
.usernameParameter("Username").passwordParameter("Password")
.and()
.logout().logoutUrl("/logout").permitAll()
.and().csrf().disable();
}
}
It's configured for both Basic authentication and normal form login. When I tested the basic authentication from Rest-Client on Firefox, I can access the secure url "/main". But in the response headers, I'm getting Set-Cookie: JSESSIONID=301225C7AE7C74B0892887389996785D;.
I don't want cookies to be generated for basic authentication. I want true Stateless session for Basic Authentication. Do note that I need cookies to be generated for form-login to work, so disabling cookies is not an option. I know about the create-session="stateless" in xml configuration, but is there any way to do the same in Java config so that Basic Authentication is Stateless and Form-Authentication is Statefull..?
I know about the create-session="stateless" in xml configuration, but is there any way to do the same in Java config so that Basic Authentication is Stateless and Form-Authentication is Statefull..?
You can do the following.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
And For your problem following custom Java Config can be used.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
#Inject
UserDetailsService userService;
#Bean
public AuthenticationManager authenticationManager() throws Exception {
AuthenticationManager authenticationManager = new ProviderManager(
Arrays.asList(authenticationProvider()));
return authenticationManager;
}
#Bean
public AuthenticationProvider authenticationProvider() throws Exception {
CustomAuthenticationProvider authenticationProvider = new CustomAuthenticationProvider();
authenticationProvider.setUserDetailsService(userService);
authenticationProvider.setSaltSource(saltSource());
authenticationProvider.setPasswordEncoder(passwordEncoder());
authenticationProvider.afterPropertiesSet();
return authenticationProvider;
}
#Bean
public SaltSource saltSource() throws Exception {
ReflectionSaltSource saltSource = new ReflectionSaltSource();
saltSource.setUserPropertyToUse("salt");
saltSource.afterPropertiesSet();
return saltSource;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new Md5PasswordEncoder();
}
#Bean
public FilterChainProxy springSecurityFilterChain()
throws ServletException, Exception {
List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>();
securityFilterChains.add(new DefaultSecurityFilterChain(
new AntPathRequestMatcher("/login**")));
securityFilterChains.add(new DefaultSecurityFilterChain(
new AntPathRequestMatcher("/resources/**")));
securityFilterChains.add(new DefaultSecurityFilterChain(
new AntPathRequestMatcher("/api/**"),
securityContextPersistenceFilterASCFalse(),
basicAuthenticationFilter(), exceptionTranslationFilter(),
filterSecurityInterceptor()));
securityFilterChains.add(new DefaultSecurityFilterChain(
new AntPathRequestMatcher("/**"),
securityContextPersistenceFilterASCTrue(), logoutFilter(),
usernamePasswordAuthenticationFilter(),
exceptionTranslationFilter(), filterSecurityInterceptor()));
return new FilterChainProxy(securityFilterChains);
}
#Bean
public SecurityContextPersistenceFilter securityContextPersistenceFilterASCTrue() {
return new SecurityContextPersistenceFilter(
new HttpSessionSecurityContextRepository());
}
#Bean
public SecurityContextPersistenceFilter securityContextPersistenceFilterASCFalse() {
HttpSessionSecurityContextRepository httpSessionSecurityContextRepository = new HttpSessionSecurityContextRepository();
httpSessionSecurityContextRepository.setAllowSessionCreation(false);
return new SecurityContextPersistenceFilter(
httpSessionSecurityContextRepository);
}
#Bean
public ExceptionTranslationFilter exceptionTranslationFilter() {
ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(
new LoginUrlAuthenticationEntryPoint("/login"));
AccessDeniedHandlerImpl accessDeniedHandlerImpl = new AccessDeniedHandlerImpl();
accessDeniedHandlerImpl.setErrorPage("/exception");
exceptionTranslationFilter
.setAccessDeniedHandler(accessDeniedHandlerImpl);
exceptionTranslationFilter.afterPropertiesSet();
return exceptionTranslationFilter;
}
#Bean
public UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter()
throws Exception {
UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = new UsernamePasswordAuthenticationFilter();
usernamePasswordAuthenticationFilter
.setAuthenticationManager(authenticationManager());
usernamePasswordAuthenticationFilter.setAllowSessionCreation(true);
SimpleUrlAuthenticationSuccessHandler successHandler = new SimpleUrlAuthenticationSuccessHandler(
"/index");
successHandler.setAlwaysUseDefaultTargetUrl(true);
usernamePasswordAuthenticationFilter
.setAuthenticationSuccessHandler(successHandler);
usernamePasswordAuthenticationFilter
.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler(
"/login?error=true"));
usernamePasswordAuthenticationFilter
.setAuthenticationDetailsSource(new CustomWebAuthenticationDetailsSource());
usernamePasswordAuthenticationFilter.afterPropertiesSet();
return usernamePasswordAuthenticationFilter;
}
#Bean
public FilterSecurityInterceptor filterSecurityInterceptor()
throws Exception {
FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
filterSecurityInterceptor
.setAuthenticationManager(authenticationManager());
filterSecurityInterceptor
.setAccessDecisionManager(accessDecisionManager());
filterSecurityInterceptor.setRunAsManager(runAsManager());
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
List<ConfigAttribute> configs = new ArrayList<ConfigAttribute>();
configs.add(new org.springframework.security.access.SecurityConfig(
"isAuthenticated()"));
requestMap.put(new AntPathRequestMatcher("/**"), configs);
FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource = new ExpressionBasedFilterInvocationSecurityMetadataSource(
requestMap, new DefaultWebSecurityExpressionHandler());
filterSecurityInterceptor
.setSecurityMetadataSource(filterInvocationSecurityMetadataSource);
filterSecurityInterceptor.afterPropertiesSet();
return filterSecurityInterceptor;
}
public AffirmativeBased accessDecisionManager() throws Exception {
List<AccessDecisionVoter> voters = new ArrayList<AccessDecisionVoter>();
voters.add(new WebExpressionVoter());
voters.add(new RoleVoter());
AffirmativeBased affirmativeBased = new AffirmativeBased(voters);
affirmativeBased.setAllowIfAllAbstainDecisions(false);
affirmativeBased.afterPropertiesSet();
return affirmativeBased;
}
#Bean
public RunAsManager runAsManager() throws Exception {
RunAsManagerImpl runAsManager = new RunAsManagerImpl();
runAsManager.setKey("V_RUN_AS");
runAsManager.afterPropertiesSet();
return runAsManager;
}
#Bean
public LogoutFilter logoutFilter() throws ServletException {
List<LogoutHandler> handlers = new ArrayList<LogoutHandler>();
handlers.add(new CookieClearingLogoutHandler("JSESSIONID"));
handlers.add(new SecurityContextLogoutHandler());
LogoutFilter logoutFilter = new LogoutFilter("/login",
handlers.toArray(new LogoutHandler[] {}));
logoutFilter.afterPropertiesSet();
return logoutFilter;
}
#Bean
public RequestContextFilter requestContextFilter() {
return new RequestContextFilter();
}
#Bean
public BasicAuthenticationFilter basicAuthenticationFilter()
throws Exception {
BasicAuthenticationEntryPoint basicAuthenticationEntryPoint = new BasicAuthenticationEntryPoint();
basicAuthenticationEntryPoint.setRealmName("V_REALM");
basicAuthenticationEntryPoint.afterPropertiesSet();
BasicAuthenticationFilter basicAuthenticationFilter = new BasicAuthenticationFilter(
authenticationManager(), basicAuthenticationEntryPoint);
basicAuthenticationFilter
.setAuthenticationDetailsSource(new CustomWebAuthenticationDetailsSource());
basicAuthenticationFilter.afterPropertiesSet();
return basicAuthenticationFilter;
}
}
This configuration creates two different authentication mechanism.
For any request starting with /api/* it will be using a basicAuthenticationFilter and securityContextPersistenceFilterASCFalse with Session Creation False.
For any request starting with /* it will be using a usernamePasswordAuthenticationFilter and securityContextPersistenceFilterASCTrue with Session Creation True.
You can make use of this and alter it to cater your problem.
For anyone else that comes across this, here's something else to check.
I was hitting this same problem with Spring Boot and even with
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
I was still seeing JSESSIONID cookies being set. In my case (using JWT), the missing piece seemed to be setting setAllowSessionCreation on the HttpSessionSecurityContextRepository object, like this:
public class StatelessAuthenticationFilter extends GenericFilterBean {
private final MyTokenAuthService authenticationService;
private SecurityContextRepository repository = new HttpSessionSecurityContextRepository();
protected final Logger logger = LoggerFactory.getLogger(getClass().getName());
public StatelessAuthenticationFilter(MyTokenAuthService authenticationService) {
this.authenticationService = authenticationService;
((HttpSessionSecurityContextRepository) repository).setAllowSessionCreation(false);
}
}
What pointed me this were these lines in HttpSessionSecurityContextRepository:
private boolean allowSessionCreation = true;
I am using Java configuration and know I can add a custom filter like this:
#Override
public void onStartup(final ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
DelegatingFilterProxy filter = new DelegatingFilterProxy("springSecurityFilterChain");
filter.setServletContext(servletContext);
filter.setContextAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher");
servletContext.addFilter("corsFilter", CorsFilter.class).addMappingForUrlPatterns(null, false, "/*");
servletContext.addFilter("springSecurityFilterChain", filter).addMappingForUrlPatterns(null, true, "/*");
}
This works fine, but feels like I am hacking around the edges of Spring.
What I would like to do is add my CORS filter to the DelegationFilterProxy.
In my root config I added:
#Bean
#Order(Ordered.HIGHEST_PRECEDENCE)
public Filter CorsFilter() {
return new CorsFilter();
}
and removed the adding of the custom filter in onStartup. Now it does not work at all.
How do I add a bean to my filter chain and make sure it is called first?
You can add it same way as you are adding springSecurityFilteChain in your onStartUp() method like...
#Override
public void onStartup(final ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
DelegatingFilterProxy filter1 = new DelegatingFilterProxy("springSecurityFilterChain");
filter1.setServletContext(servletContext);
filter1.setContextAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher");
DelegatingFilterProxy filter2 = new DelegatingFilterProxy("corsFilter");
servletContext.addFilter("springSecurityFilterChain", filter1).addMappingForUrlPatterns(null, true, "/*");
servletContext.addFilter("corsFilter",filter2).addMappingForUrlPatterns(null, false, "/*");
}
To simply globally enable the CORS filter using Java config, try this: https://spring.io/blog/2015/06/08/cors-support-in-spring-framework#javaconfig
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
I'm trying to implement what Bryan Hansen taught in his spring mvc video serie for internationalization; I'm writing the same code as e does but mine does not work properly and I'm getting the following error message :
No message found under code 'student.name' for locale 'en_US'
my startup class is as follow :
public class Startup implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
//get the context :
WebApplicationContext context = this.getContext();
//creates a listener :
ContextLoaderListener listener = new ContextLoaderListener(context);
servletContext.addListener(listener);
//register as servlet :
ServletRegistration.Dynamic dispatcherServlet = servletContext.addServlet("dispatcher", new DispatcherServlet(context));
dispatcherServlet.setLoadOnStartup(1);
dispatcherServlet.addMapping("/");
}
private WebApplicationContext getContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(WebConfig.class);
return context;
}
}
and my web config class is as follow :
#Configuration
#ComponentScan(basePackages = "ga.shahab.controllers")
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry resource) {
resource.addResourceHandler("/res/**").addResourceLocations("/resources/");
resource.addResourceHandler("/app/*.js").addResourceLocations("/resources/app/");
}
// START : Internationalization I18n
#Bean
public MessageSource MessageSource() {
// ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Bean
public LocaleResolver localResolver() {
SessionLocaleResolver localeResolver = new SessionLocaleResolver();
localeResolver.setDefaultLocale(Locale.ENGLISH);
return localeResolver;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor changeInterceptor = new LocaleChangeInterceptor();
changeInterceptor.setParamName("language");
registry.addInterceptor(changeInterceptor);
}
// END : Internationalization I18n
#Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/Views/");
resolver.setSuffix(".jsp");
return resolver;
}
}
I'm using spring messages like this :
<spring:message code="student.name"></spring:message>
and the structure of my project is as follow :
1.src/main/java
2.src/main/resources
my internationalization files are in the second folder( resources )
and finally my messages file includes just one line of code :
student.name=Name
but my project does not work for internationalization.
what's wrong with my sample ?!