1 Project file structure

Generally, we use maven to manage the directory layout. Hence, you will get a standard directory layout like below:

project
    src
        main
            java
            [scala]
            resources
                environment.properties
                environment.test.properties
                environment.prod.properties
                config
                    application.yml
                    application-dev.yml
                    application-[profile].yml
                [other]
        test
            java
            [scala]
    target

2 Management with Maven

The files environment.[profile].properties are used to provide different profiles to maven.

Since maven is used to manage the package dependencies, different maven profiles enables you to use different dependencies for development and production, e.g. run different tasks. I am not farmiliar with maven, and cannot tell how and when it can be used.

You have to add spring-boot-maven-plugin as one of the plugins under build node in pom.xml.

  • Development
    • Compile the project by maven clean insatll
    • More than Build

      Maven can help running test, web application and produceing reports by plugins. TODO

    • Compile and run: mvn spring-boot:run to run the application with default profile; mvn -Pprod spring-boot:run to run the application with prod profile.
  • Production
    • Package the files
      • Jar file. Run mvn clean package to build the jar file and run it through command line.
      • War file. Add <packaging>war</packaging> in pom.xml file, under the root node. Then run mvn package as building the jar file.
    • Deploy

From command line, you can run the jar file with specification of profile.

java -jar -Dspring.profiles.active=production YOUR_JAR.jar

3 Application Configuration

The files application-[profile].yml are used to provide different configurations for spring.

Spring configuration files let you to inject different configurations for development and production. For example, you can use different database for development and production (different host, port, username and password, etc.).

3.1 Inject configurations

You can get access to these configurations through many ways

  • Inject the value of configuration to attribute of this class by @Value("${NAME:DEFAULT_VALUE}").
  • You can also inject Environment object.
    // This line is not about get the property, but about define
    // property and let it accessible by somewhere else.
    @Configuration
    public class AppConfig {
        @Inject Environment env;
    
        @Bean
        public MyBean myBean() {
            MyBean myBean = new MyBean();
            myBean.setName(env.getProperty("bean.name"));
            return myBean;
        }
    }
    
  • You can also implement the EnvironmentAware interface to get an environment object to get the property.
    public class MailConfiguration implements EnvironmentAware {
        public void setEnvironment(Environment environment) {
            this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_SPRING_MAIL);
        }
    }
    

One difference between Environment and @Value is that we can access the information related to profiles. The environment can determine which profiles are currently active, and which profile should be active by default.

env.acceptsProfiles(String... profiles)
env.getActiveProfiles() // return an array of strings

Environment interface extends PropertyResolver, which is used to acess the properties. You can get property directly from the environment object. The example above provide a way that get properties by key name following a relaxed rule.

3.2 Load configuration files.

Different property sources are loaded in predefined order. You can set the default additional property file by

SimpleCommandLinePropertySource source = new SimpleCommandLinePropertySource(args);
if (!source.containsProperty("spring.profiles.active")){
    app.setAdditionalProfiles([profile_name])
}

3.3 Define configuration source class

A class is recognized as a source of configuration if it has the @Configuration class. @Bean defined in this class can be accessed from AnnotationConfigApplicationContext. It is a replacement of XML configuration files that defines beans. Get the configuration from configuration class in some where else.

public class ConfigurationTest {
    @Test
    public void testHelloworld () {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        Assert.assertEquals("hello", ctx.getBean("message")); // message is the name of the Bean method in configuration class
        // it can also be used to get any kind of object
    }
}

AppConfig is the class that used as configuration source.

3.4 Automatically assign properties.

In configuration source, add @EnableConfigurationProperties(A.class). In target class A, add @ConfigurationProperties(prefix = "PREFIX"). Then the properties under PREFIX will be automatically assigned to the attributes of class A. DETAILS OF THE CONFIGURATION SOURCE???

@EnableAutoConfiguration auto configure the application. I don’t know the details of this annotation, but it auto wires the configurations into the embedded tomcat. Besides, it also configures the database (host and port) automatically when database template is injected in the project.

4 Authentication & Securing

It is mainly implemented by HttpSecurity, which can be injected and configured in method of class that extends WebSecurityConfigurerAdapter.

public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Inject
    private UserDetailsService userDetailsService;
    // class access to the user database

    @Value("${drill.security.rememberme.key}")
    private String remerberMeKey;
    // key for cookies

    @Inject
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .userDetailsService(userDetailsService) // the point that connect the authentication module and user database
            .passwordEncoder(passwordEncoder());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        // Spring security ignore following URL
        web.ignoring()
            .antMatchers("/bower_components/**")
            .antMatchers("/fonts/**")
            .antMatchers("/images/**")
            ...;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/app/**").authenticated()
                .antMatchers("/app/rest/register").permitAll()
                .antMatchers("/app/rest/logs/**").hasAuthority(Authority.ADMIN)
                // set authorities
            .and()
                .formLogin()
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/app/authentication")
                .successHandler(ajaxAuthenticationSuccessHandler)
                .failureHandler(ajaxAuthenticationFailureHandler)
                .permitAll()
                // set login service url
            .and()
                .logout()
                .logoutUrl("/app/logout")
                .logoutSuccessHandler(ajaxLogoutSuccessHandler)
                .deleteCookies("JSESSIONID")
                .permitAll()
            .and()
                .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
            .and()
                .rememberMe()
                .rememberMeServices(rememberMeServices)
                // it is not persistent token
                .key(remerberMeKey)
            .and()
                .csrf().disable()  // not clear
                .headers().cacheControl(); // not clear
    }
}

The foundation of user securing is about session and cookies. Once the user logged in, the server will set the cookies in browser. Then browser will send the cookies via HTTP protocol everytime it send new request to the server. On the server side, session is also kept with a unique random id to identify the client. Look another blog Init the project to see how to replace the in memory user store by a repository that connects to database. On the server side, programmer can get the user information by:

public static String getCurrentLogin() {
    SecurityContext securityContext = SecurityContextHolder.getContext();
    Authentication authentication = securityContext.getAuthentication();
    UserDetails springSecurityUser = null;
    String userName = null;

    if(authentication != null) {
        if (authentication.getPrincipal() instanceof UserDetails) {
            springSecurityUser = (UserDetails) authentication.getPrincipal();
            userName = springSecurityUser.getUsername();
        } else if (authentication.getPrincipal() instanceof String) {
            userName = (String) authentication.getPrincipal();
        }
    }

    return userName;
}