Pages

Spring Boot Oauth2 Security

This post is an enhancement for my previous post which talks about how to secure your REST API using Spring security oauth2.
In case if you missed it, here is the place to grab.
http://blog.rajithdelantha.com/2015/09/secure-your-rest-api-with-spring.html

Spring boot is one of the new inventions from Spring framework that makes developers' lives easier when building large scale applications. Here is a good place to grab the concepts.

If you check my previous post related to oauth2 security then you know there is a bit of configuration that needs to be done in Spring side. But on the other hand Spring boot will do all the hard work and we just need to tell them what to do by a simple annotation.

So this post is about how to configure Spring boot project with Spring security and Oauth2. Actually we can't really say configure because all most all configurations are done by Spring boot itself.

Source code : https://github.com/rajithd/spring-boot-oauth2

Step 1
For this project I'm using H2 in memory database. Because of that you don't need to create any database and tables as the creation happens at run time. But if you want this project to use MySQL as the data source then first create the database and then create the tables.

 CREATE TABLE user (  
  username VARCHAR(50) NOT NULL PRIMARY KEY,  
  email VARCHAR(50),  
  password VARCHAR(500),  
  activated BOOLEAN DEFAULT FALSE,  
  activationkey VARCHAR(50) DEFAULT NULL,  
  resetpasswordkey VARCHAR(50) DEFAULT NULL  
 );  
 CREATE TABLE authority (  
  name VARCHAR(50) NOT NULL PRIMARY KEY  
 );  
 CREATE TABLE user_authority (  
   username VARCHAR(50) NOT NULL,  
   authority VARCHAR(50) NOT NULL,  
   FOREIGN KEY (username) REFERENCES user (username),  
   FOREIGN KEY (authority) REFERENCES authority (name),  
   UNIQUE INDEX user_authority_idx_1 (username, authority)  
 );  
 CREATE TABLE oauth_access_token (  
  token_id VARCHAR(256) DEFAULT NULL,  
  token BLOB,  
  authentication_id VARCHAR(256) DEFAULT NULL,  
  user_name VARCHAR(256) DEFAULT NULL,  
  client_id VARCHAR(256) DEFAULT NULL,  
  authentication BLOB,  
  refresh_token VARCHAR(256) DEFAULT NULL  
 );  
 CREATE TABLE oauth_refresh_token (  
  token_id VARCHAR(256) DEFAULT NULL,  
  token BLOB,  
  authentication BLOB  
 );  


  • user table - system users
  • authority -  roles
  • user_authority - many to many table for user and role
  • oauth_access_token - to hold access_token
  • oauth_refresh_token - to hold refresh_token
Add some seed data.

 INSERT INTO user (username,email, password, activated) VALUES ('admin', 'admin@mail.me', 'b8f57d6d6ec0a60dfe2e20182d4615b12e321cad9e2979e0b9f81e0d6eda78ad9b6dcfe53e4e22d1', true);  
 INSERT INTO user (username,email, password, activated) VALUES ('user', 'user@mail.me', 'd6dfa9ff45e03b161e7f680f35d90d5ef51d243c2a8285aa7e11247bc2c92acde0c2bb626b1fac74', true);  
 INSERT INTO user (username,email, password, activated) VALUES ('rajith', 'rajith@abc.com', 'd6dfa9ff45e03b161e7f680f35d90d5ef51d243c2a8285aa7e11247bc2c92acde0c2bb626b1fac74', true);  
 INSERT INTO authority (name) VALUES ('ROLE_USER');  
 INSERT INTO authority (name) VALUES ('ROLE_ADMIN');  
 INSERT INTO user_authority (username,authority) VALUES ('rajith', 'ROLE_USER');  
 INSERT INTO user_authority (username,authority) VALUES ('user', 'ROLE_USER');  
 INSERT INTO user_authority (username,authority) VALUES ('admin', 'ROLE_USER');  
 INSERT INTO user_authority (username,authority) VALUES ('admin', 'ROLE_ADMIN');  

Step 2
Configure WebSecurityAdapter

 @Configuration  
 @EnableWebSecurity  
 public class SecurityConfiguration extends WebSecurityConfigurerAdapter {  
   @Autowired  
   private UserDetailsService userDetailsService;  
   @Bean  
   public PasswordEncoder passwordEncoder() {  
     return new StandardPasswordEncoder();  
   }  
   @Autowired  
   public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {  
     auth  
         .userDetailsService(userDetailsService)  
         .passwordEncoder(passwordEncoder());  
   }  
   @Override  
   public void configure(WebSecurity web) throws Exception {  
     web  
         .ignoring()  
         .antMatchers("/h2console/**")  
         .antMatchers("/api/register")  
         .antMatchers("/api/activate")  
         .antMatchers("/api/lostpassword")  
         .antMatchers("/api/resetpassword");  
   }  
   @Override  
   @Bean  
   public AuthenticationManager authenticationManagerBean() throws Exception {  
     return super.authenticationManagerBean();  
   }  
   @EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)  
   private static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {  
     @Override  
     protected MethodSecurityExpressionHandler createExpressionHandler() {  
       return new OAuth2MethodSecurityExpressionHandler();  
     }  
   }  
 }  


