Sunday, 29 December 2019

This week 3/2019 - WebApp with Spring Security

Yesterday I did small exercise. I upgraded one of my old applications from xml (web.xml) configuration to class version as a first step to later upgrading.
There was no problems until I run application and all world could see everything what only an authorized user should see.

I couldn't find simple example which I could I adopt to my needs so I decided to describe this by my self.

Some examples proposed to extend AbstractSecurityWebApplicationInitializer, other proposed to import just import security configuration but it wasn't work as I expected. I created servlet initializer as below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class StockDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

  @Override
  protected Class<?>[] getRootConfigClasses() {
    return new Class<?>[]{MySecurityConfig.class, MyRootConfig.class};
  }

  @Override
  protected Class<?>[] getServletConfigClasses() {
    return new Class<?>[]{MyServletConfig.class};
  }

  @Override
  protected String[] getServletMappings() {
    return new String[]{"/"};
  }

  protected Filter[] getServletFilters() {
    return new Filter[]{new DelegatingFilterProxy("springSecurityFilterChain")};
  }

}


MySecurityConfig I extended with WebSecurityConfigurerAdapter and implemented configure method as below:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@EnableWebSecurity
@Configuration
@ComponentScan("org.my")
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  private final MyLogoutSuccessHandler myLogoutSuccessHandler;
  private final MyAuthenticationProviderImpl myAuthenticationProvider;

  public SecurityConfig(final MyLogoutSuccessHandler myLogoutSuccessHandler,
                        final MyAuthenticationProviderImpl myAuthenticationProvider) {
    this.myLogoutSuccessHandler = myLogoutSuccessHandler;
    this.myAuthenticationProvider = myAuthenticationProvider;
  }


  @Autowired
  public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(myAuthenticationProvider);
  }

  @Override
  public void configure(final WebSecurity web) {
    web.ignoring()
       .antMatchers("/css/**")
       .antMatchers("/img/**");
  }


  @Override
  protected void configure(final HttpSecurity http) throws Exception {
    formLogin(http);
    logout(http);
    headers(http);
    authorizeRequests(http);
    sessionManagement(http);
  }
.......
}


MyServletConfig looks like


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@EnableWebMvc
@Configuration
@ComponentScan({"org.**.mvc"})
public class StockServletConfig implements WebMvcConfigurer {

