annotation based Logger injection in Spring with Kotlin

what’s it all about

In this post I show how to implement an annotation-based Logger injection in Spring when writing the application with Kotlin.

The used technique is quite far from new, basically what I do is implement a BeanPostProcessor which scans the properties of the beans for fields annotated with a custom annotation and sets these fields to a Logger instance. Examples how to implement this in Java can be found on the web, here I show the Kotlin version.

In this example I have an innovative REST controller wich has the ability to say hello. This controller has an injected service to do the processing of the incoming name. The reason for implementing this in two classes is that I want to show 2 different ways to inject a Logger.

The whole project for this example is available on GitHub.

The setup

I use Spring-Boot to setup the project. There is nothing special in relation to boot here, but it gets me fast on the track. I created my project from within IntelliJ IDEA as a Spring-Boot project, but you can use https://start.spring.io/ as well. Language is Kotlin, and the only dependency needed is web.

The logging configuration

I added a simple logback.xml to my project:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/base.xml"/>
    <logger name="com.sothawo" level="DEBUG"/>
    <root level="WARN"/>
</configuration>

this sets the global level to WARN, and everything from my code to DEBUG.

The classes that get a Logger injected

The first class I show is HelloService, this is a Spring component which has one method that takes a string, and returns it concatenated to “hello “, and of course this is logged. In this class the Logger is injected in a normal property of the class by using the @Slf4jLogger annotation which I will show later:

package com.sothawo

import org.slf4j.Logger
import org.springframework.stereotype.Service

/**
 * @author P.J. Meisch (pj.meisch@sothawo.com)
 */
@Service
class HelloService {

    @Slf4jLogger
    lateinit var log: Logger

    fun sayHello(name: String): String {
        log.info("saying hello to: $name")
        return "hello $name"
    }
}

Using a normal property would create multiple instances of the same Logger-name if multiple HelloService instances were created (which they aren’t, but if you would change the scope this might happen). Therefore a better solution is to put the Logger in the companion object . This can be seen in the HelloController class:

package com.sothawo

import org.slf4j.Logger
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

/**
 * @author P.J. Meisch (pj.meisch@sothawo.com)
 */
@RestController
@RequestMapping("/hello")
class HelloController(val helloService: HelloService) {

    @GetMapping("/{name}")
    fun sayHello(@PathVariable name: String): ResponseEntity {
        log.info("sayHello called with arg $name")
        return ResponseEntity.ok(helloService.sayHello(name))
    }

    companion object {
        @Slf4jLogger
        lateinit var log: Logger
    }
}

The annotation and injection code

package com.sothawo

import org.slf4j.LoggerFactory
import org.springframework.beans.factory.config.BeanPostProcessor
import org.springframework.stereotype.Component
import kotlin.reflect.KMutableProperty
import kotlin.reflect.full.companionObjectInstance
import kotlin.reflect.full.declaredMemberProperties

/**
 * @author P.J. Meisch (pj.meisch@sothawo.com)
 */

@Target(AnnotationTarget.PROPERTY)
annotation class Slf4jLogger

@Component
class LoggingInjector : BeanPostProcessor {

    override fun postProcessBeforeInitialization(bean: Any?, beanName: String?): Any? {
        bean?.let {
            val loggerName = it::class.java.canonicalName
            processObject(it, loggerName)
            it::class.companionObjectInstance?.let {
                processObject(it, loggerName)
            }
        }

        return bean
    }

    override fun postProcessAfterInitialization(bean: Any?, beanName: String?) = bean

    private fun processObject(target: Any, loggerName: String) {
        target::class.declaredMemberProperties.forEach {
            property ->
            property.annotations
                    .filter { it is Slf4jLogger }
                    .forEach {
                        if (property is KMutableProperty<*>) {
                            property.setter.call(target, LoggerFactory.getLogger(loggerName))
                        }
                    }
        }
    }
}