Step 3
Configuration for Oauth2

 @Configuration  
 public class OAuth2Configuration {  
   @Configuration  
   @EnableResourceServer  
   protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {  
     @Autowired  
     private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;  
     @Autowired  
     private CustomLogoutSuccessHandler customLogoutSuccessHandler;  
     @Override  
     public void configure(HttpSecurity http) throws Exception {  
       http  
           .exceptionHandling()  
           .authenticationEntryPoint(customAuthenticationEntryPoint)  
           .and()  
           .logout()  
           .logoutUrl("/oauth/logout")  
           .logoutSuccessHandler(customLogoutSuccessHandler)  
           .and()  
           .csrf()  
           .requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))  
           .disable()  
           .headers()  
           .frameOptions().disable()  
           .sessionManagement()  
           .sessionCreationPolicy(SessionCreationPolicy.STATELESS)  
           .and()  
           .authorizeRequests()  
           .antMatchers("/hello/**").permitAll()  
           .antMatchers("/secure/**").authenticated();  
     }  
   }  
   @Configuration  
   @EnableAuthorizationServer  
   protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements EnvironmentAware {  
     private static final String ENV_OAUTH = "authentication.oauth.";  
     private static final String PROP_CLIENTID = "clientid";  
     private static final String PROP_SECRET = "secret";  
     private static final String PROP_TOKEN_VALIDITY_SECONDS = "tokenValidityInSeconds";  
     private RelaxedPropertyResolver propertyResolver;  
     @Autowired  
     private DataSource dataSource;  
     @Bean  
     public TokenStore tokenStore() {  
       return new JdbcTokenStore(dataSource);  
     }  
     @Autowired  
     @Qualifier("authenticationManagerBean")  
     private AuthenticationManager authenticationManager;  
     @Override  
     public void configure(AuthorizationServerEndpointsConfigurer endpoints)  
         throws Exception {  
       endpoints  
           .tokenStore(tokenStore())  
           .authenticationManager(authenticationManager);  
     }  
     @Override  
     public void configure(ClientDetailsServiceConfigurer clients) throws Exception {  
       clients  
           .inMemory()  
           .withClient(propertyResolver.getProperty(PROP_CLIENTID))  
           .scopes("read", "write")  
           .authorities(Authorities.ROLE_ADMIN.name(), Authorities.ROLE_USER.name())  
           .authorizedGrantTypes("password", "refresh_token")  
           .secret(propertyResolver.getProperty(PROP_SECRET))  
           .accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class, 1800));  
     }  
     @Override  
     public void setEnvironment(Environment environment) {  
       this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH);  
     }  
   }  
 }  

This is it. Try running Spring boot application by
mvn spring-boot:run

Then check your oauth2 security by executing following curls.
https://github.com/rajithd/spring-boot-oauth2 

Debugging Application Performance With Perf4j

Performance is one of the main aspect that we all need to consider when implementing systems. Today's world of Service Oriented Architectures and distributed applications, detecting performance bottleneck is really a difficult task.

If someone asked "Why is my web app slow ?"  this requires investigation multiple components and execution paths, and it requires detailed performance data from all of the application components.

In order to get the performance data one of the most common practice is to get the execution time for given path. System.currentTime() is one approach to do this. But we need to have some custom mechanism to aggregate these results to analyze further.

Perf4j is an open source performance logging framework that specially design to solve the above scenario. Its a,

  • Simple stop watch mechanism to time statements.
  • Command line tool to generate aggregate statistics 
  • Can integrate with log4j
  • Provides @Profile annotation to use it with Aspects.
You can find more details explanation in here and here.

But here I will drive through with some detailed example to understand,
  • How to integrate perf4j with log4j
  • How to configure log4j.xml to use separate log file for performance results
  • Commands to aggregate performance log data
As usual you can find the sample code here.

