How to add Filter in Spring MVC Javaconfig correctly - spring

I'm a bit confused about adding Filters in Spring MVC with JavaConfig.
For example using the ResourceUrlEncodingFilter and the ShallowEtagHeaderFilter.
I've seen people doing this
public class MvcWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
FilterRegistration.Dynamic filterRegistration = servletContext.addFilter("resourceUrlEncodingFilter",
new ResourceUrlEncodingFilter());
filterRegistration.setInitParameter("encoding", "UTF-8");
filterRegistration.setInitParameter("forceEncoding", "true");
filterRegistration.addMappingForUrlPatterns(null, true, "/*");
}
}
(do i have to create a Filter registration for every single Filter?)
or this
#Override
protected Filter[] getServletFilters() {
return new Filter[] { new CharacterEncodingFilter() };
}
or this
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
servletContext.addFilter("name", CharacterEncodingFilter.class)
.addMappingForUrlPatterns(null, false, "/*");
}
or
public class AppConfig extends WebMvcConfigurerAdapter {
/* ... */
#Bean
public ShallowEtagHeaderFilter shallowEtagHeaderFilter() {
return new ShallowEtagHeaderFilter();
}
}
or even this:
public class AppConfig extends WebMvcConfigurerAdapter {
/* ... */
#Bean
public Filter shallowEtagHeaderFilter() {
return new ShallowEtagHeaderFilter();
}
}
(seems not to work in my app)
So what is the best approach for adding Filters and keep code clean?
(What are the differences)

Related

Intercept request & send to external

I am developing a Spring boot project.
One example of my controller:
#Controller
public class RestController {
#GetMapping(value = "/student/{studentId}")
public #ResponseBody Student getData(#PathVariable Integer studentId) {
Student student = new Student();
student.setName("Peter");
student.setId(studentId);
return student;
}
}
I have other endpoints implemented as well.
I need to intercept every request hits my endpoints & forward the request to another service (microservice), in other words, I need to forward each request to another web app running on the same local machine as current one, based on the response from that service to decide whether proceed forward the request or not.
My rough idea is to use HandlerIntercept , but I am not sure whether I am going to the right direction. Could someone please share some experiences what is the best way to achieve this? It would be nice if you could show some sample code. Thanks in advance.
You can use HandlerInterceptorAdapter.
Define the Interceptor as below.
#Component
public class RequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object object) throws Exception {
System.out.println("In preHandle we are Intercepting the Request");
System.out.println("____________________________________________");
//Call the Rest API and Validate
if (conditionsNotMet()) {
response.getWriter().write("something");
response.setStatus(someErrorCode);
return false;
}
}
}
Register the HandlerInterceptorAdapter
#Configuration
public class PathMatchingConfigurationAdapter extends WebMvcConfigurerAdapter {
#Autowired
private RequestInterceptor requestInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestInterceptor);
}
}
using WebMvcConfigurer
#Configuration
public class PathMatchingConfigurationAdapter implements WebMvcConfigurer {
#Autowired
private RequestInterceptor requestInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestInterceptor);
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry resourceHandlerRegistry) {
}
#Override
public void addCorsMappings(CorsRegistry corsRegistry) {
}
#Override
public void addViewControllers(ViewControllerRegistry viewControllerRegistry) {
}
#Override
public void configureViewResolvers(ViewResolverRegistry viewResolverRegistry) {
}
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> list) {
}
#Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> list) {
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> list) {
}
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> list) {
}
#Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {
}
#Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {
}
#Override
public Validator getValidator() {
return null;
}
#Override
public MessageCodesResolver getMessageCodesResolver() {
return null;
}
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false);
}
#Override
public void configureAsyncSupport(AsyncSupportConfigurer asyncSupportConfigurer) {
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer defaultServletHandlerConfigurer) {
}
#Override
public void addFormatters(FormatterRegistry formatterRegistry) {
}
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
}
}

Unable to override LocaleResolver bean in spring boot