First I create the annotation with the AnnotationTarget.PROPERTY  (lines 14, 15). Then I define a Spring component that implements the BeanPostProcessor interface. The postProcessAfterInitialization method does nothing, it just returns the bean that is passed in, the magic happens in postProcessBeforeInitialization. Here, if the bean that is passed in is not null, it is passed into my processObject method, and then I check if there is a companion object and analyze this in the same way. The Logger name is built from the bean’s class as I do not want to have the companion object’s name in there.

In the processObject method I iterate over the properties of the target object that was passed in. For each property I check if it has the @SLF4JLogger annotation and if so I set it’s value to a newly created Logger instance with the requested name.

The output

When running the program and issuing a call like

curl localhost:8080/hello/world

the log output shows:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.4.RELEASE)

2017-07-12 15:58:32.567  INFO 2797 --- [           main] KotlinSpringLoggerInjectionApplicationKt : Starting KotlinSpringLoggerInjectionApplicationKt on pjm.local with PID 2797 (/Users/peter/Entwicklung/sothawo/kotlin-spring-logger-injection/target/classes started by peter in /Users/peter/Entwicklung/sothawo/kotlin-spring-logger-injection)
2017-07-12 15:58:32.571 DEBUG 2797 --- [           main] KotlinSpringLoggerInjectionApplicationKt : Running with Spring Boot v1.5.4.RELEASE, Spring v4.3.9.RELEASE
2017-07-12 15:58:32.571  INFO 2797 --- [           main] KotlinSpringLoggerInjectionApplicationKt : No active profile set, falling back to default profiles: default
2017-07-12 15:58:35.423  INFO 2797 --- [           main] KotlinSpringLoggerInjectionApplicationKt : Started KotlinSpringLoggerInjectionApplicationKt in 3.355 seconds (JVM running for 3.776)
2017-07-12 15:58:51.745  INFO 2797 --- [nio-8080-exec-1] com.sothawo.HelloController              : sayHello called with arg world
2017-07-12 15:58:51.745  INFO 2797 --- [nio-8080-exec-1] com.sothawo.HelloService                 : saying hello to: world

Conclusion

As shown here it is pretty simple to process annotation in Kotlin and use this for custom injection in Spring. Especially statments like annotations.filter { it is Slf4jLogger } are a pretty cool feature.

Run a Spring-Boot application on OpenShift behind HTTPS only

When a Spring-Boot application is deployed on OpenShift, it can be reached both with a HTTP URL and a HTTPS URL. This is because OpenShift runs a proxy in front of the application which in case of HTTP just routes the request to the application. If a request comes in via HTTPS, the proxy does all the encryption handling with the client and then passes the decrypted request on to the application – on the HTTP channel – and encrypts the response before sending it to the client.

The advantage for an application developer is that you do not need to bother about the details of encryption, you just write your application and leave the rest to OpenShift.

This post shows how to setup and modify your application so that it only can be reached by HTTPS and so enforces the use of a secure conversation channel. To know how to set up a Spring-Boot application on OpenShift you might read this post.

Add the security to your project and set it to ssl only

When you setup your project with the Spring Initializr include the core/security. If you have an existing project, add the following dependency to your pom.xml:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>

This enables Spring security and secures your application with the user named user and a password that is displayed on the console during startup.  To force the use of HTTPS you normally only need to add the following entry to the application.properties file:

security.require-ssl=true

Now you would have an application that will automatically redirect to HTTPS (the default port in Spring Boot for HTTPS is 8443 when running on unsecure port 8080) when called on port 8080. Besides not having a certificate and the configuration to run on HTTPS, we don’t need the Basic Authentication for our purpose of running HTTPS only. So we can disable it by adding the following entry to the application.properties file:

security.basic.enabled=false

When restarting the application the need for Basic Authentication is gone, but this also disables the require-ssl setting, so we can access our application as before on the normal HTTP port.

