Skip to content

Getting Started With Spring Lemon

Sanjay Patel edited this page Apr 11, 2018 · 84 revisions

Click here to get the video tutorial FREE for now.

Introduction

This guide covers Spring Lemon 1.0.0.M2. For earlier version, refer this guide.

What is Spring Lemon

Spring Lemon is a library containing the essential configurations and code needed for developing REST services using Spring Framework and Spring Boot. It also contains a production-grade, extensible user module having features like sign up, sign in, social signup/in, stateless JWT token authentication, verify email, update profile, forgot password, change password, change email, captcha validation etc..

You can know more about Spring Lemon here.

About this guide

In this guide, we are going to walk through developing an example RESTful JSON Web Service using Spring Lemon. The finished application would be similar to the Lemon Demo application. For seeing it in action, you can use the Demo Angular 1.x front-end application.

Watch this video to see how the finished application is going to look like.

Prerequisites

To follow this guide, you must have some prior knowledge of Spring Framework’s Dependency Injection, Java configuration, Boot, MVC, Security and Spring Data JPA. If you want to learn these first, we'd highly recommend modules I, II and III of our Spring Framework Rapid Tutorial For Real World Development course.

Getting updates

Spring Lemon is a highly active project, and so is this guide. Do subscribe to receive updates!

Help and Support

  1. Links to documentation and resources are available here.
  2. Community help is available at stackoverflow.com, under the spring-lemon tag. Do not forget to tag your questions with spring-lemon.
  3. Submit an issue for any bug or enhancement in Spring Lemon or in this guide. Please check first that the issue isn't already reported earlier.
  4. Training and professional help is provided by naturalprogrammer.com.
  5. Before seeking help, you may like to go through our Mastering Real-World RESTful Web Services Development With Spring eBook or Video tutorial. That covers Spring Lemon in depth. Whether you use Spring Lemon or not, that course would make you a Real-World Spring expert - we'd highly recommend that to any Spring developer.

Creating A New Project

Follow these steps to create a new project and add Spring Lemon to it.

1. Create a new Spring Boot project

Create a new Spring Boot project using your favorite method. If you are using Spring Tool Suite (STS), you can use the Spring Starter Project Wizard, which is available at File -> New -> Spring Starter Project.

Fill in the following data there:

  • Name: The name of your application, e.g. lemon-demo
  • Type: Maven Project (Or gradle)
  • Packaging: Jar
  • Java Version: 1.8
  • Group: The group name, e.g. com.naturalprogrammer.spring
  • Artifact: The artifact name, e.g. lemon-demo
  • Description: A one line description about your application
  • Package Name: Could be something like com.naturalprogrammer.spring.lemondemo
  • Boot Version: Choose a version later than or equal to 2.0.0. We have tested it with 2.0.0.
  • Dependencies:
    • SQL: Say HSQLDB in-memory database for this demo (but you can very well choose MySQL or whatever you want)
    • Core: Optionally DevTools
    • No need to include other dependencies, e.g. Security, JPA, Mail or Web; those would be transitively included by Spring Lemon.

Leave other fields to defaults.

2. Add Spring Lemon Dependency

Add to your pom.xml (or build.gradle) the following repository and dependency:

<repositories>
  <repository>
      <id>jitpack.io</id>
      <url>https://jitpack.io</url>
  </repository>
</repositories>

<dependencies>
    ...
    <dependency>
        <groupId>com.naturalprogrammer</groupId>
        <artifactId>spring-lemon</artifactId>
        <version>1.0.0.M2</version><!-- See https://github.com/naturalprogrammer/spring-lemon/releases for latest release -->
    </dependency>
<dependencies>

Configuring Spring Lemon

Now that we have added Spring Lemon to our project, time to configure it.

Setting up application.yml

Rename application.properties inside your src/main/resources folder to application.yml, and paste into that the following:

# Spring related properties
spring:

  # database settings
  jpa:
    database: HSQL
    hibernate.ddl-auto: update

  security:
    oauth2:
      client:
        provider:
          facebook:
            user-info-uri: https://graph.facebook.com/me?fields=email,name,verified 
        registration:
          google:
            client-id: 1011974249454-6gq0hr01gqh3cndoqnss5r69tkk2nd84.apps.googleusercontent.com
            client-secret: saDA6Cj60wipncFM-hzBD-C6
          facebook:
            client-id: 1234020186718741
            client-secret: 0c0abaf685a83e879e8e48b1167c96ab

  # JSON serialization settings
  jackson:
    default-property-inclusion: NON_NULL
    
    serialization:
      write-null-map-values: false 
      
    deserialization:
      accept-single-value-as-array: true
  
  devtools:
    # Comment this if you want the app to restart
    # when source code changes
    restart.enabled: false
    livereload.enabled: false