I have a pure REST spring boot application, which I'm trying to inject my own implementation of the AcceptHeaderLocaleResolver class:
public class SmartLocaleResolver extends AcceptHeaderLocaleResolver implements InitializingBean {
#Override
public Locale resolveLocale(HttpServletRequest request) {
// Some code...
}
}
And I'm injecting the bean like so:
#EnableTransactionManagement()
#SpringBootApplication(scanBasePackages = { "app.core.i18n" })
public class Application extends SpringBootServletInitializer {
private static Class<Application> applicationClass = Application.class;
#Autowired
private FhngHibernateInterceptor interceptor;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
.allowedOrigins("*")
;
}
#Bean
public LocaleResolver localeResolver() {
SmartLocaleResolver slr = new SmartLocaleResolver();
slr.setDefaultLocale(Locale.forLanguageTag("en"));
return slr;
}
};
}
}
However, no matter what I do, my own resolveLocale() method is never called. I set a breakpoint inside DispatcherServlet::initLocaleResolver() to confirm this:
private void initLocaleResolver(ApplicationContext context) {
try {
// This always throww the NoSuchBeanException
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
}
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
}
The bean is never found so a default stragety is used.
What might be missing?
Thank you,
Move your LocaleResolver bean outside of your WebMvcConfigurer bean.
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
.allowedOrigins("*");
}
};
}
#Bean
public LocaleResolver localeResolver() {
SmartLocaleResolver slr = new SmartLocaleResolver();
slr.setDefaultLocale(Locale.forLanguageTag("en"));
return slr;
}
The locale of your bean (pardon the pun) will not be picked up by Component Scanning if you define it there.

Spring configuration for webapps without spring MVC

I'm trying to configure my multi-module project for using Spring without Spring MVC.
Here is the project hierarchy :
brewspberry-rpm-parent
---- brewspberry-api (containing webservices)
---- brewspberry-core (containing services and DAOs)
---- brewspberry-webapp (containing web pages, servlets, ...)
brewspberry-core is a maven dependency of webapp.
What I try to do is being able to autowire core beans in webapp. I use Java-based configuration.
Here is my Spring webapp initializer :
public class SpringWebappInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer implements
WebApplicationInitializer {
public void onStartup(ServletContext servletContext)
throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.setServletContext(servletContext);
// rootContext.setConfigLocation("net.brewspberry.util");
rootContext.register(SpringCoreConfiguration.class);
//servletContext.addListener(new ContextLoaderListener(rootContext));
getWebAppContext(servletContext);
}
private void getWebAppContext(ServletContext servletContext) {
// now the config for the Dispatcher servlet
AnnotationConfigWebApplicationContext mvcContext = new AnnotationConfigWebApplicationContext();
// mvcContext.setConfigLocation("net.brewspberry.util.config");
mvcContext.register(SpringWebappConfiguration.class);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet(
"DispatcherServlet", new DispatcherServlet(mvcContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("*.do");
}
#Override
protected Filter[] getServletFilters() {
return null; // new Filter[] { new AuthentificationFilter() };
}
#Override
protected Class<?>[] getRootConfigClasses() {
// TODO Auto-generated method stub
return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
// TODO Auto-generated method stub
return null;
}
#Override
protected String[] getServletMappings() {
// TODO Auto-generated method stub
return null;
}
}
The configuration class is :
#Configuration
#EnableWebMvc
#ComponentScan({ "net.brewspberry" })
public class SpringWebappConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("forward:/login.jsp");
}
#Bean(name = "viewResolver")
public InternalResourceViewResolver getViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
I would like that servlets could inject services from Brewspberry-core module.
I tried a solution from a previous post in SO that consisted in creating an Abstract Servlet containing this :
#Override
public void init(ServletConfig arg0) throws ServletException {
// Autowire beans in webapp
final AutowireCapableBeanFactory autowireCapableBeanFactory = WebApplicationContextUtils
.getWebApplicationContext(servletContext)
.getAutowireCapableBeanFactory();
autowireCapableBeanFactory.autowireBean(this);
}
I tried several things but still I get a NullPointerException when getting servletContext :
from arg0.getServletContext()
by autowiring it
I precise that core configuration works in tests. The issue I got is with webapp to core configuration
Update
By removing overriden onStartup method and adding both config classes to getRootConfigClasses(), servletContext is created :
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{SpringCoreConfiguration.class, SpringWebappConfiguration.class};
}
You are extending AbstractAnnotationConfigDispatcherServletInitializer but are trying very hard not to use the way it should be used.
Replace your class with the following
public class SpringWebappInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {SpringCoreConfiguration.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] {SpringWebappConfiguration.class};
}
#Override
protected String[] getServletMappings() {
return new String[] {"*.do"};
}
}
That will register all that is needed (including the proper servlet mapping) and will make the code for the servlet you already have work.
The main issue is the fact that you have overridden the onStartup method basicaly destroying all the features of the AbstractAnnotationConfigDispatcherServletInitializer. That already creates a ContextLoaderListener and a DispatcherServlet for you.