The solution to this problem is to provide a custom configuration class:

/**
 * Copyright (c) 2015 sothawo
 *
 * http://www.sothawo.com
 */
package com.sothawo.sayservice;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;

/**
 * Security configuration.
 *
 * @author P.J. Meisch (pj.meisch@sothawo.com).
 */
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requiresChannel().anyRequest().requiresSecure();
        http.csrf().disable();
    }
}

After adding this class to our project (as I in this case don’t need CSRF for a pure REST service behind HTTPS, I disable it here), all requests to the HTTP port are redirected to HTTPS and the Basic Authentication is still disabled (you can remove the server-require-ssl entry from the config file). So far so good.

Fix eternal redirection on OpenShift

After deploying the application in this stage to OpenShift you will notice that both requests, HTTP on port 80 and HTTPS on port 443 result in an eternal redirection to the HTTPS URL. This happens because when the application is accessed by HTTPS, the OpenShift proxy does the HTTPS handling and then contacts the application on the normal internal HTTP port. The application checks the channel and sends a redirect request to the secure channel to the client which in turn request the application from the HTTPS proxy, which will strip the HTTPS part and so on.

To fix this you need to make your application honour two special HTTP headers. Add the following lines to the application.conf file:

server.tomcat.remote_ip_header=x-forwarded-for
server.tomcat.protocol_header=x-forwarded-proto

The x-forwarded- headers are set by the proxy and by putting these settings in your configuration, the embedded tomcat checks these headers when deciding wether a redirect is needed and so even when the application is called from the proxy on HTTP, a redirect will only be requested when the proxy itself was not accessed by a secure channel.

That’s all that’s needed to run yur application HTTPS only.

Spring-Boot, Spring profiles and configuration files

Note to self: When using Spring-Boot, use application.conf as a base configuration for the needed values. Configuration values for the specific profile go into the application-<profile>.config file.

Profiles are activated by using either the -Dspring.profiles.active=<profile> VM flag or --spring.profiles.active=<profile> commandline arg.

Deploying a Spring-Boot application running with Java8 on OpenShift2

This post describes how to create and deploy a Spring-Boot application to RedHat OpenShift (version 2) when the application is using Java 8.

Edit 2015-10-04: In this newer post I show how to not install a custom JDK. So you should first read this post and then the linked one for additional information.

Normally deploying a Spring-Boot application on OpenShift is not too much pain and is explained in the Spring-Boot documentation. But some extra work is needed when the application is built and run with Java 8, as at the time of writing, the DIY cartridge of OpenShift only supports Java 7. And, to make things worse, the mvn command which is available in the DIY cartridge is rewritten by RedHat, so it will pick up Java 7 no matter what you set your JAVA_HOME to.

This post will show how to overcome these deficiencies by walking through the necessary steps to create a Spring-Boot based REST service which is deployed on OpenShift. To follow along you need

  • Java 8 installed
  • an OpenShift account
  • setup the rhc command line tool as described on OpenShift documentation
  • know how to create and set up an Spring Boot project (I use a maven project)

My sample is created on Mac OSX by using the terminal and IntelliJ. I will create a REST service named SayService which will just return it’s string input prepended by “you said: “. Not very interesting, but enough for this example.

Create the OpenShift application

As a first step I create the application on OpenShift. To do that, I change into the local directory where I want the app to be created and issue the following rhc command, assuming you are logged in to OpenShift with rhc:

rhc app-create sayservice diy

This creates the OpenShift application and clones it’s Git repository to your local sayservice directory. The structure is shown below:

sayservice
├── .git
├── .openshift
│   ├── README.md
│   ├── action_hooks
│   │   ├── README.md
│   │   ├── start
│   │   └── stop
│   ├── cron
│   │   ├── README.cron
│   │   ├── daily
│   │   │   └── .gitignore
│   │   ├── hourly
│   │   │   └── .gitignore
│   │   ├── minutely
│   │   │   └── .gitignore
│   │   ├── monthly
│   │   │   └── .gitignore
│   │   └── weekly
│   │       ├── README
│   │       ├── chrono.dat
│   │       ├── chronograph
│   │       ├── jobs.allow
│   │       └── jobs.deny
│   └── markers
│       └── README.md
├── README.md
├── diy
│   ├── index.html
│   └── testrubyserver.rb
└── misc
    └── .gitkeep

The diy subdirectory contains the sample, we ignore that. What we need to adjust later are scripts in the .openshift/action_hooks directory. And of course we need to add our source code for the service.

Create the Spring-Boot REST service

With the help of the Spring Boot Initializr (which I use from within IntelliJ, but the jar created on the Website is quite the same) I create a project that just has the Web/Web component added and where the Java version is set to 1.8. The important thing here is that the project is created in the sayservice directory so that the project files are added to the existing directory. After adding my standard .gitignore file, the directory contains the following data (not showing the contents of the .openshift directory again and skipping IntelliJ files):

sayservice
├── .git
├── .gitignore
├── .openshift
├── README.md
├── diy
├── misc
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── sothawo
    │   │           └── sayservice
    │   │               └── SayserviceApplication.java
    │   └── resources
    │       ├── application.properties
    │       ├── static
    │       └── templates
    └── test
        └── java
            └── com
                └── sothawo
                    └── sayservice
                        └── SayserviceApplicationTests.java

The following listing shows the pom.xml, notice the explicit setting of the java version to 1.8:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.sothawo</groupId>
  <artifactId>sayservice</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>sayservice</name>
  <description>Demo project for Spring Boot REST service on OpenShift</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.5.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
  
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

Add the Service implementation

At the moment we have an application that has not yet a service defined, so we add the following Sayservice class:

/**
 * Copyright (c) 2015 sothawo
 *
 * http://www.sothawo.com
 */
package com.sothawo.sayservice;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * Sample Service.
 *
 * @author P.J. Meisch (pj.meisch@sothawo.com).
 */
@RestController
@RequestMapping("/")
public class Sayservice {
    @RequestMapping(value = "/say/{in}", method = RequestMethod.GET)
    public String echo(@PathVariable(value = "in") final String in) {
        return "you said: " + in;
    }
}

After creating and running the application with