  @Override
  public void addResourceHandlers(final ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/img/**").addResourceLocations("/img/");
    registry.addResourceHandler("/css/**").addResourceLocations("/css/");
  }

  @Bean
  ViewResolver viewResolver() {
    return new InternalResourceViewResolver("/WEB-INF/jsp/", ".jsp");
  }

  @Bean
  PropertiesFactoryBean propertiesFactoryBean() {
    final PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
    propertiesFactoryBean.setLocation(new ClassPathResource("version.properties"));
    return propertiesFactoryBean;
  }

  @Bean
  ObjectMapper customObjectMapper() {
    return new CustomObjectMapper();
  }

  @Bean
  LocaleResolver localeResolver() {
    final SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
    sessionLocaleResolver.setDefaultLocale(ENGLISH);
    return sessionLocaleResolver;
  }

  @Bean
  public LocaleChangeInterceptor localeChangeInterceptor() {
    final LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
    localeChangeInterceptor.setParamName("lang");
    return localeChangeInterceptor;
  }

  @Override
  public void addInterceptors(final InterceptorRegistry registry) {
    registry.addInterceptor(localeChangeInterceptor());
  }

  @Override
  public void configureMessageConverters(final List<HttpMessageConverter<?>> messageConverters) {
    messageConverters.add(new MappingJackson2HttpMessageConverter(customObjectMapper()));
  }
}


Now I am analysing MyServletConfig and it is possible to do it other way - by adding some specific interceptor. Nevertheless current solution works:)

Any other better ideas ???

--
I used in example Spring/Spring Security 5.2.1 and Apache Tomcat 8.5.50

Wednesday, 25 December 2019

This week 2/2019 - Spring Integration


This is my first meet with Spring Integration. I started with version 4.3 so I will not mention of older version which I don't know.
For whom who don't know what is it Spring Integration. In a nutshell, this is a framework to create flows between betweens input adapters and endpoints.

In a short story I describe what I'd like to reach.

I've got some configuration in XML version which is included in to class version of configuration. I have as well test context (class version) which override some beens - adapters from/to external world). I'd like to translate this XML to have:
  • consistent configuration in production and test code,
  • to be able override production configuration of some adapters (ex. file adapters)
  • to use inline transformations (not included in example).
Below I added a sample of configuration for class version and commented out a equivalent XML code.



  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
@Configuration
//@ImportResource("/spring-int.xml")
@EnableIntegration
public class IntConfig {


//    <int:channel id="inChannel"/>

//    <int:transformer input-channel="inChannel"
//                     output-channel="srcEmailChannel"
//                     ref="inTransformer"/>
//    <bean id="inTransformer" class="PublisherTransformer"/>
//

    @Bean
    @Transformer(inputChannel = "inChannel", outputChannel = "srcEmailChannel")
    PublisherTransformer inTransformer() {
        return new PublisherTransformer();
    }

//    <int:channel id="srcEmailChannel">
//        <int:interceptors>
//            <int:wire-tap channel="backup"></int:wire-tap>
//        </int:interceptors>
//    </int:channel>


    @Bean
    public MessageChannel backup() {
        return new DirectChannel();
    }

    @Bean
    public MessageChannel srcEmailChannel() {
        final DirectChannel directChannel = new DirectChannel();
        directChannel.addInterceptor(new WireTap(backup()));
        return directChannel;
    }


//    <int:router input-channel="backup" expression="headers.country">
//        <int:mapping value="PL" channel="plChannel"/>
//        <int:mapping value="EN" channel="enChannel"/>
//    </int:router>

    @Bean
    @Router(inputChannel = "backup")
    public AbstractMessageRouter backupRouter() {
        HeaderValueRouter router = new HeaderValueRouter("country");
        router.setChannelMapping("PL", "plChannel");
        router.setChannelMapping("EN", "enChannel");
        return router;
    }


//    <int:channel id="plChannel"/>
//    <int-file:outbound-channel-adapter id="plChannelOutFile"
//                                       directory="/tmp/plChannel/"
//                                       channel="plChannel"
//                                       mode="APPEND"
//                                       charset="UTF-8"/>

    @Bean
    @ServiceActivator(inputChannel = "plChannel")
    MessageHandler plChannelOutFile() {
        final FileWritingMessageHandler fileWritingMessageHandler = 
                new FileWritingMessageHandler(new File("/tmp/plChannel/"));
        fileWritingMessageHandler.setCharset(UTF_8);
        fileWritingMessageHandler.setFileExistsMode(APPEND);
        fileWritingMessageHandler.setExpectReply(false);
        return fileWritingMessageHandler;
    }

//    <int:channel id="enChannel"/>
//    <int-file:outbound-channel-adapter  id="enChannelOutFile"
//                                        directory="/tmp/enChannel/"
//                                        channel="enChannel"
//                                        mode="APPEND"
//                                        filename-generator="fileGenerator"
//                                        charset="UTF-8"/>
//    <bean id="fileGenerator" class="FileNameGen"/>

    @Bean
    @ServiceActivator(inputChannel = "enChannel")
    MessageHandler enChannelOutFile() {
        final FileWritingMessageHandler fileWritingMessageHandler = 
                new FileWritingMessageHandler(new File("/tmp/enChannel/"));
        fileWritingMessageHandler.setCharset(UTF_8);
        fileWritingMessageHandler.setFileExistsMode(APPEND);
        fileWritingMessageHandler.setExpectReply(false); // I don't need return
        fileWritingMessageHandler.setFileNameGenerator(new FileNameGen());
        return fileWritingMessageHandler;
    }


//    <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"/>
      // this not complete mail server configutation
      @Bean
      MailSender mailSender() {       
          return new JavaMailSenderImpl();
      }
//
//
//    <int-mail:outbound-channel-adapter channel="srcEmailChannel" mail-sender="mailSender"/>
    @Bean
    @ServiceActivator(inputChannel = "srcEmailChannel")
    MessageHandler messageHandler(MailSender mailSender) {
       return new MailSendingMessageHandler(mailSender);
    }
    @Bean
    PublisherService publisherService() {
        return new PublisherService();
    }
}

As you can see all of used channels must be defined manualy in XML version. In class version I defined only those which I'd like add some additional behaviour.

Is it true that in Java we can't change Spring Integration configuration with simple editor an restart applicaton - we need compile this configuration but we have plenty of possibilities to do.

Starting from version 5.0 there is additional implementation supporting mixed configuration in test, ex.
  • @SpringIntegrationTest
  • MockIntegrationContext
However this works in other way than overrding bean definition as I did it. This stops current MessageHandler lifecycle and replace condext with mocked bean.

In version 5 it was added as well DSL to make configuration more readable. More information in [1]