sessionRegistry.getAllPrincipals() is empty

I trying to get all logged-in users using sessionRegistry in my Spring-MVC application, i found a lot of posts and answers on how to resolve it, but i could not fix it. I'm using configuration by annotation.
I'm new to spring mvc and i want to learn a best practice, so all comments about other configurations or about my code are welcome.
Here is my code
#Configuration
#ComponentScan(basePackages = {"com.uno"})
#Import({ SecurityConfig.class })
#EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
#Bean
public TilesViewResolver tilesViewResolver() {
TilesViewResolver resolver = new TilesViewResolver();
resolver.setViewClass(TilesView.class);
resolver.setOrder(1);
return resolver;
}
#Bean
public TilesConfigurer tilesConfigurer() {
TilesConfigurer tilesConfigurer = new TilesConfigurer();
tilesConfigurer.setCompleteAutoload(true);
tilesConfigurer.setCheckRefresh(true);
return tilesConfigurer;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations(
"/resources/");
}
#Bean
SessionFactory sessionFactory() {
org.hibernate.cfg.Configuration configuration = new org.hibernate.cfg.Configuration();
configuration.configure();
LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(
dataSource());
builder.scanPackages("com.uno.domain").addProperties(
configuration.getProperties());
return builder.buildSessionFactory();
}
#Bean
public DriverManagerDataSource dataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/uno");
ds.setUsername("root");
return ds;
}
#Bean
public HibernateTransactionManager transactionManager() {
return new HibernateTransactionManager(sessionFactory());
}
#Bean
UserDao userDao() {
return new UserDaoImpl();
}
#Bean
UserService userService() {
return new UserServiceImpl();
}
#Bean
RoleDao roleDao() {
return new RoleDaoImpl();
}
#Bean
RoleService roleService() {
return new RoleServiceImpl();
}
#Bean
ConnexionSucessHandler connexionSuccessHandler() {
return new ConnexionSucessHandler();
}
#Bean
PersistentTokenRepository remmeberMeTokenRepository() {
JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();
db.setDataSource(dataSource());
return db;
}
/* Localization section */
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
#Bean
LocaleResolver localeResolver() {
SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
sessionLocaleResolver.setDefaultLocale(new Locale("en"));
return sessionLocaleResolver;
}
#Bean
LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
return localeChangeInterceptor;
}
#Bean
ControllerClassNameHandlerMapping controllerClassNameHandlerMapping() {
ControllerClassNameHandlerMapping controllerClassNameHandlerMapping = new ControllerClassNameHandlerMapping();
Object[] interceptors = new Object[] { localeChangeInterceptor() };
controllerClassNameHandlerMapping.setInterceptors(interceptors);
return controllerClassNameHandlerMapping;
}
#Bean
ReloadableResourceBundleMessageSource messageSource() {
ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource = new ReloadableResourceBundleMessageSource();
reloadableResourceBundleMessageSource.setBasename("resources/i18n/messages");
reloadableResourceBundleMessageSource.setDefaultEncoding("UTF-8");
return reloadableResourceBundleMessageSource;
}
/* Localization section */
#Bean
CommonsMultipartResolver filterMultipartResolver(){
return new CommonsMultipartResolver();
}
}
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("userService")
UserService userDetailsService;
#Autowired
ConnexionSucessHandler connexionSucessHandler;
#Autowired
SessionRegistry sessionRegistry;
#Autowired
PersistentTokenRepository remmeberMeTokenRepository;
#Autowired
ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlAuthenticationStrategy;
#Autowired
ConcurrentSessionFilter concurrentSessionFilter;
#Autowired
RegisterSessionAuthenticationStrategy registerSessionAuthenticationStrategy;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN").antMatchers("/mailManagement/**")
.hasAnyRole("USER", "ADMIN").antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.antMatchers("/login/**").permitAll().and().formLogin().successHandler(connexionSucessHandler)
.loginPage("/login").failureUrl("/login?error").usernameParameter("username")
.passwordParameter("password").and().logout().invalidateHttpSession(true).deleteCookies("JSESSIONID")
.logoutUrl("/logout").logoutSuccessUrl("/login?logout").and().csrf().and().exceptionHandling()
.accessDeniedPage("/403").and().rememberMe().rememberMeParameter("uno-remember-me")
.rememberMeCookieName("uno-remember-me").tokenValiditySeconds(1296000)
.tokenRepository(remmeberMeTokenRepository).and().sessionManagement()
.sessionAuthenticationStrategy(concurrentSessionControlAuthenticationStrategy).maximumSessions(-1);
}
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
#Bean
public ConcurrentSessionFilter concurrentSessionFilter(){
return new ConcurrentSessionFilter(sessionRegistry);
}
#Bean
public ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlAuthenticationStrategy(){
return new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry);
}
#Bean
public RegisterSessionAuthenticationStrategy registerSessionAuthenticationStrategy(){
return new RegisterSessionAuthenticationStrategy(sessionRegistry);
}
}
public class SecurityInitializer extends
AbstractSecurityWebApplicationInitializer {
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
#Override
protected boolean enableHttpSessionEventPublisher() {
return true;
}
}
public class MvcWebApplicationInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class[] getRootConfigClasses() {
return new Class[] { AppConfig.class };
}
#Override
protected Class[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addListener(new RequestContextListener());
super.onStartup(servletContext);
}
}
public class ConnexionSucessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
#Autowired
UserProfile userProfile;
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication auth) throws IOException,
ServletException {
RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
Collection authorities = auth.getAuthorities();
userProfile.loadUser(auth.getName());
for(GrantedAuthority grantedAuthority : authorities){
switch (grantedAuthority.getAuthority()) {
case "ROLE_ADMIN":
redirectStrategy.sendRedirect(request, response, "/admin");
break;
case "ROLE_USER":
redirectStrategy.sendRedirect(request, response, "/user");
break;
}
}
}
}
problem resolved.
I was loading the same configuration twice in both ContextLoaderListener and DispatcherServlet.

Basic Authentication not enabling on Spring Boot Application

I am working on a REST WebService. Now as per the requirement, I need to make the webservice secure. To do that I tired to use Spring Security in my application by enabling basic authentication. But Still i can access the app without authentication. I am using only annotations to do all the configuration. Please help me
UPDATE1: I am Deploying it on JBOSS EAP 6.4
Here is the WebSecurityConfig.java which enables the security
#Configuration
#EnableGlobalMethodSecurity
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${ldap.url}")
private String ldapUrl;
#Value("${ldap.userDN}")
private String ldapuserDN;
#Value("${ldap.password}")
private String ldapPassword;
#Override
#Order(1)
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
//.and().csrf().disable();
}
#Override
#Order(2)
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
LdapAuthenticationProvider provider = new LdapAuthenticationProvider(bindAuth());
return provider;
}
#Bean
public BindAuthenticator bindAuth(){
BindAuthenticator bindAuther=new BindAuthenticator(ldapContext());
String [] patternList=new String[1];
patternList[0]="cn={0},ou=ColtUsers,dc=eu,dc=colt";
bindAuther.setUserDnPatterns(patternList);
return bindAuther;
}
#Bean
public DefaultSpringSecurityContextSource ldapContext(){
DefaultSpringSecurityContextSource context= new DefaultSpringSecurityContextSource("ldap://host:390");
context.setUserDn("dndeatils");
context.setPassword("password");
return context;
}
}
here is the appconfig.java
#Configuration
#ComponentScan("package")
#EnableWebMvc
public class AppConfig {
}
Here is the WebAppInitializer
public class WebAppInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(AppConfig.class);
ctx.register(WebSecurityConfig.class);
ctx.setServletContext(servletContext);
Dynamic dynamic = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
dynamic.addMapping("/*");
dynamic.setLoadOnStartup(1);
}
}
You need to add DelegatingFilterProxy in your WebAppInitializer
public class WebAppInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(AppConfig.class);
ctx.register(WebSecurityConfig.class);
ctx.setServletContext(servletContext);
// This ContextLoaderListener
servletContext.addListener(new ContextLoaderListener(ctx));
// This Filter
servletContext.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain")).addMappingForUrlPatterns(null, false, "/*");
Dynamic dynamic = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
dynamic.addMapping("/*");
dynamic.setLoadOnStartup(1);
}
}

Resources