mvn package && java -jar target/*.jar

you can access and test it:

curl http://localhost:8080/say/hello
you said: hello

Create an OpenShift build script to install Java8 and build the application

The following script named build must be put in the .openshift/action_hooks directory (it must be executable):

#!/bin/bash

# define some variables for JDK 8
JDK_TGZ=jdk-8u60-linux-i586.tar.gz
JDK_URL=http://download.oracle.com/otn-pub/java/jdk/8u60-b27/$JDK_TGZ
JDK_DIR=jdk1.8.0_60
JDK_LINK=jdk1.8

# download JDK1.8 to the data directory if it does not yet exist, extract it and create a symlink
cd ${OPENSHIFT_DATA_DIR}

if [[ ! -d $JDK_DIR ]]
then
  wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" $JDK_URL
  tar -zxf $JDK_TGZ
  rm -fr $JDK_TGZ
  rm $JDK_LINK
  ln -s $JDK_DIR $JDK_LINK
fi

# export environment vriables
export JAVA_HOME="$OPENSHIFT_DATA_DIR/$JDK_LINK"
export PATH=$JAVA_HOME/bin:$PATH

# call our own mvn script with the right settings
cd $OPENSHIFT_REPO_DIR
./.openshift/mvn package -s .openshift/settings.xml -DskipTests=true

The script downloads the Oracle JDK if it is not yet available and extracts it to the OPENSHIFT_DATA_DIR directory.

The next thing to adjust is the mvn script. The one that’s available in the DIY cartridge resets JAVA_HOME, so I put the following mvn script in the .openshift directory:

#!/bin/sh
prog=$(basename $0)
export JAVACMD=$JAVA_HOME/bin/java
export M2_HOME=/usr/share/java/apache-maven-3.0.4
exec $M2_HOME/bin/$prog "$@"

As an alternative you might add a download of maven to the build script.

The last needed file for the build is the settings.xml, which I also put into the .openshift directory (Edit 2015-10-04: fixed variable with {} and added /.m2/repository):

<settings>
 <localRepository>${OPENSHIFT_DATA_DIR}/.m2/repository</localRepository>
</settings>

Set up start and stop scripts

Replace the files start and stop in the .openshift/action_hooks directory with the following ones (they must be executable):

start

#!/bin/bash
# The logic to start up your application should be put in this
# script. The application will work only if it binds to
# $OPENSHIFT_DIY_IP:8080

JDK_LINK=jdk1.8

export JAVA_HOME="$OPENSHIFT_DATA_DIR/$JDK_LINK"
export PATH=$JAVA_HOME/bin:$PATH

cd $OPENSHIFT_REPO_DIR
nohup java -jar target/*.jar --server.port=${OPENSHIFT_DIY_PORT} --server.address=${OPENSHIFT_DIY_IP} &

stop

#!/bin/bash
source $OPENSHIFT_CARTRIDGE_SDK_BASH

# The logic to stop your application should be put in this script.
PID=$(ps -ef | grep java.*\.jar | grep -v grep | awk '{ print $2 }')
if [ -z "$PID" ]
then
    client_result "Application is already stopped"
else
    kill $PID
fi

Deploy and run the application

After committing your files to git a final

git push

will upload all your changes to OpenShift, build and start the application. Check it out by calling

curl http://sayservice-yourdomain.rhcloud.com/say/it-works

and getting the answer

you said: it-works

So much for my first post concerning OpenShift.

Build a Spring-Boot REST service with Basic Authentication for several users

This post describes how to build a REST service with Spring-Boot that uses Basic-Authentication for several users and that uses the username of the authenticated user to do it’s work. Warning: A service using basic authentication should always use HTTPS as transport protocol, either by running behind a web server proxy or by setting up HTTPS by itself. I’ll cover the latter in a later post.

This might be a setup for a service, where for each user, data is stored in a database, so it not only is necessary to authenticate the user to use the service, but it is also necessary in the service to know which user is accessing the service.

The source code for this project can be found at GitHub. The coce relevant for this post is tagged post-20150728.

Setting up the project

I use IntelliJ IDEA (EAP Version 15 with Spring support) for the project and a maven project setup.

To create the initial project, I use Spring Initializr from within IDEA, but you can use https://start.spring.io as well to create the project. I named the project SecuRest, the initial dependencies are Core/Security and Web/Web:

Bildschirmfoto 2015-07-28 um 13.13.57

The created pom.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.sothawo</groupId>
  <artifactId>securest</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>SecuRest</name>
  <description>Demo project for Spring Boot with Basic Authentication and HTTPS</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.5.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
  
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
  

</project>

The Service

The first version of the implemented service just has one method which echoes it’s call arguments back:

/**
 * Copyright (c) 2015 sothawo
 *
 * http://www.sothawo.com
 */
package com.sothawo.securest;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * Sample service.
 *
 * @author P.J. Meisch (pj.meisch@sothawo.com).
 */
@RestController
@RequestMapping("/service")
public class UserService {

    @RequestMapping(value = "/echo/{in}", method = RequestMethod.GET)
    public String echo(@PathVariable(value = "in") final String in) {
        return "You said: " + in;
    }
}

There is nothing yet to retrieve the name of the current user, we’ll do that later.

Running the application with the basic setup

This is all that is needed for the first basic application sceleton. When the application ist started, Spring-Boot sets up the security so that the whole application is secured and can only be accessed by a user user; the password is displayed on application startup in the log in a line like this (the password changes each time the application starts). This is the basic behaviour of Spring-Boot when spring-boot-starter-security is found on application startup.