server.servlet.session.persistent: false

logging:
  level:
    root: INFO
    com.naturalprogrammer: DEBUG
  #file: D:\\tmp\\dev-log.txt

lemon:

  # application-url: http://localhost:9000
  # oauth2-authentication-success-url: http://localhost:9000/social-login-success?token=

  # First ADMIN user
  admin:
    username: [email protected]
    password: admin!
    
   # Spring Lemon flags
   # enabled:
      # json-prefix: false
      
  cors:
    # Comma separated values of CORS allowedOrigins
    # If this property is not given, CORS is not configured
    allowed-origins: http://localhost:9000
    
  recaptcha:
    sitekey: 6LdwxRcUAAAAABkhOGWQXhl9FsR27D5YUJRuGzx0
    secretkey: 6LdwxRcUAAAAADaG0Eo1qkYCco15cnngiBoBt2IO
  
  jwt:
    # An aes-128-cbc key generated at https://asecuritysite.com/encryption/keygen (take the "key" field)
    secret: 841D8A6C80CBA4FCAD32D5367C18C53B
    # expiration-millis: 864000000 # 10 days
    # short-lived-millis: 120000   # two minutes

  # Properties to be passed to client
  shared:
    fooBar: 123...

Notice above the following:

  1. You may like to use a persistent database instead of HSQL. In such case, you'll need to create a database and replace the datasource details with yours. The following is an example MySQL configuration:
    jpa:
      hibernate:
        ddl-auto: update
        use-new-id-generator-mappings: false
    datasource:
      url: jdbc:mysql://localhost:3306/lemon?useSSL=false
      username: lemon
      password: lemon
    
  2. The CORS allowed-origins property is needed if you plan to have web clients (e.g. an AngularJS client) hosted cross origin.
  3. By providing recaptcha sitekey and secretkey, we tell Spring Lemon to support Google reCAPTCHA validation. If you don't provide these properties, captcha validation won't be supported. The sitekey and secretkey given above would work only at localhost. You should replace those with your keys in production.
  4. See how facebook and google OAuth2 clients are configured. That tells Spring Lemon to support google and facebook signup/in. The client-id and client-secret values given above would work only at localhost. You should replace those with your keys in production.
  5. The application-url property provides the base url of your web front-end (e.g. an AngularJS application). The default value is http://localhost:9000.
  6. The oauth2-authentication-success-url property is used for building the URL to redirect the user to after successful facebook/google sign in. The default value is http://localhost:9000/social-login-success?token=.
  7. When an application is installed, it's helpful to have its database initialized with an ADMIN user. The admin.username and admin.password become the credentials of that first administrator. At application startup, Spring Lemon will check if that user exists. If not, the user will be created, with ADMIN rights.
  8. jwt.secret is used for signing and encrypting JWT tokens. Spring Lemon uses Nimbus JWE tokens with shared key.
  9. jwt.expiration-millis tells how many milliseconds the JWT tokens would be valid. Default is 864000000 (10 days)
  10. jwt.short-lived-millis tells how many milliseconds shortlived tokens should be valid. Default is 120000 (2 minutes)

More details are covered in our Mastering Real-World RESTful Web Services Development With Spring course.

Optional - Sending Emails

Spring Lemon comes with a MailSender service for sending emails. To configure that to use an SMTP mail sending platform like GMail, add the following to application.yml:

spring:
  mail:
    host: smtp.gmail.com
    username: [email protected]
    password: xxxxxx

    properties:
      mail:
        smtp:
          auth: true
          ssl.enable: true
          socketFactory:
            port: 465
            class: javax.net.ssl.SSLSocketFactory
            fallback: false

The above configuration works with GMail, provided you have enabled Google 2-step Verification, and the password is an application password. The properties might differ for other SMTP services.

If you skip the above configuration, Spring Lemon will just write the email verification and forgot password mails onto the log, which may be fine for a demo.

Spring Lemon also allows you to configure a MailSender based on non-SMTP services, e.g. AWS SES. Details are covered in our Mastering Real-World RESTful Web Services Development With Spring course.

Optional - Spring Lemon flags

Spring Lemon provides some flags to enable/disable certain of its features. Defaults would be fine for most of the applications. But, just to give you an example, if you want to enable JSON vulnerablity protection, add the following property to application.yml:

lemon.enabled.json-prefix: true

Refer our Mastering Real-World RESTful Web Services Development With Spring course to know about all the Spring Lemon configuration options and flags.

Providing validation messages

Spring Lemon uses I18n error messages when validating the input parameters. Hence, create a file named ValidationMessages.properties inside src\main\resources, and paste the following into that:

com.naturalprogrammer.spring.blank.email: Email needed
com.naturalprogrammer.spring.invalid.email: Not a well formed email address
com.naturalprogrammer.spring.invalid.email.size: Email must be between {min} and {max} characters
com.naturalprogrammer.spring.duplicate.email: Email Id already used
com.naturalprogrammer.spring.wrong.captcha: Looks like you are a robot! Please try again.

com.naturalprogrammer.spring.invalid.password.size: Password must be between {min} and {max} characters

com.naturalprogrammer.spring.different.passwords: Passwords do not match
com.naturalprogrammer.spring.blank.password: Password needed

Providing other I18n messages

Apart from the validation messages, Spring Lemon also uses some other I18n messages. Provide those in a new messages.properties file inside src\main\resources, as below:

com.naturalprogrammer.spring.validationError: Validation Error

com.naturalprogrammer.spring.verifySubject: Please verify your email id
com.naturalprogrammer.spring.verifyEmail: Hi,<br/><br/>Your email id at XYZ is unverified. Please click the link below to get verified:<br/><br/>{0}<br/><br/>

com.naturalprogrammer.spring.alreadyVerified: Already verified
com.naturalprogrammer.spring.wrong.verificationCode: Wrong verification code

com.naturalprogrammer.spring.wrong.login: Wrong login
com.naturalprogrammer.spring.notFound: Not found

com.naturalprogrammer.spring.forgotPasswordSubject: Reset Password
com.naturalprogrammer.spring.forgotPasswordEmail: Please click <a href="{0}">here</a> to reset your password.

com.naturalprogrammer.spring.versionException: Could not save. Looks like the {0} is already modified by somebody else. Please refresh and try again.

com.naturalprogrammer.spring.wrong.password: Wrong password

com.naturalprogrammer.spring.changeEmailSubject: Want to change your email?
com.naturalprogrammer.spring.changeEmailEmail: Please click <a href="{0}">here</a> to change your email.

com.naturalprogrammer.spring.wrong.changeEmailCode: Could not change email. Already changed?
com.naturalprogrammer.spring.duplicate.email: Email Id already used
com.naturalprogrammer.spring.blank.newEmail: No new email found. Looks like you have already changed.

com.naturalprogrammer.spring.oauth2EmailNeeded: {0} didn't provide any email id. OAuth2 scopes configured properly?
com.naturalprogrammer.spring.oauth2EmailNotVerified: Can't proceed because your email isn't yet verified at {0}

com.naturalprogrammer.spring.wrong.audience: Wrong token audience
com.naturalprogrammer.spring.obsoleteToken: Token has become obsolete
com.naturalprogrammer.spring.expiredToken: Expired token

com.naturalprogrammer.spring.notGoodAdminOrSameUser: Only a good Admin or same user is permitted for this operation

Extending Spring Lemon

Spring Lemon comes with a few base classes, which you need to extend and configure.

AbstractUser

Spring Lemon comes with an AbstractUser entity, which you need to extend as below:

import javax.persistence.Entity;
import javax.persistence.Table;

import com.naturalprogrammer.spring.lemon.domain.AbstractUser;

@Entity
@Table(name="usr")
public class User extends AbstractUser<User,Long> {

    private static final long serialVersionUID = 2716710947175132319L;   
}

The Long generic parameter above is the type of the primary key.

In the next section, we are going see how to add new a field to this class. For more details, refer our Mastering Real-World RESTful Web Services Development With Spring course.

LemonController

Spring Lemon API endpoints are coded in the abstract LemonController class. Extend it as below:

@RestController
@RequestMapping("/api/core")
public class MyController extends LemonController<User, Long> {

}

LemonService

Spring Lemon comes with an abstract LemonService class, which has the service methods used by LemonController. Extend it as below:

@Service
public class MyService extends LemonService<User, Long> {

    @Override
    public User newUser() {
        return new User();
    }
}

LemonService has modular methods that you can override. At the least, you need to override the newUser method, as above. We are going to do a couple of more overrides in the next section, and more details are discussed in our Mastering Real-World RESTful Web Services Development With Spring course.