First we will create a sample maven project and then create a class called PerformanceMonitor to hold the stop watch.
Add per4j and log4j dependencies to pom.xml
 <dependencies>  
     <dependency>  
       <groupId>org.perf4j</groupId>  
       <artifactId>perf4j</artifactId>  
       <version>${perf4j.version}</version>  
     </dependency>  
     <!-- Logging -->  
     <dependency>  
       <groupId>org.slf4j</groupId>  
       <artifactId>slf4j-api</artifactId>  
       <version>${slf4j.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.slf4j</groupId>  
       <artifactId>slf4j-log4j12</artifactId>  
       <version>${slf4j.version}</version>  
     </dependency>  
   </dependencies>  


PerformanceMonitor.java
 public class PerformanceMonitor {  
   private static final ThreadLocal<StopWatch> WATCH = new ThreadLocal<StopWatch>() {  
     @Override  
     protected StopWatch initialValue() {  
       return new Log4JStopWatch(Logger.getLogger("instrument.org.perf4j.TimingLogger"));  
     }  
   };  
   public static void start(String tag) {  
     WATCH.get().start(tag);  
   }  
   public static void stop(String tag) {  
     WATCH.get().stop(tag);  
   }  
 }  

As you can clearly see I used this as a ThreadLocal instance because I'm expecting to run this application in multi-threaded environment.

Then configure log4j.xml
 <?xml version="1.0" encoding="UTF-8" ?>  
 <!DOCTYPE log4j:configuration SYSTEM  
     " http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">  
 <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">  
   <appender name="console" class="org.apache.log4j.ConsoleAppender">  
     <layout class="org.apache.log4j.PatternLayout">  
       <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %t %c{1}:%L - %m%n"/>  
     </layout>  
   </appender>  
   <appender name="trace" class="org.apache.log4j.DailyRollingFileAppender">  
     <param name="File" value="trace.log"/>  
     <param name="Threshold" value="debug"/>  
     <param name="DatePattern" value="'.'yyyy-MM-dd"/>  
     <layout class="org.apache.log4j.PatternLayout">  
       <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %t %c{1}:%L - %m%n"/>  
     </layout>  
   </appender>  
   <appender name="error" class="org.apache.log4j.DailyRollingFileAppender">  
     <param name="File" value="logs/vetstoria-oltp-error.log"/>  
     <param name="Threshold" value="error"/>  
     <param name="DatePattern" value="'.'yyyy-MM-dd"/>  
     <layout class="org.apache.log4j.PatternLayout">  
       <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %t %c{1}:%L - %m%n"/>  
     </layout>  
   </appender>  
   <appender name="statistics" class="org.apache.log4j.FileAppender">  
     <param name="File" value="perf.log"/>  
     <param name="Append" value="false"/>  
     <layout class="org.apache.log4j.PatternLayout">  
       <param name="ConversionPattern" value="%m%n"/>  
     </layout>  
   </appender>  
   <logger name="org.perf4j.TimingLogger">  
     <level value="info"/>  
     <appender-ref ref="statistics"/>  
   </logger>  
   <logger name="com.rd">  
     <level value="debug"/>  
     <appender-ref ref="console"/>  
     <appender-ref ref="trace"/>  
     <appender-ref ref="error"/>  
   </logger>  
   <logger name="instrument">  
     <level value="info"/>  
     <appender-ref ref="console"/>  
     <appender-ref ref="statistics"/>  
   </logger>  
 </log4j:configuration>  

Check the "org.per4j.TimingLogger" and "instrument" loggers carefully.  You can see the relevant appender called "statistics"  that will redirect output to a file called perf.log

App.java is pretty straightforward.

 public class App {  
   private static final Logger LOGGER = Logger.getLogger(App.class);  
   public static void main(String[] args) throws InterruptedException {  
     LOGGER.info("Starting .....");  
     PerformanceMonitor.start("METHOD1");  
     method1();  
     PerformanceMonitor.stop("METHOD1");  
     PerformanceMonitor.start("METHOD2");  
     method2();  
     PerformanceMonitor.stop("METHOD2");  
     LOGGER.info("Completed. Please check the perf.log file for time logging");  
   }  
   private static void method1() throws InterruptedException {  
     Thread.sleep(5000);  
   }  
   private static void method2() throws InterruptedException {  
     Thread.sleep(2000);  
   }  
 }  


Then run the App.java main method and this will create two log files in same hierarchy. All the performance related logs populate into a file called perf.log.

When you inspect the perf.log file you can see the following output.
 start[1444722158471] time[5000] tag[METHOD1]  
 start[1444722163472] time[2000] tag[METHOD2]  

To get aggregate results,

  1. Download per4j jar. (http://mvnrepository.com/artifact/org.perf4j/perf4j/0.9.16)
  2. Run following command
 java -jar <perf4j.location>/perf4j-0.9.16.jar --timeslice 3000000000 --format csv perf.log -o output.csv  

Open up the output.csv and you can see the aggregated performance results.