Using default security password: e482e82e-1115-4fc4-86e4-bdcc432da039

When trying to access the application without passing in a username and password, the following result is returned:

curl localhost:8080/service/echo/hello
{"timestamp":1438088762118,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/service/echo/hello"}

With curl I can pass in the username and password like this:

curl user:e482e82e-1115-4fc4-86e4-bdcc432da039@localhost:8080/service/echo/hello
You said: hello

As an alternative I use IntelliJ’s REST Tool to create an authorization header for the user user and password e482e82e-1115-4fc4-86e4-bdcc432da039 and pass it along with the request:

curl -H "Authorization: Basic dXNlcjplNDgyZTgyZS0xMTE1LTRmYzQtODZlNC1iZGNjNDMyZGEwMzk=" localhost:8080/service/echo/hello
You said: hello

So now access to the service is granted. But, as I said, there is only one user named user allowed, and the password changes every time on application startup. Next, I’ll set up some users with passwords.

 

Setting up the Users

I am using the most basic form to setup different users, and that is by using an in-memory user store. To have that I add the following configuration class:

/**
 * Copyright (c) 2015 sothawo
 *
 * http://www.sothawo.com
 */
package com.sothawo.securest;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * Security configuration.
 *
 * @author P.J. Meisch (pj.meisch@sothawo.com).
 */
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user1").password("secret1").roles("USER")
                .and()
                .withUser("user2").password("secret2").roles("USER");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().fullyAuthenticated();
        http.httpBasic();
        http.csrf().disable();
    }
}

This configuration class configures two users with their passwords and roles. And although when starting the application, a password for the default user user is shown in the log, that user no longer can access the application, only the new configured two users can.

By using @EnableWebMvcSecurity and not @EnableWebSecurity the application has access to the current user in the following way:

/**
 * Sample service.
 *
 * @author P.J. Meisch (pj.meisch@sothawo.com).
 */
@RestController
@RequestMapping("/service")
public class UserService {

    @RequestMapping(value = "/echo/{in}", method = RequestMethod.GET)
    public String echo(@PathVariable(value = "in") final String in, @AuthenticationPrincipal final UserDetails user) {
        return "Hello " + user.getUsername() + ", you said: " + in;
    }
}

After restarting the application the service gives the following result:

curl user1:secret1@localhost:8080/service/echo/hello
Hello user1, you said: hello

So our service is protected by username and passwords and has access to the name of the current user.

Notice: As written at the beginning of this post, it is necessary to use HTTPS transport. This is covered in a later post.

Using Spring-Boot configuration properties in your own classes

When writing a Spring-Boot application it is possible to use your own custom configuration classes which are injected by spring into your application and which  are configured in the application.properties file. There is even support for autocomplete support in the properties file editor in IntelliJ IDEA (I suppose there is something similar in Eclipse or NetBeans, but I haven’t tried that). This post shows how to achieve this.

Basically this is used for auto configuration of Spring-Boot components, but can be used in the Spring-Boot application as well.

The configuration class

Let’s assume we have a class named Support which represents our configuration:

/**
 * Copyright (c) 2015 sothawo
 *
 * http://www.sothawo.com
 */
package com.sothawo.sbconfig;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * @author P.J. Meisch (pj.meisch@sothawo.com).
 */
@Component
@ConfigurationProperties(prefix = "support")
public class Support {

    private Integer productId;

    private Contact contact;


    public Contact getContact() {
        return contact;
    }

    public void setContact(Contact contact) {
        this.contact = contact;
    }

    public Integer getProductId() {
        return productId;
    }

    public void setProductId(Integer productId) {
        this.productId = productId;
    }

    @Override
    public String toString() {
        return "Support{" +
                "productId=" + productId +
                ", contact=" + contact +
                '}';
    }

