Spring Tutorial
Table of Contents
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.
- Compile the project by
- 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 runmvn package
as building the jar file.
- Jar file. Run
- Deploy
- Package the files
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; }