AbstractUserRepository

Finally, you need to extend Spring Lemon's AbstractUserRepository, as below:

public interface UserRepository extends AbstractUserRepository<User, Long> {

}

Customizing the user module

Whatever we have coded so far is already a complete API with a great user module. But, its users have only email and password, and no name! So, let's add a name field.

Adding a name to the User entity

Add the following lines to the User class that we had coded previously:

    public static final int NAME_MIN = 1;
    public static final int NAME_MAX = 50;

    @JsonView(SignupInput.class)
    @NotBlank(message = "{blank.name}", groups = {SignUpValidation.class, UpdateValidation.class})
    @Size(min=NAME_MIN, max=NAME_MAX, groups = {SignUpValidation.class, UpdateValidation.class})
    @Column(nullable = false, length = NAME_MAX)
    private String name;

    public String getName() {
        return name;
    }

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

Note the following:

  1. @JsonView(SignupInput.class) tells Spring Lemon to recognize this field when signing up.
  2. groups = {SignUpValidation.class, UpdateValidation.class} tells Spring Lemon to apply the validation while signing up or updating profile.

The @NotBlank annotation above expects a blank.name entry in ValidationMessages.properties. So, add the following line there:

blank.name: Name required

Spring Lemon stores loggedin-user data in a UserDto object. To add our name field to that, add some more lines in User:

    
    public static class Tag implements Serializable {
		
		private static final long serialVersionUID = -2129078111926834670L;
	
		private String name;
	
		public String getName() {
		    return name;
		}
	
		public void setName(String name) {
	            this.name = name;
		}
    }

    @Override
    public Tag toTag() {
		
		Tag tag = new Tag();
		tag.setName(name);
		return tag;
    }

For more details, refer our Mastering Real-World RESTful Web Services Development With Spring course.

Extracting name from Google, Facebook

For extracting name when a user does Google or Facebook signup, add the following lines to MyService that we coded previously:

    @Override
    public void fillAdditionalFields(String registrationId, User user, Map<String, Object> attributes) {
    	
    	String nameKey;
    	
    	switch (registrationId) {
    		
    	case "facebook":
    		nameKey = StandardClaimNames.NAME;
    		break;
    		
    	case "google":
		nameKey = StandardClaimNames.NAME;
		break;
			
	default:
		throw new UnsupportedOperationException("Fetching name from " + registrationId + " login not supprrted");
    	}
    	
    	user.setName((String) attributes.get(nameKey));
    }

Updating name

For updating name when updating profile, add the following method to MyService:

    @Override
    protected void updateUserFields(User user, User updatedUser, UserDto<Long> currentUser) {

        super.updateUserFields(user, updatedUser, currentUser);

        user.setName(updatedUser.getName());

        LemonUtils.afterCommit(() -> {
            if (currentUser.getId().equals(user.getId()))
                currentUser.setTag(user.toTag());
        });
    }

As you see, the super method would be called first. Then, the name would be updated. Finally, if a user is updating his own profile, Spring Security's principal would also be updated.

Mastering Real-World RESTful Web Services Development With Spring covers it in details. Also, a good way to get more familiar with Spring Lemon is to browse its source code. For example, if you are using STS, pressing F3 while your mouse is over super.updateUserFields(...) will show you its source code.

Adding name to the first admin user

If you remember, we told you that an ADMIN user is added at application startup if it doesn't already exist. To give it a name, add the following lines to MyService:

@Override
protected User createAdminUser() {

    User user = super.createAdminUser(); 
    user.setName("Administrator");
    return user;
}

If you are using a persistent database and tried running the application previously, the ADMIN user would already have been created in your local database. Unless that's deleted, the above code wouldn't be run by Spring Lemon, and so the name of the ADMIN user won't be set.

Your application is complete!

This completes your application! Here is the documentation of the API that you have just developed. This API can be used from single page JavaScript applications or any consumer. In fact, we have created a Demo Angular 1.x front-end application, which you can use to test your API out.

Next Steps

  1. Check the API that you have developed, using Postman or the Demo Angular 1.x front-end application.
  2. Check our Lemon Demo Application, which is quite similar to the one we just developed, but additionally has automated tests.
  3. Go through our Mastering Real-World RESTful Web Services Development With Spring course.
  4. Ask questions at stackoverflow.com (use spring-lemon tag), submit an issue for any bug or enhancement (please check first that the issue isn't already reported earlier), or seek for professional help.
  5. Be a committer! Mail to info at naturalprogrammer dot com.
Clone this wiki locally