    public static class Contact {

        private String name;

        private String email;

        public String getEmail() {
            return email;
        }

        public void setEmail(String email) {
            this.email = email;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Contact{" +
                    "name='" + name + '\'' +
                    ", email='" + email + '\'' +
                    '}';
        }
    }
}

This is a plain POJO; it contains an inner class, but that is only for the purpose of showing how Spring Boot configures a field that is itself a class. What marks this as a configuration class is the @ConfigurationProperties annotation, which by it’s prefix argument defines the prefix string to use in the properties file.

Edit 2015-09-21: added missing @Component annotation.

Enable the Spring Boot annotation processor

To have the annotation processed it is necessary to add the following dependency to the project’s pom.xml (notice that it’s optional):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

After compiling the project there is a new file in the target/META-INF directory named spring-configuration-metadata.json. This is needed for autocomplete support, it contains the names, types and class information of the configuration class:

{
  "groups": [
    {
      "name": "support",
      "type": "com.sothawo.sbconfig.Support",
      "sourceType": "com.sothawo.sbconfig.Support"
    },
    {
      "name": "support.contact",
      "type": "com.sothawo.sbconfig.Support$Contact",
      "sourceType": "com.sothawo.sbconfig.Support",
      "sourceMethod": "getContact()"
    }
  ],
  "properties": [
    {
      "name": "support.contact.email",
      "type": "java.lang.String",
      "sourceType": "com.sothawo.sbconfig.Support$Contact"
    },
    {
      "name": "support.contact.name",
      "type": "java.lang.String",
      "sourceType": "com.sothawo.sbconfig.Support$Contact"
    },
    {
      "name": "support.product-id",
      "type": "java.lang.Integer",
      "sourceType": "com.sothawo.sbconfig.Support"
    }
  ]
}

Using the configuration object

The configuration object is used by injecting it in a Spring bean, for example like this:

/**
 * Copyright (c) 2015 sothawo
 *
 * http://www.sothawo.com
 */
package com.sothawo.sbconfig;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 * @author P.J. Meisch (pj.meisch@sothawo.com).
 */
@Component
@EnableConfigurationProperties(Support.class)
public class SupportInfo {
    @Autowired(required = false)
    private Support support;

    @PostConstruct
    public void init() {
        if (null == support) {
            System.out.println("no support");
        } else {
            System.out.println(support.toString());
        }
    }
}

Configuring the object

IntelliJ supports editing the application.properties file with code completion:

sbcfg01

This shows the available configuration properties as well as their types and default values (if the fields of the configuration class are initialized with values). The screenshot shows how field names and inner classes are mapped to the corresponding property entries.

Custom theme for a Vaadin – Spring-boot application

Recently I wrote an application based on Vaadin Spring-boot, and when I needed to modify the theme of the application I needed to do some research as how to achieve that. I think that at the time the combination of Vaadin and Spring-boot is still pretty new so that information still must be sought. So in this post I describe the necessary steps.

I use the following versions of different tools:

  • Oracle JDK 1.8.0_45
  • vaadin 7.4.5
  • vaadin-spring-start 1.0.0.beta3
  • spring-boot 1.2.3.RELEASE
  • spring 4.1.6.RELEASE

My IDE is IntelliJ IDEA 14.1.3

Creating the basic application

I create the application project (named vsbt for vaadin spring boot theme) by using the Spring Initializr from within IDEA, but it can be done via the Website https://start.spring.io as well:

vsbt01

For this demo I set up a project with the following properties that has only Vaadin as a dependency:

vsbt02

vsbt03

After finishing the setup I have an IDEA maven project with the basic application class, but still with no UI:

vsbt04

So to have something visible, I add a MainUI class which uses the valo theme and which just inserts a button in the UI. The button is nonfunctional, as this demo is only concerning with theming the UI and not with functionality:

/**
 * Copyright (c) 2015 sothawo
 *
 * http://www.sothawo.com
 */
package org.sothawo.vsbt;

import com.vaadin.annotations.Theme;
import com.vaadin.server.VaadinRequest;
import com.vaadin.spring.annotation.SpringUI;
import com.vaadin.ui.Button;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

/**
 * @author P.J. Meisch (pj.meisch@sothawo.com).
 */
@Theme("valo")
@SpringUI
public class MainUI extends UI {
    @Override
    protected void init(VaadinRequest vaadinRequest) {
        VerticalLayout layout = new VerticalLayout();
        layout.setSizeFull();
        layout.setMargin(true);
        layout.setSpacing(true);

        layout.addComponent(new Button("This is a button"));

        setContent(layout);
    }
}

Compiling and running the application gives the following result in the browser:

vsbt05

Changing the theme

Now for changing the theme. I want to create a theme named colored where I change some colors. The first step ist to put the theme files (sass files) in the src/main/webapp/VAADIN/themes/colored folder:

vsbt06

 

The styles.scss file:

@import "addons.scss";
@import "colored.scss";

The colored.scss file:

@import "../valo/valo.scss";

@mixin colored {
  @include valo;

  .v-app {
    background-color: red;
  }

  .v-button {
    background-image: none;
    background-color: yellow;
  }
}

@include colored;

I don’t get into the details of sass files here. In the MainUI class the annotation for the theme must be changed to @Theme("colored").

When I now start the program after building the program with the following command:

mvn clean package
cd target
java -jar vsbt-0.0.1-SNAPSHOT.jar

then the browser shows no theme:

vsbt07

The reason for this is that the files that are located under the src/main/webapp directory are not considered when the jar is packaged, this is only done when building a war file. So the first step that needs to be done is to specify this directory as a resource directory in the maven pom.xml:

<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
    </resource>
    <resource>
      <directory>src/main/webapp</directory>
    </resource>
  </resources>
  ...
</build>

One might be tempted to move the VAADIN/themes/colored folder to src/main/resources, but there is still a problem to be solved. When building and running the application, the logging output shows an error message like:

un 02, 2015 9:21:14 PM com.vaadin.server.VaadinServlet persistCacheEntry
WARNUNG: Error persisting scss cache /private/var/folders/xw/1zt9sly53_76h29g7067_y700000gp/T/tomcat-docbase.6378846904000133878.8080/VAADIN/themes/colored/styles.scss.cache
java.io.FileNotFoundException: /private/var/folders/xw/1zt9sly53_76h29g7067_y700000gp/T/tomcat-docbase.6378846904000133878.8080/VAADIN/themes/colored/styles.scss.cache (No such file or directory)
        at java.io.FileOutputStream.open0(Native Method)

This happens because the sass compiler – at least on my Mac – has problems with persisting the compiled css file. To remove that error it is necessary to add the sass compiler to the compile step in the maven pom.xml (and this is the reason to leave the files in the src/main/webapp directory, it’s there that the compiler searches for them):

<dependencies>
        <dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-themes</artifactId>
        </dependency>
</dependencies>
...
<plugins>
    <plugin>
        <groupId>com.vaadin</groupId>
        <artifactId>vaadin-maven-plugin</artifactId>
        <executions>
            <execution>
                <goals>
                    <goal>update-theme</goal>
                    <goal>compile-theme</goal>
                    <!--
                    <goal>clean</goal>
                    <goal>resources</goal>
                    <goal>update-widgetset</goal>
                    <goal>compile</goal>
                    -->
                </goals>
            </execution>
        </executions>
    </plugin>
</plugins>

When now compiling the project, the addons.scss and the colored.css files are created in the theme directory and packaged in the application, and after packaging and running, the browser shows the following application with no more logged errors:

vsbt08

I hope this post can help if somebody needs to theme a vaadin spring-boot application and has the same problems finding out where to put the files and how to compile them.