Adding Social Sign In to a Spring MVC Web Application: Registration and Login

The first part of this tutorial described how we can configure Spring Social 1.1.0 and Spring Security 3.2.0 but it left two very important questions unanswered.

These questions are:

  • How can a user create a new user account?
  • How can a user log in?

It is time to get our hands dirty and answer to these questions. The requirements of our example application are:

  • It must be possible to create a "traditional" user account. This means that the user is authenticated by using username and password.
  • It must be possible to create a user account by using a SaaS API provider such as Facebook or Twitter. In this case, the user is authenticated by the SaaS API provider.
  • It must be possible to log in by using username and password.
  • It must be possible to log in by using a SaaS API provider.

Let's start fulfilling these requirements. The first thing that we have to do is to create a login page for our application.

Creating the Login Page

The login page of our application has three responsibilities which are described in the following:

  1. It must provide a way to log in by using username and password.
  2. It must have a link to the registration page. If a user wants to create "traditional" user account, he can do this by clicking this link.
  3. It must have the links which start the social sign in flow. These links can be used for two purposes:
    • If the user in question has a user account, he can log in by using a SaaS API provider.
    • If the user doesn't have a user account, he can create one by using a SaaS API provider.

The application context configuration which we created in the first part of this tutorial specifies some requirements for our login page. These requirements are:

  1. If an anonymous user tries to access a protected page, he is redirected to url '/login'.
  2. When the login form of our application is submitted, our application must create a POST request to url '/login/authenticate'.
  3. We must include a CSRF token to the POST request which is created when our login form is submitted. The reason for this is that the CSRF protection of Spring Security 3.2.0 is enabled by default when we configure Spring Security by using Java configuration.
  4. The name of the username parameter is username. This is the default value of the username parameter when Spring Security is configured by using Java configuration
  5. The name of the password parameter is password. This the default value value of the password parameter when Spring Security is configured by using Java configuration.
  6. If a form login fails, the user is redirected to url '/login?error=bad_credentials'. This means that when the login page is requested and the value of the of error request parameter is 'bad_credentials', we must show an error message to the user.
  7. The SocialAuthenticationFilter processes GET requests send to url '/auth/{provider}'. This means that
    • We can start the Facebook sign in flow by sending a GET request to url '/auth/facebook'.
    • We can start the Twitter sign in flow by sending a GET request to url '/auth/twitter'.

Let's start by creating a controller which renders the login page.

Creating the Controller

We can implement the controller which renders the login page by following these steps:

  1. Create a LoginController class and annotate the created class with the @Controller annotation.
  2. Add a showLoginPage() method to the controller class. This method returns the name of the rendered view.
  3. Implement the showLoginPage() method by following these steps:
    1. Annotate the method with the @RequestMapping annotation and ensure that the showLoginPage() method processes GET requests send to url '/login'.
    2. Return the name of the login view ('user/login').

The source code of the LoginController class looks as follows:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class LoginController {

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String showLoginPage() {
        return "user/login";
    }
}

Our next step is to create the login page by using JSP. Let's see how this is done.

Creating the JSP Page

We can create the login page by following these steps:

  1. Ensure that that the login form and social sign in buttons are shown only to anonymous users. We can do this by following these steps:
    1. Wrap the login form and social sign in buttons inside the authorize tag of the Spring Security tag library.
    2. Set the value of the access attribute to isAnonymous().
  2. Show an error message if log in fails. We can get the localized error message by using the message tag of the Spring tag library if the value of the request parameter called error is 'bad_credentials'.
  3. Implement the login form by following these steps:
    1. Ensure that when the login form is submitted, a POST request is send to url '/login/authenticate'.
    2. Add CSRF token to the request which is send when the login form is submitted. This is required because we enabled the CSRF protection of Spring Security in the first part of this tutorial.
    3. Add a username field to the login form.
    4. Add a password field to the login form.
    5. Add a submit button to the login form.
  4. Add 'Create user account' link below the login form. This link creates a GET request to url '/user/register' (registration page).
  5. Add social sign buttons to the login page by following these steps:
    1. Add Facebook sign in button. This button must create a GET request to url '/auth/facebook'.
    2. Add Twitter sign in button. This button must create a GET request to url '/auth/twitter'.
  6. Ensure that a help message is shown if an authenticated user accesses the login page. We can do this by following these steps:
    1. Wrap the error message area inside the authorize tag of the Spring Security tag library.
    2. Set the value of the access attribute to isAuthenticated().
    3. Get the localized error message by using the message tag of the Spring tag library.

The source code of the login.jsp page looks as follows:

<!DOCTYPE html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<html>
<head>
    <title></title>
    <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/static/css/social-buttons-3.css"/>
</head>
<body>
<div class="page-header">
    <h1><spring:message code="label.user.login.page.title"/></h1>
</div>
<!-- 
	If the user is anonymous (not logged in), show the login form
	and social sign in buttons.
-->
<sec:authorize access="isAnonymous()">
	<!-- Login form -->
    <div class="panel panel-default">
        <div class="panel-body">
            <h2><spring:message code="label.login.form.title"/></h2>
			<!--
				Error message is shown if login fails.
			-->
            <c:if test="${param.error eq 'bad_credentials'}">
                <div class="alert alert-danger alert-dismissable">
                    <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
                    <spring:message code="text.login.page.login.failed.error"/>
                </div>
            </c:if>
			<!-- Specifies action and HTTP method -->
            <form action="${pageContext.request.contextPath}/login/authenticate" method="POST" role="form">
				<!-- Add CSRF token -->
                <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
                <div class="row">
                    <div id="form-group-email" class="form-group col-lg-4">
                        <label class="control-label" for="user-email"><spring:message code="label.user.email"/>:</label>
                        <!-- Add username field to the login form -->
						<input id="user-email" name="username" type="text" class="form-control"/>
                    </div>
                </div>

                <div class="row">
                    <div id="form-group-password" class="form-group col-lg-4">
                        <label class="control-label" for="user-password"><spring:message code="label.user.password"/>:</label>
                        <!-- Add password field to the login form -->
						<input id="user-password" name="password" type="password" class="form-control"/>
                    </div>
                </div>
				<div class="row">
					<div class="form-group col-lg-4">
						<!-- Add submit button -->
						<button type="submit" class="btn btn-default"><spring:message code="label.user.login.submit.button"/></button>
					</div>
				</div>
            </form>
			<div class="row">
				<div class="form-group col-lg-4">
					<!-- Add create user account link -->
					<a href="${pageContext.request.contextPath}/user/register"><spring:message code="label.navigation.registration.link"/></a>
				</div>
			</div>
        </div>
    </div>
	<!-- Social Sign In Buttons -->
    <div class="panel panel-default">
        <div class="panel-body">
            <h2><spring:message code="label.social.sign.in.title"/></h2>
            <div class="row social-button-row">
                <div class="col-lg-4">
					<!-- Add Facebook sign in button -->
                    <a href="${pageContext.request.contextPath}/auth/facebook"><button class="btn btn-facebook"><i class="icon-facebook"></i> | <spring:message code="label.facebook.sign.in.button"/></button></a>
                </div>
            </div>
            <div class="row social-button-row">
                <div class="col-lg-4">
					<!-- Add Twitter sign in Button -->
                    <a href="${pageContext.request.contextPath}/auth/twitter"><button class="btn btn-twitter"><i class="icon-twitter"></i> | <spring:message code="label.twitter.sign.in.button"/></button></a>
                </div>
            </div>
        </div>
    </div>
</sec:authorize>
<!-- 
	If the user is already authenticated, show a help message instead
	of the login form and social sign in buttons.
-->
<sec:authorize access="isAuthenticated()">
    <p><spring:message code="text.login.page.authenticated.user.help"/></p>
</sec:authorize>
</body>
</html>
Our application uses Twitter Bootstrap 3. The social sign in buttons are created by using a Twitter Bootstrap plugin called Social Buttons for Twitter Bootstrap 3.

We have now created the login page which fulfils our requirements. The relevant part of our login page looks as follows:

social-login-form

Our next step is to implement the registration function. Let's get started.

Implementing the Registration Function

The registration function of our example application has two requirements:

  1. It must be possible to create a "normal" user account.
  2. It must be possible to create a user account by using social sign in.

Also, the application context configuration which we created in the first part of this tutorial specifies one requirement for the registration function:

The url of the registration page must be '/signup'. This is the default value of the sign up (also known as registration) page, and at the moment it is not possible to override this url if we configure the application context by using Java configuration. However, since the url '/signup' looks a bit ugly, we will replace this url with the url '/user/register'.

Note: It is possible to override the default value of the sign up url if the application context is configured by using XML configuration files (look for the property called signUpUrl).

The user of our example application can end up to the registration page by using one of the following methods:

  1. He clicks the 'Create user account link. This link starts the "normal" registration process.
  2. He clicks the social sign in button which starts the social sign in flow.

Because it is hard to get the general idea from such a shallow description, I have created a diagram which illustrates the steps a user has to follow before he ends up to the registration page of our example application. This diagram has two rules:

  1. The grey colour represents actions which are the responsibility of our example application.
  2. The blue colour represents actions which are the responsibility of the SaaS API provider.

This diagram looks as follows:

registration

Let's move on and start by creating a form object for the registration form.

Creating the Form Object

The form object is a data transfer object which contains the information entered to the registration form and specifies the validation constraints which are used to validate that information.

Before we implement the form object, let’s take a quick look at the validation constraints which we use to validate our form object. These constraints are described in following:

  • The @Email annotation ensures that the email address given by the user is well-formed.
  • The @NotEmpty annotation ensures that the value of the field cannot be empty or null.
  • The @Size annotation ensures that the length of the field value isn't longer than the maximum length of the field.

Let’s move on and create the form object. We can do this by following these steps:

  1. Create a class called RegistrationForm.
  2. Add an email field to the class and specify its validation constraints by following these rules:
    1. The email must be well-formed.
    2. The email cannot be empty or null.
    3. The maximum length of the email is 100 characters.
  3. Add a firstName field to the class and specify its validation constraints by following these rules:
    1. The first name cannot be empty or null.
    2. The maximum length of the first name is 100 characters.
  4. Add a lastName field to the class and specify its validation constraints by following these rules:
    1. The last name cannot be empty or null.
    2. The maximum length of the last name is 100 characters.
  5. Add a password field to the class.
  6. Add a passwordVerification field to the class.
  7. Add a signInProvider field to the class. The type of this field is SocialMediaService.
  8. Add a isNormalRegistration() method to created class. This method returns true if the value of the signInProvider field is null. If the value of that field is not null, this method returns false.
  9. Add a isSocialSignIn() method to the created class. This method returns true if the value of the signInProvider field is not null. If the value of that field is null, this method returns false.

The source code of the RegistrationForm class looks as follows:

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;

import javax.validation.constraints.Size;

@PasswordsNotEmpty(
        triggerFieldName = "signInProvider",
        passwordFieldName = "password",
        passwordVerificationFieldName = "passwordVerification"
)
@PasswordsNotEqual(
        passwordFieldName = "password",
        passwordVerificationFieldName = "passwordVerification"
)
public class RegistrationForm {

    @Email
    @NotEmpty
    @Size(max = 100)
    private String email;

    @NotEmpty
    @Size(max = 100)
    private String firstName;

    @NotEmpty
    @Size(max = 100)
    private String lastName;

    private String password;

    private String passwordVerification;

    private SocialMediaService signInProvider;

	//Constructor is omitted for the of clarity.
	
	public boolean isNormalRegistration() {
		return signInProvider == null;
	}

	public boolean isSocialSignIn() {
		return signInProvider != null;
	}
	
	//other methods are omitted for the sake of clarity.
}

The SocialMediaService is an enum which identifies the SaaS API provider which was used to authenticate the user. Its source code looks as follows:

public enum SocialMediaService {
    FACEBOOK,
    TWITTER
}

Wait, didn't we just forget something?

What on earth are those weird annotations such as @PasswordsNotEqual and @PasswordsNotEmpty?

Well, they are custom bean validation constraints. Let’s find out how we can create these constraints.

Creating the Custom Validation Constraints

We have to create two custom validation constraints for our example application. If the user is creating a "normal" user account, we have to ensure that:

  1. The password and passwordVerification fields of our form object cannot be empty or null.
  2. The password and passwordVerification fields are equal.

We can create custom validation constraints by following these steps:

  1. Create a constraint annotation.
  2. Implement a custom validator class which ensures that the constraint is not broken.
The reference manual of Hibernate validator 4.2 has more information about creating custom validation constraints.

Let's start by creating the constraint annotations.

Creating the Constraint Annotations

When we create the constraint annotations, we have to always follow these common steps:

This section explains how you can create the required constraint annotations. However, you don't have to create the CustomConstraint annotation. I use it only as example that demonstrates the required steps, and it is not used in the actual example application.
  1. Create an annotation type. Let's assume that the name of our annotation type is CommonConstraint.
  2. Annotate the created annotation type with the @Target annotation and set its value to {ElementType.TYPE, ElementType.ANNOTATION_TYPE} (the Javadoc of the ElementType enum). This means that both classes and annotation types can be annotated with the @CommonConstraint annotation.
  3. Annotate the created annotation type with the @Retention annotation and set its value to RetentionPolicy.RUNTIME. This means that the @CommonConstraint annotation is available at runtime and it can be read by using reflection.
  4. Annotate the created annotation type with the @Constraint annotation and set the value of its validatedBy attribute. The value of this attribute specifies the class which validates the classes annotated with the @CommonConstraint annotation.
  5. Annotate the class with the @Documented annotation. This means that the @CommonConstraint annotation is visible in the Javadoc documentation of all classes which are annotated with it.
  6. Add a message attribute to the annotation type. The type of this attribute is String, and its default value is 'CommonConstraint'.
  7. Add a groups attribute to the annotation type. The type of this attribute is an array of type Class<?>, and its default value is empty array. This attribute allows the creation of validation groups.
  8. Add a payload attribute to the annotation type. The type of this attribute is an array of type Class<? extends Payload>, and its default value is empty array. This attribute is not used by the Bean Validation API but clients of the API can assign custom PayLoad objects to the constraint.

The source code of the @CommonConstraint annotation looks as follows:

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Target( { TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = CommonConstraintValidator.class)
@Documented
public @interface CommonConstraint {

    String message() default "CommonConstraint";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

Let's move on and find out how we can create the @PasswordsNotEmpty and @PasswordNotEqual annotations.

First, we have to create the @PasswordsNotEmpty annotation. We can do this by following these steps:

  1. Follow the common steps described earlier and make the following changes to the created annotation:
    1. Rename the annotation type to PasswordsNotEmpty.
    2. Set the value of the @Constraint annotation's validatedBy attribute to PasswordsNotEmptyValidator.class.
  2. Add a triggerFieldName attribute to the annotation type. The type of this attribute is String, and its default value is empty string. This attribute specifies the name of the field which triggers our custom constraint if its value is null.
  3. Add a passwordFieldName attribute to the annotation type. The type of this attribute is String, and its default value is empty string. This attribute specifies the name of the field which contains the password of the user.
  4. Add a passwordVerificationFieldName attribute to to the annotation type. The type of this attribute is String, and its default value is empty string. This attribute specifies the name of the field which contains the password verification of the user.

The source code of the @PasswordsNotEmpty annotation looks as follows:

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Target( { TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = PasswordsNotEmptyValidator.class)
@Documented
public @interface PasswordsNotEmpty {

    String message() default "PasswordsNotEmpty";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    String triggerFieldName() default "";

    String passwordFieldName() default "";

    String passwordVerificationFieldName() default "";
}

Second, we have to create the @PasswordsNotEqual annotation. We can do this by following these steps:

  1. Follow the common steps described earlier and make the following changes to the created annotation:
    1. Rename the annotation type to PasswordsNotEqual.
    2. Set the value of the @Constraint annotation's validatedBy attribute to PasswordsNotEqualValidator.class.
  2. Add a passwordFieldName attribute to the annotation type. The type of this attribute is String, and its default value is empty string. This attribute specifies the name of the field which contains the password of the user.
  3. Add a passwordVerificationFieldName attribute to the annotation type. The type of this attribute is String, and its default value is empty string. This attribute specifies the name of the field which contains the password verification of the user.

The source code of the @PasswordsNotEqual annotation looks as follows:

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target( { TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = PasswordsNotEqualValidator.class)
@Documented
public @interface PasswordsNotEqual {

    String message() default "PasswordsNotEqual";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    String passwordFieldName() default "";

    String passwordVerificationFieldName() default "";
}

We have now created our constraint annotations. Let's move on and take a look at a utility class which we use when we are implementing the validator classes for our custom constraint annotations.

Creating the Validation Utility Class

The validation utility class provides two static methods which are described in the following:

  • The first method is used to add validation errors to a field of the validated object.
  • The second method returns the value of the requested field.

We can implement this class by following these steps:

  1. Create a class called ValidatorUtil.
  2. Add a addValidationError() method to the ValidatorUtil class. This method takes two parameters which are described in the following:
    1. The first parameter is the name of the field.
    2. The second parameter is a ConstraintValidatorContext object.
  3. Implement the addValidationError() method by following these steps:
    1. Create a new constraint violation and ensure that the message specified by the constraint annotation is used as a prefix when the constraint violation message is build.
    2. Add the field to the constraint validation error.
    3. Create the constraint validation error.
  4. Add a getFieldValue() method to the ValidatorUtil class. This method returns the field value of the specified field and takes two parameters which are described in the following:
    1. The first parameter is the object which contains the requested field.
    2. The second parameter is the name of the requested field.
  5. Implement the getFieldValue() method by following these steps:
    1. Get a reference to the Field object which reflects the requested field.
    2. Ensure that we can access the value of the field even if the field is private.
    3. Return the field value.

The source code of the ValidatorUtil class looks as follows:

import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.Field;

public class ValidatorUtil {

    public static void addValidationError(String field, ConstraintValidatorContext context) {
        context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
                .addNode(field)
                .addConstraintViolation();
    }

    public static Object getFieldValue(Object object, String fieldName) throws NoSuchFieldException, IllegalAccessException {
        Field f = object.getClass().getDeclaredField(fieldName);
        f.setAccessible(true);
        return f.get(object);
    }
}

We are now ready to implement our validator classes. Let's see how that is done.

Creating the Validator Classes

First, we have to create the validator class which can validate classes annotated with the @PasswordsNotEmpty annotation. We can do this by following these steps:

  1. Create a PasswordsNotEmptyValidator class and implement the ConstraintValidator interface. The ConstraintValidator interface defines two type parameters which are described in the following:
    1. The first type parameter is the annotation type. Set the value of this type parameter to PasswordsNotEmpty.
    2. The second type parameter is the type of element which can be validated by the validator. Set the value of this type parameter to Object (We could set this to RegistrationForm but using the type Object ensures that our validator is not restricted to this example application).
  2. Add a private validationTriggerFieldName field to the created class and set its type to String.
  3. Add a private passwordFieldName field to the created class and set its type to String.
  4. Add a private passwordVerificationFieldName field to the created class and set its type to String.
  5. Add the initialize(PasswordsNotEmpty constraintAnnotation) method of the ConstraintValidator interface to the validator class and implement it by following these steps:
    1. Set the value of the validationTriggerFieldName field.
    2. Set the value of the passwordFieldName field.
    3. Set the value of the passwordVerificationFieldName field.
  6. Add a private isNullOrEmpty(String field) method to the created class. This method returns true if the String given as a method parameter is null or empty. Otherwise this method returns false.
  7. Add a private passwordsAreValid(Object value, ConstraintValidatorContext context) method to the created class. This method returns a true if the password fields are valid and false otherwise. This method takes two method parameters which are described in the following:
    1. The first method parameter is the validated object.
    2. The second method parameter is a ConstraintValidatorContext object.
  8. Implement the passwordsAreValid() method by following these steps:
    1. Obtain the value of the password field by calling the getFieldValue() method of the ValidatorUtil class. Pass the validated object and the name of the password field as method parameters.
    2. If the value of the password field is empty or null, add a validation error by calling the addValidationError() method of the ValidatorUtil class. Pass the name of the password field and the ConstraintValidatorContext object as method parameters.
    3. Obtain the value of the passwordVerification field by calling the getFieldValue() method of the ValidatorUtil class. Pass the validated object and the name of the password verification field as method parameters.
    4. If the value of the password verification field is empty or null, add a validation error by calling the addValidationError() method of the ValidatorUtil class. Pass the name of the password verification field and the ConstraintValidatorContext object as method parameters.
    5. If validation errors were found, return false. Otherwise return true.
  9. Add the isValid(Object value, ConstraintValidatorContext context) method of the ConstraintValidator interface to the validator class and implement it by following these steps:
    1. Disable the default error message by calling the disableDefaultConstraintViolation() method of the ConstraintValidatorContext interface.
    2. Add a try-catch structure to the method and catch all checked exceptions. If a checked exception is thrown, catch it and wrap it inside a RuntimeException. This is required because the isValid() method of the ConstraintValidator interface cannot throw checked exceptions Implement the try block by following these steps:
      1. Get the value of the validation trigger field by calling the getFieldValue() method of the ValidatorUtil class. Pass the validated object and the name of the validation trigger field as method parameters.
      2. If the value of the validation trigger field is null, call the passwordFieldsAreValid() method and pass the validated object and the ConstraintValidatorContext object as method parameters. Return the boolean value returned by this method.
      3. If the value of the validation trigger field is not null, return true.

The source code of the PasswordsNotEmptyValidator class looks as follows:

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class PasswordsNotEmptyValidator implements ConstraintValidator<PasswordsNotEmpty, Object> {

    private String validationTriggerFieldName;
    private String passwordFieldName;
    private String passwordVerificationFieldName;

    @Override
    public void initialize(PasswordsNotEmpty constraintAnnotation) {
        validationTriggerFieldName = constraintAnnotation.triggerFieldName();
        passwordFieldName = constraintAnnotation.passwordFieldName();
        passwordVerificationFieldName = constraintAnnotation.passwordVerificationFieldName();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        context.disableDefaultConstraintViolation();
        try {
            Object validationTrigger = ValidatorUtil.getFieldValue(value, validationTriggerFieldName);
            if (validationTrigger == null) {
                return passwordFieldsAreValid(value, context);
            }
        }
        catch (Exception ex) {
            throw new RuntimeException("Exception occurred during validation", ex);
        }

        return true;
    }

    private boolean passwordFieldsAreValid(Object value, ConstraintValidatorContext context) throws NoSuchFieldException, IllegalAccessException {
        boolean passwordWordFieldsAreValid = true;

        String password = (String) ValidatorUtil.getFieldValue(value, passwordFieldName);
        if (isNullOrEmpty(password)) {
            ValidatorUtil.addValidationError(passwordFieldName, context);
            passwordWordFieldsAreValid = false;
        }

        String passwordVerification = (String) ValidatorUtil.getFieldValue(value, passwordVerificationFieldName);
        if (isNullOrEmpty(passwordVerification)) {
            ValidatorUtil.addValidationError(passwordVerificationFieldName, context);
            passwordWordFieldsAreValid = false;
        }

        return passwordWordFieldsAreValid;
    }

    private boolean isNullOrEmpty(String field) {
        return field == null || field.trim().isEmpty();
    }
}

Second, we have to create the validator class which validates classes annotated with the @PasswordsNotEqual annotation. We can do this by following these steps:

  1. Create a PasswordsNotEqualValidator class and implement the ConstraintValidator interface. The ConstraintValidator interface defines two type parameters which are described in the following:
    1. The first type parameter is the annotation type. Set the value of this type parameter to PasswordsNotEqual.
    2. The second type parameter is the type of element which can be validated by the validator. Set the value of this type parameter to Object (We could set this to RegistrationForm but using the type Object ensures that our validator is not restricted to this example application).
  2. Add a private passwordFieldName field to the created class and set its type to String.
  3. Add a private passwordVerificationFieldName field to the created class and set its type to String.
  4. Add the initialize(PasswordsNotEqual constraintAnnotation) method of the ConstraintValidator interface to the validator class and implement it by following these steps:
    1. Set the value of the passwordFieldName field.
    2. Set the value of the passwordVerificationFieldName field.
  5. Add a private passwordsAreNotEqual(String password, String passwordVerification) method to the created class. If the password and password verification given as method parameters aren't equal, this method returns true. Otherwise this method returns false.
  6. Add the isValid(Object value, ConstraintValidatorContext context) method of the ConstraintValidator interface to the validator class and implement it by following these steps:
    1. Disable the default error message by calling the disableDefaultConstraintViolation() method of the ConstraintValidatorContext interface.
    2. Add a try-catch structure to the method and catch all checked exceptions. If a checked exception is thrown, catch it and wrap it inside a RuntimeException. This is required because the isValid() method of the ConstraintValidator interface cannot throw checked exceptions Implement the try block by following these steps:
      1. Get the value of the password field by calling the getFieldValue() method of the ValidatorUtil class. Pass the validated object and the name of the password field as method parameters.
      2. Get the value of the password verification field by calling the getFieldValue() method of the ValidatorUtil class. Pass the validated object and the name of the password verification field as method parameters.
      3. Check if passwords aren't equal by calling the passwordsAreNotEqual() method. Pass the password and password verification as method parameters.
      4. If the password and password verification aren't equal, add validation error to both password and password verification fields by calling the addValidationError() method of the ValidatorUtil class. Return false.
      5. If password and password verification were are, return true.

The source code of the PasswordsNotEqualValidator looks as follows:

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class PasswordsNotEqualValidator implements ConstraintValidator<PasswordsNotEqual, Object> {

    private String passwordFieldName;

    private String passwordVerificationFieldName;

    @Override
    public void initialize(PasswordsNotEqual constraintAnnotation) {
        this.passwordFieldName = constraintAnnotation.passwordFieldName();
        this.passwordVerificationFieldName = constraintAnnotation.passwordVerificationFieldName();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        context.disableDefaultConstraintViolation();
        try {
            String password = (String) ValidatorUtil.getFieldValue(value, passwordFieldName);
            String passwordVerification = (String) ValidatorUtil.getFieldValue(value, passwordVerificationFieldName);

            if (passwordsAreNotEqual(password, passwordVerification)) {
                ValidatorUtil.addValidationError(passwordFieldName, context);
                ValidatorUtil.addValidationError(passwordVerificationFieldName, context);

                return false;
            }
        }
        catch (Exception ex) {
            throw new RuntimeException("Exception occurred during validation", ex);
        }

        return true;
    }

    private boolean passwordsAreNotEqual(String password, String passwordVerification) {
        return !(password == null ? passwordVerification == null : password.equals(passwordVerification));
    }
}

That is it. We have now implemented our custom validation constraints. Let's find out how we can render the registration page.

Rendering the Registration Page

The requirements of our registration page are following:

  1. The url of the registration page must be '/user/register'.
  2. If the user is creating a "normal" user account, our application must render an empty registration form.
  3. If the user is using social sign in, the information provided by the SaaS API provider must be used to pre-populate the form fields of the registration form.

Let's start by finding out how we can redirect user to the registration page.

Redirecting User to the Registration Page

Before we can start implementing the controller method which renders the registration page, we have to implement a controller which redirects user to the correct url. The requirements of this controller are following:

  • It must process GET requests send to url '/signup'.
  • It must redirect requests to url '/user/register'.
If you are configuring the application context of your application by using XML configuration files, you can skip this step. This step is required only if you configure the application context of your application by using Java configuration. The reason for this is that at the moment you can configure the sign up url only if you are using XML configuration (Search for a property called signUpUrl).

We can implement this controller by following these steps:

  1. Create a SignUpController class and annotate the class with the @Controller annotation.
  2. Add a public redirectRequestToRegistrationPage() method to the created class. The return type of this method is String.
  3. Implement the redirectRequestToRegistrationPage() method by following these steps:
    1. Annotate the method with the @RequestMapping annotation and ensure that the method processes GET requests send to url '/signup'.
    2. Return a String 'redirect:/user/register'. This will redirect the request to url '/user/register'.

The source code of the SignUpController class looks as follows:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class SignUpController {

    @RequestMapping(value = "/signup", method = RequestMethod.GET)
    public String redirectRequestToRegistrationPage() {
        return "redirect:/user/register";
    }
}

Let's move on and find out how we can implement the controller method which renders the registration page.

Implementing the Controller Method

The controller method which renders the registration page has one important responsibility:

It creates the form object and pre-populates its fields. If the user is creating a "normal" user account, this controller method creates an empty form object. On the other hand, if the user is creating a user account by using social sign in, this controller method sets the field values of the form object by using the information provided by the used SaaS API provider.

We can implement the controller method which renders the registration page by following these steps:

  1. Create the controller class and annotate it with the @Controller annotation.
  2. Annotate the class with the @SessionAttributes annotation and set its value to 'user'. We use this annotation to ensure that a model attribute called 'user' (our form object) is stored to the session.
  3. Add a private createRegistrationDTO() method to the class. This method takes a Connection object as a method parameter and returns a RegistrationForm object. We can implement this method by following these steps:
    1. Create a new RegistrationForm object.
    2. If the Connection object given as a method parameter is not null, the user is creating a new user account by using social sign in. If this is the case, we have to
      1. Get a UserProfile object by calling the fetchUserProfile() method of the Connection class. This object contains the user information returned by the SaaS API provider.
      2. Set the email, first name, and the last name to the form object. We can the get this information by calling the methods of the UserProfile class.
      3. Get a ConnectionKey object by calling the getKey() method of the Connection class. This object contains id of the used social sign in provider and a provider specific user id.
      4. Set the sign in provider to the form object by following these steps:
        1. Get the sign in provider by calling the getProviderId() method of the ConnectionKey class.
        2. Transform the String returned by the getProviderId() method to uppercase.
        3. Get the correct value of the SocialMediaService enum by calling its nameOf() method. Pass the sign in provider (in uppercase) as a method parameter (This means that the values of the SocialMediaService enum depends from the sign in provider ids).
        4. Set the returned value to the form object.
    3. Return the form object.
  4. The controller method which renders the registration page is called showRegistrationForm(). Add this method to the controller class and implement it by following these steps:
    1. Annotate the method with the @RequestMapping annotation and ensure that controller method processes GET requests send to url '/user/register'.
    2. Add a WebRequest object as a method parameter. We use the WebRequest as a method parameter because it gives us an easy access to request metadata.
    3. Add a Model object as a method parameter.
    4. Get a Connection object by calling the static getConnection() method of the ProviderSignInUtils class. Pass the WebRequest object as a method parameter. This method returns null if the WebRequest object doesn't contain SaaS API provider metadata (this means that user is creating a normal user account). If the metadata is found, this method creates a Connection object by using that information and returns the created object.
    5. Get the form object by calling the private createRegistrationDTO() method. Pass the Connection object as a method parameter.
    6. Set the form object to model as a model attribute called 'user'.
    7. Return the name of the registration form view ('user/registrationForm').

The relevant part of the RegistrationController class looks as follows:

import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionKey;
import org.springframework.social.connect.UserProfile;
import org.springframework.social.connect.web.ProviderSignInUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.context.request.WebRequest;

@Controller
@SessionAttributes("user")
public class RegistrationController {

    @RequestMapping(value = "/user/register", method = RequestMethod.GET)
    public String showRegistrationForm(WebRequest request, Model model) {
        Connection<?> connection = ProviderSignInUtils.getConnection(request);

        RegistrationForm registration = createRegistrationDTO(connection);
        model.addAttribute("user", registration);

        return "user/registrationForm";
    }

    private RegistrationForm createRegistrationDTO(Connection<?> connection) {
        RegistrationForm dto = new RegistrationForm();

        if (connection != null) {
            UserProfile socialMediaProfile = connection.fetchUserProfile();
            dto.setEmail(socialMediaProfile.getEmail());
            dto.setFirstName(socialMediaProfile.getFirstName());
            dto.setLastName(socialMediaProfile.getLastName());

            ConnectionKey providerKey = connection.getKey();
            dto.setSignInProvider(SocialMediaService.valueOf(providerKey.getProviderId().toUpperCase()));
        }

        return dto;
    }
}

The next thing that we have to do is to create the JSP page. Let’s move on and find out how this is done.

Creating the JSP Page

We can create the JSP page which contains the registration form by following these steps:

  1. Ensure that the registration form is shown only to anonymous users. We can do this by following these steps:
    1. Wrap the login form and social sign in buttons inside the authorize tag of the Spring Security tag library.
    2. Set the value of the access attribute to isAnonymous().
  2. Implement the registration form by following these steps:
    1. Ensure that when the registration form is submitted, a POST request is send to url '/user/register'.
    2. Add a CSRF token to the request. This is required because we enabled the CSRF protection of Spring Security in the first part of this tutorial.
    3. If the sign in provider is found from the form object, add it to the form as a hidden field.
    4. Add a firstName field to the form and ensure that the validation errors concerning the firstName field are shown.
    5. Add a lastName field to the form and ensure that the validation errors concerning the lastName field are shown.
    6. Add an email field to the form and ensure that the validation errors concerning the email field are shown.
    7. If the user is creating a normal user account (the value of the form object's signInProvider field is null), follow these steps:
      1. Add a password field to the form and ensure that the validation errors concerning the password field are shown.
      2. Add a passwordVerification field to the form and ensure that validation errors concerning the passwordVerification field are shown.
    8. Add a submit button to the form
  3. Ensure that a help message is shown if an authenticated user accesses the registration page. We can do this by following these steps:
    1. Wrap the error message area inside the authorize tag of the Spring Security tag library.
    2. Set the value of the access attribute to isAuthenticated().
    3. Get the localized error message by using the message tag of the Spring tag library.
The Spring 3.2 reference manual has more information about the form tags of the Spring JSP tag library.

The source code of the registrationForm.jsp page looks as follows:

<!DOCTYPE html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<html>
<head>
    <title></title>
    <script type="text/javascript" src="${pageContext.request.contextPath}/static/js/app/user.form.js"></script>
</head>
<body>
    <div class="page-header">
        <h1><spring:message code="label.user.registration.page.title"/></h1>
    </div>
	<!--
	    If the user is anonymous (not logged in), show the registration form.
	-->
    <sec:authorize access="isAnonymous()">
        <div class="panel panel-default">
            <div class="panel-body">
				<!-- 
					Ensure that when the form is submitted, a POST request is send to url
					'/user/register'.
				-->
                <form:form action="${pageContext.request.contextPath}/user/register" commandName="user" method="POST" enctype="utf8" role="form">
                    <!-- Add CSRF token to the request. -->
					<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
                    <!--
						If the user is using social sign in, add the signInProvider
						as a hidden field.
					-->
					<c:if test="${user.signInProvider != null}">
                        <form:hidden path="signInProvider"/>
                    </c:if>
                    <div class="row">
                        <div id="form-group-firstName" class="form-group col-lg-4">
                            <label class="control-label" for="user-firstName"><spring:message code="label.user.firstName"/>:</label>
							<!--
								Add the firstName field to the form and ensure 
								that validation errors are shown.
							-->
                            <form:input id="user-firstName" path="firstName" cssClass="form-control"/>
                            <form:errors id="error-firstName" path="firstName" cssClass="help-block"/>
                        </div>
                    </div>
                    <div class="row">
                        <div id="form-group-lastName" class="form-group col-lg-4">
                            <label class="control-label" for="user-lastName"><spring:message code="label.user.lastName"/>:</label>
							<!--
								Add the lastName field to the form and ensure
								that validation errors are shown.
							-->
                            <form:input id="user-lastName" path="lastName" cssClass="form-control"/>
                            <form:errors id="error-lastName" path="lastName" cssClass="help-block"/>
                        </div>
                    </div>
                    <div class="row">
                        <div id="form-group-email" class="form-group col-lg-4">
                            <label class="control-label" for="user-email"><spring:message code="label.user.email"/>:</label>
							<!-- 
								Add the email field to the form and ensure
								that validation errors are shown.
							-->
                            <form:input id="user-email" path="email" cssClass="form-control"/>
                            <form:errors id="error-email" path="email" cssClass="help-block"/>
                        </div>
                    </div>
					<!--
						If the user is creating a normal user account, add password fields
						to the form.
					-->
                    <c:if test="${user.signInProvider == null}">
                        <div class="row">
                            <div id="form-group-password" class="form-group col-lg-4">
                                <label class="control-label" for="user-password"><spring:message code="label.user.password"/>:</label>
								<!--
									Add the password field to the form and ensure 
									that validation errors are shown.
								-->
                                <form:password id="user-password" path="password" cssClass="form-control"/>
                                <form:errors id="error-password" path="password" cssClass="help-block"/>
                            </div>
                        </div>
                        <div class="row">
                            <div id="form-group-passwordVerification" class="form-group col-lg-4">
                                <label class="control-label" for="user-passwordVerification"><spring:message code="label.user.passwordVerification"/>:</label>
								<!-- 
									Add the passwordVerification field to the form and ensure
									that validation errors are shown.
								-->
                                <form:password id="user-passwordVerification" path="passwordVerification" cssClass="form-control"/>
                                <form:errors id="error-passwordVerification" path="passwordVerification" cssClass="help-block"/>
                            </div>
                        </div>
                    </c:if>
					<!-- Add the submit button to the form. -->
                    <button type="submit" class="btn btn-default"><spring:message code="label.user.registration.submit.button"/></button>
                </form:form>
            </div>
        </div>
    </sec:authorize>
	<!--
	    If the user is authenticated, show a help message instead
	    of registration form.
	-->
    <sec:authorize access="isAuthenticated()">
        <p><spring:message code="text.registration.page.authenticated.user.help"/></p>
    </sec:authorize>
</body>
</html>

Let's move on and find out how we can process the submission of the registration form.

Processing the Form Submissions of the Registration Form

Our next step is to process the form submissions of the registration form. We can do this by following these steps:

  1. Validate the information entered to the registration form. If the information is not valid, we render the registration form and show validation error messages to the user.
  2. Ensure that the email address given by the user is unique. If the email address is not unique, we render the registration form and show an error message to the user.
  3. Create a new user account and log in the user.
  4. Redirect the user to the front page.

This process is illustrated in the following diagram:

social-registrationform-submit

Let's start by implementing the controller method which processes the form submissions of the registration form.

Implementing the Controller Method

The controller method which processes the form submissions of the registration form has the following responsibilities:

  • It ensures that the information entered to the registration form is valid.
  • It informs the user if the email address entered to the registration form is found from the database.
  • It passes the form object forward to the service layer.
  • It persists the connection to the UserConnection table if the user is creating a new user account by using social sign in.
  • It logs the user in after a new user account has been created.

We can implement this controller method by making the following changes to the RegistrationController class:

  1. Add a private UserService field to the controller class.
  2. Add a constructor which takes a UserService object as a constructor argument to the RegistrationController class and implement it by following these steps:
    1. Annotate the constructor with the @Autowired annotation. This ensures that the dependencies of this bean are injected by using constructor injection.
    2. Set the value of service field.
  3. Add a private addFieldError() method to the controller class. This method is used to add binding errors to the binding result. The method parameters of this method are described in the following:
    1. The objectName parameter is the name of the form object.
    2. The fieldName parameter is the name of the form field which contains invalid value.
    3. The fieldValue parameter contains the value of the form field.
    4. The errorCode parameter is the error code of the field error.
    5. The result parameter is a BindingResult object.
  4. Implement the addFieldError() method by following these steps:
    1. Create a new FieldError object by using the method parameters.
    2. Add the created FieldError object to the binding result by calling the AddError() method of the BindingResult class.
  5. Add a private createUserAccount() method to the controller class. This method returns the created User object, and takes a RegistrationForm and BindingResult objects as method parameters. If the email address is found from the database, this method returns null. Implement this method by following these steps:
    1. Add a try-catch structure to the method and catch DuplicateEmailException objects.
    2. Implement the try block by calling the registerNewUserAccount() method of the UserService interface. Pass the RegistrationForm object as a method parameter. Return the information of the created user account.
    3. Implement the catch block by calling the private addFieldError() method. Pass the required information as method parameters. This ensures that the user receives an error message which informs him that the email address entered to the registration form is found from the database. Return null.
  6. Add a public registerUserAccount() method to the controller class and implement it by following these steps:
    1. Annotate the method with the @RequestMapping annotation and ensure that the method processes POST request send to url '/user/register'.
    2. Add a RegistrationForm object as a method parameter and annotate it with the following annotations:
      1. Annotate the method parameter with the @Valid annotation. This ensures that the information of this object is validated before the controller method is called.
      2. Annotate the method parameter with the @ModelAttribute annotation and set its value to 'user' (this is the name of the form object).
    3. Add a BindingResult object as a method parameter.
    4. Add a WebRequest object as a method parameter. This object is required because we need to access the metadata of the request after the a new user account has been created.
    5. If the binding result has errors, return the name of the form view.
    6. Call the private createUserAccount() method and pass the RegistrationForm and BindingResult objects as method parameters.
    7. If the User object returned by the createUserAccount() method is null, it means that the email address was found from the database. Return the name of the form view.
    8. Log the created user in by calling the static loginInUser() method of the SecurityUtil class. Pass the created User object as a method parameter.
    9. Call the static handlePostSignUp() method of the ProviderSignInUtils class. Pass the email address of the created user and the WebRequest object as method parameters. If the user created user account by using social sign in, this method persists the connection to the UserConnection table. If the user created a normal user account, this method doesn't do anything.
    10. Redirect the user to the front page of our application by returning a String 'redirect:/'. This will redirect the request to url '/'.

The relevant part of the RegistrationController class looks as follows:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.web.ProviderSignInUtils;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.context.request.WebRequest;

import javax.validation.Valid;

@Controller
@SessionAttributes("user")
public class RegistrationController {

    private UserService service;

    @Autowired
    public RegistrationController(UserService service) {
        this.service = service;
    }

    @RequestMapping(value ="/user/register", method = RequestMethod.POST)
    public String registerUserAccount(@Valid @ModelAttribute("user") RegistrationForm userAccountData,
                                      BindingResult result,
                                      WebRequest request) throws DuplicateEmailException {
        if (result.hasErrors()) {
            return "user/registrationForm";
        }

        User registered = createUserAccount(userAccountData, result);

        if (registered == null) {
            return "user/registrationForm";
        }
        SecurityUtil.logInUser(registered);
        ProviderSignInUtils.handlePostSignUp(registered.getEmail(), request);

        return "redirect:/";
    }

    private User createUserAccount(RegistrationForm userAccountData, BindingResult result) {
        User registered = null;

        try {
            registered = service.registerNewUserAccount(userAccountData);
        }
        catch (DuplicateEmailException ex) {
            addFieldError(
                    "user",
                    "email",
                    userAccountData.getEmail(),
                    "NotExist.user.email",
                    result);
        }

        return registered;
    }

    private void addFieldError(String objectName, String fieldName, String fieldValue,  String errorCode, BindingResult result) {
        FieldError error = new FieldError(
                objectName,
                fieldName,
                fieldValue,
                false,
                new String[]{errorCode},
                new Object[]{},
                errorCode
        );

        result.addError(error);
    }
}

The SecurityUtil class has one static method called loginInUser(). This method takes the information of the created user as a method parameter, and logs the user in programmatically. We can implement this method by following these steps:

  1. Create a new ExampleUserDetails object by using the information of the created user.
  2. Create a new UsernamePasswordAuthenticationToken object and pass the following arguments to its constructor:
    1. The first argument is the principal (aka logged in user). Pass the created ExampleUserDetails object as the first constructor argument.
    2. The second argument contains the credentials of the user. Pass null as the second constructor argument.
    3. The third argument contains the the authorities of the user. We can get the authorities by calling the getAuthorities() method of the ExampleUserDetails class.
  3. Set created Authentication object into security context by following these steps:
    1. Get the SecurityContext object by calling the static getContext() method of the SecurityContextHolder class.
    2. Call the static setAuthentication() method of the SecurityContext class and pass the created UsernamePasswordAuthenticationToken object as a method parameter.

The source code of the SecurityUtil class looks as follows:

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

public class SecurityUtil {

    public static void logInUser(User user) {
        ExampleUserDetails userDetails = ExampleUserDetails.getBuilder()
                .firstName(user.getFirstName())
                .id(user.getId())
                .lastName(user.getLastName())
                .password(user.getPassword())
                .role(user.getRole())
                .socialSignInProvider(user.getSignInProvider())
                .username(user.getEmail())
                .build();

        Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }
}
It is not a good idea to log in a user who has created a normal user account. Typically you want to send a confirmation email which is used to verify his email address. However, the example application works this way because it simplifies the registration process.

Let's move on and find out how we can create the domain model of our example application.

Creating the Domain Model

The domain model of our application consists of two classes and two enums which are described in the following:

  • The BaseEntity class is a superclass of all entity classes of our application.
  • The User class is the only entity class of our application. It contains the information of a single user.
  • The Role enum specifies the user roles of our application.
  • The SocialMediaService enum specifies the SaaS API providers which are supported by our example application.
Our example application doesn't really need a separate base class for entities because it has only one entity. However, I decided to add it anyway because this is often a good idea in real life applications.

Let's move on and find out how we can create the domain model.

First, we have to create a BaseEntity class. It contains the fields which are shared by all entity classes and two callback methods which are used to store values to some of those fields. We can implement this class by following these steps:

  1. Create an abstract BaseEntity class which has one type parameter called ID. This parameter is the type of the entity's private key.
  2. Annotate the class with the @MapperSuperclass annotation. This means that the mapping information of the BaseEntity class is applied to its subclasses.
  3. Add a DateTime field called creationTime to the class and configure it by following these steps:
    1. Annotate the field with the @Column annotation and configure the name of the database column. The value of the nullable attribute to false.
    2. Annotate the field with the @Type annotation and set the value of the type attribute to 'org.jadira.usertype.dateandtime.joda.PersistentDateTime' (Javadoc here). This marks the field as a custom type and configures the type class which makes it possible to persist DateTime objects with Hibernate.
  4. Add a DateTime field called modificationTime to the class and configure it by using these steps:
    1. Annotate the field with the @Column annotation and set the name of the database column. Ensure that this column is not nullable.
    2. Annotate the field with the @Type annotation and set the value of the type attribute to 'org.jadira.usertype.dateandtime.joda.PersistentDateTime' (check step 3 for more details about this).
  5. Add a long field called version to the class and annotate the field with the @Version annotation. This enables optimistic locking and states the value of the version field serves as optimistic lock value.
  6. Add an abstract getId() method to the class. This method returns the id of the actual entity.
  7. Add a public prePersist() method to the class and annotate the method with the @PrePersist annotation. This method is called before the entity manager persists the object, and it sets the current time as the value of the creationTime and the modificationTime fields.
  8. Add a public preUpdate() method to the class and annotate the method with the @PreUpdate annotation. This method is called before the database UPDATE operation is performed. The implementation of this method sets the current time as the value of the modificationTime field.

The source code of the BaseEntity class looks as follows:

import org.hibernate.annotations.Type;
import org.joda.time.DateTime;

import javax.persistence.*;

@MappedSuperclass
public abstract class BaseEntity<ID> {

    @Column(name = "creation_time", nullable = false)
    @Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
    private DateTime creationTime;

    @Column(name = "modification_time", nullable = false)
    @Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
    private DateTime modificationTime;

    @Version
    private long version;

    public abstract ID getId();

	//Other getters are omitted for the sake of clarity.

    @PrePersist
    public void prePersist() {
        DateTime now = DateTime.now();
        this.creationTime = now;
        this.modificationTime = now;
    }

    @PreUpdate
    public void preUpdate() {
        this.modificationTime = DateTime.now();
    }
}

Second, we have to create the User class. We can create this class following these steps:

  1. Create a User class which extends the BaseEntity class and give the type of its private key (Long) as a type parameter.
  2. Annotate the created class with the @Entity annotation.
  3. Annotate the created class with the @Table annotation and ensure that the user information is stored to a database table called 'user_accounts'.
  4. Add a private id field to the class and set its type to Long. Configure the field by following these steps:
    1. Annotate the field with the @Id annotation. This annotation is used to specify the primary key of the entity.
    2. Annotate the field with the @GeneratedValue annotation and set the value of the strategy attribute to GenerationType.AUTO. This means that the persistence provider will pick the appropriate key generation strategy for the used database.
  5. Add a private email field to the class and set its type to String. Annotate the field with the @Column annotation and configure the field by following these rules:
    1. The email address is stored to the 'email' column of the 'users' table.
    2. The maximum length of the email address is 100 characters.
    3. The email address cannot be null.
    4. The email address must be unique.
  6. Add a private firstName field to the class and set its type to String. Annotate the field with the @Column annotation and configure the field by following these rules:
    1. The first name is stored to the 'first_name' column of the 'users' table.
    2. The maximum length of the first name is 100 characters.
    3. The first name cannot be null.
  7. Add a private lastName field to the class and set its to type to String. Annotate the field with the @Column annotation and and configure the field by following these rules:
    1. The last name is stored to the 'last_name' column of the 'users' table.
    2. The maximum length of the last name is 100 characters.
    3. The last name cannot be null.
  8. Add a private password field to the class and set its type to String. Annotate the field with the @Column annotation and configure the field by following these rules:
    1. The password is stored to the 'password' column of the 'users' table.
    2. The maximum length of the password is 255 characters.
  9. Add a private role field to the class and set its type to Role. Annotate the field with the @Enumerated annotation and set its value to EnumType.STRING. This means the value of this field is persisted as enumerated type and that a String value is stored to the database. Annotate the field with the @Column annotation and configure the field by following these rules:
    1. The role is stored to the 'role' column of the 'users' table.
    2. The maximum length of the role is 20 characters.
    3. The role cannot be null.
  10. Add a private signInProvider field to the class and set its type to SocialMediaService. Annotate the field with the @Enumerated annotation and set its value to EnumType.STRING (check step 9 for more details about this). Annotate the field with the @Column annotation and configure the field by following these rules:
    1. The sign in provider is stored to the 'sign_in_provider' field of the 'users' table.
    2. The maximum length of the sign in provider is 20 characters.
  11. Add a public static inner class called Builder to the User class. Implement this class by following these steps:
    1. Add a User field to the class. This field holds a reference to the constructed User object.
    2. Add a constructor to the class. This constructor creates a new User object and sets the role of the created user to Role.ROLE_USER.
    3. Add methods used to set the field values of created User object to the builder class. Each method sets the value given as a method parameter to the correct field and returns a reference to User.Builder object.
    4. Add a build() method to the builder class. This method returns the created User object.
  12. Add a public static getBuilder() method to the User class. This method returns a new User.Builder object.
You can get more information about the builder pattern by reading a blog post called The builder pattern in practice.

The source code of the User class looks as follows:

import javax.persistence.*;

@Entity
@Table(name = "user_accounts")
public class User extends BaseEntity<Long> {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "email", length = 100, nullable = false, unique = true)
    private String email;

    @Column(name = "first_name", length = 100,nullable = false)
    private String firstName;

    @Column(name = "last_name", length = 100, nullable = false)
    private String lastName;

    @Column(name = "password", length = 255)
    private String password;

    @Enumerated(EnumType.STRING)
    @Column(name = "role", length = 20, nullable = false)
    private Role role;

    @Enumerated(EnumType.STRING)
    @Column(name = "sign_in_provider", length = 20)
    private SocialMediaService signInProvider;

	//The constructor and getters are omitted for the sake of clarity

    public static Builder getBuilder() {
        return new Builder();
    }

    public static class Builder {

        private User user;

        public Builder() {
            user = new User();
            user.role = Role.ROLE_USER;
        }

        public Builder email(String email) {
            user.email = email;
            return this;
        }

        public Builder firstName(String firstName) {
            user.firstName = firstName;
            return this;
        }

        public Builder lastName(String lastName) {
            user.lastName = lastName;
            return this;
        }

        public Builder password(String password) {
            user.password = password;
            return this;
        }

        public Builder signInProvider(SocialMediaService signInProvider) {
            user.signInProvider = signInProvider;
            return this;
        }

        public User build() {
            return user;
        }
    }
}

The Role is an enum which specifies the user roles of our application. Its source code looks as follows:

public enum Role {
    ROLE_USER
}

The SocialMediaService is an enum which identifies the SaaS API provider which was used to authenticate the user. Its source code looks as follows:

public enum SocialMediaService {
    FACEBOOK,
    TWITTER
}

Next we will find out how we can implement the service class which creates new user accounts and persists them to the database.

Creating the Service Class

First, we have to create an interface which declares the method used to add new user accounts to the database. This method is described in the following:

The registerNewUserAccount() method takes a RegistrationForm object as method parameter and returns a User object. If the email address stored to the email field of the RegistrationForm object is found from the database, this method throws a DuplicateEmailException.

The source code of the UserService interface looks as follows:

public interface UserService {

    public User registerNewUserAccount(RegistrationForm userAccountData) throws DuplicateEmailException;
}

Second, we have to implement the UserService interface. We can do it by following these steps:

  1. Create a class which implements the UserService interface and annotate this class with the @Service annotation.
  2. Add a PasswordEncoder field to the created class.
  3. Add a UserRepository field to to created class.
  4. Add a constructor which takes PasswordEncoder and UserRepository objects as constructor arguments to the service class. Implement the constructor by following these steps:
    1. Annotate the constructor with the @Autowired annotation. This ensures that the dependencies of this bean are injected by using constructor injection.
    2. Set the values of passwordEncoder and repository fields.
  5. Add a private emailExist() method to the service class. This method takes a email address as a method argument and returns a boolean. Implement this method by following these steps:
    1. Get the user whose email address is equal to the email address given as a method parameter by calling the findByEmail() method of the UserRepository interface. Pass the email address as a method parameter.
    2. If a user is found, return true.
    3. If a user is not found, return false.
  6. Add a private encodePassword() method to service class. This method takes a RegistrationForm object as a method parameter and returns the encoded password. Implement this method by following these steps:
    1. Find out if the user is creating a normal user account. We can get this information by calling the isNormalRegistration() method of the RegistrationForm class. If this method returns true, obtain the encoded password by calling the encode() method of the PasswordEncoder class. Pass the cleartext password as a method parameter. Return the encoded password.
    2. If the user is creating a user account by using social sign in, return null.
  7. Add a registerNewUserAccount() method to the service class and implement it by following these steps:
    1. Annotate the method with the @Transactional annotation. This means that the method is executed "inside" a read-write transaction.
    2. Find out if the email address is found from the database. We can do this by calling the private emailExist() method. Pass the RegistrationForm object as a method parameter. If this method returns true, throw a new DuplicateEmailException.
    3. Obtain the encoded password by calling the private encodePassword() method. Pass the RegistrationForm object as a method parameter.
    4. Get the builder object by calling the getBuilder() method of the User class and set the following information to the created User object:
      • Email address
      • First name
      • Last name
      • Password
    5. Find out if the user is creating a new user account by using social sign in. We can do this by calling the method of the egistrationForm class. If this method returns true, set the used social sign in provider by calling the signInProvider() method of the User.Builder class. Pass the used sign in provider as a method parameter.
    6. Create the User object.
    7. Persist the User object to the database by calling the save() method of the UserRepository interface. Pass the created User object as a method parameter.
    8. Return the persisted object.

The source code of the RepositoryUserService class looks as follows:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class RepositoryUserService implements UserService {

    private PasswordEncoder passwordEncoder;

    private UserRepository repository;

    @Autowired
    public RepositoryUserService(PasswordEncoder passwordEncoder, UserRepository repository) {
        this.passwordEncoder = passwordEncoder;
        this.repository = repository;
    }

    @Transactional
    @Override
    public User registerNewUserAccount(RegistrationForm userAccountData) throws DuplicateEmailException {
        if (emailExist(userAccountData.getEmail())) {
            throw new DuplicateEmailException("The email address: " + userAccountData.getEmail() + " is already in use.");
        }

        String encodedPassword = encodePassword(userAccountData);

        User.Builder user = User.getBuilder()
                .email(userAccountData.getEmail())
                .firstName(userAccountData.getFirstName())
                .lastName(userAccountData.getLastName())
                .password(encodedPassword);

        if (userAccountData.isSocialSignIn()) {
            user.signInProvider(userAccountData.getSignInProvider());
        }

        User registered = user.build();

        return repository.save(registered);
    }

    private boolean emailExist(String email) {
        User user = repository.findByEmail(email);

        if (user != null) {
            return true;
        }

        return false;
    }

    private String encodePassword(RegistrationForm dto) {
        String encodedPassword = null;

        if (dto.isNormalRegistration()) {
            encodedPassword = passwordEncoder.encode(dto.getPassword());
        }

        return encodedPassword;
    }
}

We still have to create the Spring Data JPA repository for our example application. Let's find out how we can do this.

Creating the Spring Data JPA Repository

Our last step is to create a Spring Data JPA repository which is used to

  • Persist new User objects to the database.
  • Find a User object from the database by using email address as a search criteria.

We can create a Spring Data JPA repository which fulfils these requirements by following these steps:

  1. Create the repository interface and extend the JpaRepository interface. Give the type of the entity (User) and type of its private key (Long) as type parameters. This gives us access to the methods declared by the JpaRepository interface. One of those methods is the save() method which is used to persist User objects to the database.
  2. Add a findByEmail() method to the created repository interface. This method takes an email address as a method parameter and returns a User object whose email is equal to the email address given as a method parameter. If no user is found, this method returns null.
If you want to get more information about Spring Data JPA, you can take a look at my Spring Data JPA tutorial.

The source code of the UserRepository interface looks as follows:

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {

    public User findByEmail(String email);
}

That was it! Let's move on and spend a moment to summarize what we have achieved during this blog post.

The Summary

We have now implemented the requirements of our example application. This means that

  • We have created a registration function which supports both "normal" user accounts and user accounts created by using social sign.
  • The users of our application can log in by using username and password.
  • The users of our application can log in by using social sign in.

Let's refresh our memories and take a look at the registration process. This process is illustrated in the following figure:

social-signin-flow

This blog post has taught us the following things:

  • We learned how we can start the social sign in flow.
  • We learned how we can pre-populate the field of our registration form by using the information provided by the SaaS API provider.
  • We learned how we can create custom validation constraints which ensures that information entered to the registration form is valid.

The next part of this tutorial describes how we can write unit tests for the web layer of our application.

P.S. The example application of this blog post is available at Github.

If you want to learn how to use Spring Social, you should read my Spring Social tutorial.
293 comments… add one
  • Sri Oct 27, 2013 @ 7:37

    I am facing this problem when trying to run on server tomcat.

    Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.social.connect.UsersConnectionRepository] is defined
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean (DefaultListableBeanFactory.java:295)
    at org.springframework.context.support.AbstractApplicationContext.getBean (AbstractApplicationContext.java:1125)
    at org.springframework.social.security.SpringSocialConfigurer.getDependency (SpringSocialConfigurer.java:87)

    • Petri Oct 27, 2013 @ 10:52

      Thanks for point this problem out!

      I think that might be related to the changes which were made to configuration of Spring Social 1.1.0. I updated the Java configuration to use the new configuration scheme but it seems that it is not working on Tomcat.

      I will investigate this problem.

      • Sri Oct 27, 2013 @ 18:07

        I tried the example with Jetty and it worked flawlessly, if you can suggest me how to make it work with tomcat that would be awsome. I have been following your blogs since one year and you are splendid with spring.

  • Sri Oct 30, 2013 @ 6:39

    Petri another java guru suggested to remove @Profile("application") in the class SocialContext.java. That has fixed the problem in Apache Tomcat.

    • Petri Oct 30, 2013 @ 9:18

      Thanks for coming back and leaving this comment! It seems that the active Spring profile was not configured when Tomcat was started.

      The reason why I specified the profile was that I don't want to use Facebook and Twitter for authentication when I write integration tests for the web layer (this is discussed in the fourth part of the tutorial).

      It is also possible to configure the Tomcat to use the correct Spring profile. This blog post describes how you can use Spring profiles with Tomcat.

      I will add these instructions to the README of my example application. Again, thanks for pointing this out!

  • Alex Nov 1, 2013 @ 3:06

    Hi Petri,
    Your post is so detail, I like it very much. I've a question, do you know where can I find the doc for SpringSocialConfigurer ? Thanks

    • Petri Nov 1, 2013 @ 9:34

      Hi Alex,

      It is good to hear that you like my blog post. Unfortunately I haven't been able to find the Javadoc of the SpringSocialConfigurer class (or any other documentation about it). Actually, I read its source code when I was trying to figure out what it does. Do you have some questions concerning that class?

  • Alex Nov 1, 2013 @ 9:59

    Hi Petri,
    Thanks for your prompt reply, actually, I want to know its APIs, I wonder where to configure the URL for "/auth/facebook" and "/auth/twitter" or it's fixed ?

    Best Regards,
    Alex

  • sai Nov 23, 2013 @ 0:03

    i am also getting below error...
    Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.social.connect.UsersConnectionRepository] is defined

    Is it possible to do it normal way...with out creating spring profile in tomcat. never did that..

    sai

  • sai Nov 23, 2013 @ 0:24

    Hi Petri,

    I have downloaded codes. Deployed codes, but getting above error.
    Also, how to use XML configuration without java?

    I see config files, but no web.xml.

    please help.
    sai

    • Petri Nov 23, 2013 @ 1:18

      Are you referring to the error which occurred because the active Spring profile wasn't specified? If so, check out my answer to that comment.

      As you probably already noticed, this example application configures the web application by using Java configuration. If you are using a Servlet 3.0 compliant container such as Tomcat 7, there is no need to create the web.xml
      file.

      If you don't want to use the Java configuration, you have to create the web.xml file. Remember to

      • Load the context configuration
      • Configure the dispatcher servlet
      • Configure the required servlet filters
      • Configure the context loader listener

      Here are some links to useful resources:

  • sai Nov 23, 2013 @ 21:58

    Hi Petri,

    I am just new for java config thingy. need your help. Do you have any steps that i can integrate into current files?

    How do i add below information in spring social. Do i need to modify ExampleApplicationConfig file? or what are files that i need to modify to get pass over current error or spring profiles to work.

    spring.profiles.active
    ${spring.profiles.active}

    spring.profiles.active=${spring.profiles.active}

    • Petri Nov 23, 2013 @ 23:41

      Hi Sai,

      there are two solution to this problem:

      1. Remove the @Profile annotation from the SocialContext class. You don't need it if you don't want to write integration tests for the registration function.
      2. You have to set the active Spring profile when the servlet container is started. If you use Tomcat 7, you can do this by adding this line: spring.profiles.active=dev to the conf/catalina.properties file.
  • sai Nov 26, 2013 @ 5:59

    Hi Petri,

    removed @profile. it works fine...

    thank you,
    Sridhar

    • Petri Nov 26, 2013 @ 9:36

      Great! The only reason why I use Spring profile in this example is that I want to investigate how I could write integration tests to this application (and especially to the registration function). To be more specific, I want to write integration tests which are not using the actual Facebook or Twitter API. I can achieve this goal by using a different Spring Social configuration when I run my integration tests.

  • sai Nov 26, 2013 @ 6:48

    Sorry to bug you.. so many times..
    now i am able to see home page but no buttons are displayed.i think site mesh is displaying body..

  • sai Nov 26, 2013 @ 7:19

    please ignore my comments. application is working fine. great tutorial.

    sai

    • Petri Nov 26, 2013 @ 9:36

      I am happy to hear that you could solve your problems!

  • Sai Nov 26, 2013 @ 19:45

    Hi Pete,

    Once, user logs in through facebook(social), if user exists based on email address, is it possible to authenticate him with spring security?

    sai

  • Sai Nov 27, 2013 @ 4:20

    how do we handle, where after social sign in, where use account exists. i want to authorize user, instead of again prompting for user id and password. Any pointers?

    • Petri Nov 27, 2013 @ 21:46

      The approach has two problems:

      First, if a user has registered a user account by using a normal registration, his information is not found from the UserConnection table. That is why the registration form is rendered when this user attempts to sign in by using social sign in.

      You could of course try to add custom behavior to the sign in flow and use the email address obtained from the SaaS API provider to identify the user. This brings us to the second problem.

      Second, you don't necessarily get the email address of the user from all providers.

      You can get it from Facebook if your application has been granted the email permission. On the other hand, you cannot get private information such as email address from Twitter.

      In other words, if your application has to support SaaS API provider which doesn't return the email address of the user, there is no clean way to this.

      Unfortunately I am not sure what could be the "hacky" way to do this.

  • Tanuja Dec 20, 2013 @ 8:04

    I downloaded the code from git . But when I do mvn clean install it gives the following error

    Caused by: java.io.FileNotFoundException:
    spring-social-examples/sign-in/spring-mvc-normal/profiles/dev/socialConfig.properties

    Please help me I am stuck

    • Petri Dec 20, 2013 @ 9:19

      You have to create that file yourself and follow these steps:

      1. Add the Facebook application id and application secret to the properties file.
      2. Add the Twitter consumer key and consumer secret to the properties file.

      This is also described in the README of the example application.

      I hope that this answered to your question.

  • suv Dec 26, 2013 @ 14:02

    Hi,

    I'm trying to login through facebook and twitter. Though following this I could connect to FB, I'm getting the following error while trying the same for Twitter. Can you help me with what might went wrong?

    HTTP ERROR 500

    Problem accessing /auth/twitter. Reason:

    Server Error
    Caused by:

    org.springframework.web.client.HttpClientErrorException: 406 Not Acceptable
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:88)
    at org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:537)

    ...
    ...

    • Petri Dec 27, 2013 @ 10:39

      According to this page, the API should return a more descriptive error message in the response body. Can you see anything interesting in the response body (you can see this by setting your log level to DEBUG)?

      Unfortunately the original error message is so vague that it is hard to say what is going without seeing any details, but I assume that the request is somehow malformed or some required information is missing.

      You could also ensure that the configuration of your Twitter application is correct. To be more specific, you have to:

      • Ensure that the callback url is correct.
      • Verify that you have selected the 'Allow this application to be used to Sign in with Twitter' checkbox

      That is pretty much everything I can figure out at the moment. I can investigate this issue further if you can provide me more information about the problem.

      • Jagger Jan 13, 2015 @ 17:04

        I have had exactly the same problem. Solved it exactly by providing callback URL.

        By the way. Thanks for the great tutorial!!! The Spring Social examples are too simple and are showing just how to get the data like feeds from Facebook. The user data are then saved globally. Your example showed me how I can write an auth mechanism for multiple users using social accounts!

        • Petri Jan 13, 2015 @ 17:34

          Thank you for verifying that setting the correct callback URL solves this problem! Also, it was nice to hear that my Spring Social tutorial was useful to you.

  • Liu Jan 10, 2014 @ 4:41

    Hi, Petri,
    You do a great work .
    I am a beginner to the Spring Social. I got problems after I download and build you project.
    1. I can register a user account, and database has the information of the account. But it display '??' when I use UTF-8 characters in the field of lastname/firstname. I am using MySQL 5.1.65 and I created the database with utf8 option.
    2. I can NOT login to the system when I use the account I registered above. It just tell me: Login failed!

    • Petri Jan 10, 2014 @ 19:47

      Hi Liu,

      Does the email address or password of the user contain UTF-8 characters?

      • Liu Jan 11, 2014 @ 8:52

        There are NOT UTF-8 characters in the email address or password.
        I add the following code in login.jsp:

        Reason:

        Then the error shows: No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken

      • Liu Jan 11, 2014 @ 14:44

        Hi Petri,

        I solved the problem.

        1. I used configure(AuthenticationManagerBuilder auth) method instead of registerAuthentication(AuthenticationManagerBuilder auth) in the WebSecurityConfigurerAdapter class.

        2. Extra property was added to the dataSource in the PersistenceContext class:

        
        Properties prop = new Properties();
        prop.put("useUnicode", "true");
        prop.put("characterEncoding", "UTF-8");
        ......
        dataSource.setDriverProperties(prop);
        

        Now, I can login and get correct UTF-8 characters in the display pages.

        • Petri Jan 11, 2014 @ 15:09

          Hi,

          I assume that you used Spring Security 3.2.0.RELEASE instead of 3.2.0.RC1? It seems that the changes made between these two versions break the example application. I will update the example application (and blog post) by following your instructions.

          Thanks for pointing these problems out!

          • Liu Jan 12, 2014 @ 16:55

            Yeah, I forgot that. I really used Spring Security 3.2.0.RELEASE instead of 3.2.0.RC1.

          • Petri Jan 12, 2014 @ 17:11

            Yes, I figured that out when I noticed that the name of the method which is used to register authentication manager was changed from registerAuthentication() to configure() in Spring Security 3.2.0.RELEASE.

            I updated the example application and my blog post yesterday.

            Again, thanks for pointing this problem out!

  • Son Jan 12, 2014 @ 5:47

    Hi Petri,

    Thanks for the great tutorial, I wonder how to fix the error as below? I already registered apps on Facebook & Twitter and I also created the socialprofile.properties file with necessary security credentials on those 2 social platforms. I run the app on my local machine & compiled it with Java 1.6.

    Thanks

    HTTP ERROR 500

    Problem accessing /auth/twitter. Reason:

    Server Error
    Caused by:

    org.springframework.web.client.HttpClientErrorException: 406 Not Acceptable
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:88)
    at org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:537)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:493)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:465)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:416)
    at org.springframework.social.oauth1.OAuth1Template.exchangeForToken(OAuth1Template.java:187)
    at org.springframework.social.oauth1.OAuth1Template.fetchRequestToken(OAuth1Template.java:115)
    at org.springframework.social.security.provider.OAuth1AuthenticationService.getAuthToken(OAuth1AuthenticationService.java:91)
    at org.springframework.social.security.SocialAuthenticationFilter.attemptAuthService(SocialAuthenticationFilter.java:228)
    at org.springframework.social.security.SocialAuthenticationFilter.attemptAuthentication(SocialAuthenticationFilter.java:147)

    Update: I removed the unrelevant parts from the stacktrace. - Petri

    • Petri Jan 12, 2014 @ 10:29

      Hi,

      I think that this question has been asked before. Did you check out my answer to that question?

      I just tested the following use cases:

      • Register a new user account by using Twitter.
      • Login by using Twitter.

      Both of these use cases are working. Have you compared the example application of this blog post with your application?

  • Ego Ran Jan 16, 2014 @ 17:10

    Thanks for the perfect post, Petri, works like a charm!

    • Petri Jan 16, 2014 @ 17:31

      You are welcome!

  • Sambhav Jan 24, 2014 @ 10:04

    Hi Petri
    Excellent post !!!
    I really appreciate all your posts. To the point, precise and very well explained.

    I am trying to solve exactly same use case using Spring Oauth2 and Google as the Authentication provider.

    I am unable to figure out how to get started with it.
    What filters to add, how to customize the UserDetailsService, how to create the Oauth2Token, how to extract user details from the response from Google authentication server....

    Stackoverflow question :
    http://stackoverflow.com/questions/20664846/user-registration-login-sso-using-spring-security-oauth-2-0

    I did implemented the same use case using Spring OpenId.
    The Spring security namespace has tag that takes care of the configuration.

    I am trying to figure out its equivalent tag.

    Thanks for your help.

    • Petri Jan 27, 2014 @ 20:40

      Hi,

      Unfortunately I haven't used Spring Security OAuth so I am not sure what your problem is. However, I did found two websites which might be useful to you:

      I hope that this helps (at least a bit).

      • Venkat Oct 15, 2014 @ 15:16

        Hi mr.petri .
        i have the problem with our application connects to google+ and sign in there and it will be redirected to out application. (in one sentence i want to login to my application by using sign in with google(or any social website(facebook) account if it is exit otherwise it must be registered and login and it will be redirected to our application).

        i think so you are the expert in this type of applications creation.
        so i hope you can do this.
        help me
        thanq

        • Petri Oct 15, 2014 @ 22:17

          If you want that your application creates a user account if it is not already found from the database and the social sign in successful, you should follow these steps:

          1. Create a controller method that extracts the user information from the Connection object and ensure that this controller method is invoked if the user account is found. If you are using XML configuration, you can configure the sign up url. Otherwise you have to configure your controller method to process GET requests send to url '/signup'.
          2. After you have extracted the user information from the Connection object, you have to create a new user account and save it to the database.
          3. After you have saved the created user account to the database, you have to persist the connection to the UserConnection database table. You can do this by using the ProviderSignInUtils class.
          4. Create a new SocialUserDetails object and log the user in.

          If you have any further questions about this, don't hesitate to ask them.

  • JSSAggie Feb 4, 2014 @ 6:17

    Petri,

    I want to thank you for your hard work and very detailed posts. They have helped this 13 year old java veteran jump in and adopt spring as the framework of choice for my first startup.

    I have followed this post and got everything working in tomcat. I made a few changes and additions. One of the changes I made was to add a remember-me process. I did this by adding the bean PersistentTokenBasedRememberMeServices using JdbcTokenRepositoryImpl and the UserDetailsService that we built in your post. Everything seams like it is working great. I login and check the remember-me checkbox and it puts an entry into the persistent_logins table. I log out and it removes the entry from the persistent_logins table. This is great....Except

    When I stop my server and force a cookie login (autologin) I get logged in but when I logout and then try to now log back in with the remember-me option again I get a lock on the table. There are a few other use cases that cause the lock but all of them happen once an atuologin is done.

    java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction

    It seams like the autologin or logout is updating the persistent_logins table and not committing its work. Have you seen this before? How would you go about diagnosing/fixing this issue?

    • Petri Feb 4, 2014 @ 22:01

      Hi,

      I haven't seen this before but I could try reproduce this when I have got time to do it (probably during the weekend). Can I reproduce this issue by following these steps:

      1. Login and select 'remember me'
      2. Restart server
      3. Use cookie login

      By the way, have you configured the logout function to remove the remember me cookie? If you haven't done this, it can cause some problems but to be honest, I am not sure if this is one of them.

      • JSSAggie Feb 5, 2014 @ 6:54

        Petri,

        Thanks for your reply. I looked into the removal of the cookie and found that this is done by default in AbstractRememberMeServices.cancelCookie(). I added a breakpoint and it is hitting this code when I log out. I don't believe this is an issue of the cookie not being cleared.

        I did a little more research while I was trying to track down the exact use-case and I have narrowed it down to the JdbcTokenRepositoryImpl.removeUserTokens() method. This calls the SQL that removes the token from the persistent_logins table.

        It appears that once I "use a cookie login" and try to logout the record is not deleted and the row is locked. I can try to edit the record using mysqlworkbench and it will not let me commit the change until I kill the server and release the record.

        Steps:
        1. Login and select ‘remember me’ - Verify the record in the persistent_logins table.
        2. Restart server
        3. Use cookie login - Verified by adding a breakpoint to RepositoryUserDetailsService.loadUserByUsername(String) and see that we log the user in.
        4. Log out - This should delete the record from the persistent_logins table but it does not go away and there is a lock on that row/table.
        5. Any other action on that table by code or using SQL fails to work until you kill your server and release the lock.

        If I do not take the route of "Use cookie login" and login and logout the record deletes from the persistent_logins table and there are not table locks.

        FYI, I am using the following method in the view for logout. I am sure there is a better way but that is what I am doing for now. I thought I would include in case this could be the issue.

        Thanks again...

        -Jeff

        • Petri Feb 5, 2014 @ 19:06

          Hi Jeff,

          Thank you for providing additional details about your problem. I will try to reproduce the problem next weekend and hopefully find a fix to it (I will also update the dependencies of the example application).

        • Petri Feb 10, 2014 @ 21:22

          Hi Jeff,

          I wasn't able to reproduce your problem. I made the following the changes to the example application:

          1. Updated Spring Framework to version 4.0.0
          2. Updated Apache HttpClient to version 4.3.2
          3. Configured the remember me function by using Java configuration.

          You can get my version of the example application from Github (I created a new branch for it).

          It would be interesting to know if I missed something.

          • JSSAggie Feb 11, 2014 @ 6:15

            Petri,

            Again I thank you for even looking into this. I looked at your branch and compared it to the master and it does not look like much changed in order to add the remember me feature. I made sure that I was following your syntax exactly just to make sure I did not miss something. I also looked back at you previous commits on the master branch and I also updated the spring and httpclient versions.

            The bad news is that I am still having the issue. I did notice that the issue does not happen right away like it did before using the use-case that I explained above. I can repeat it by following steps 1-4 but the error does not happen on the first time i login but if I logout and then try to login again it does happen.

            Steps:
            1. Login and select ‘remember me’ – Verify the record in the persistent_logins table.
            2. Restart server
            3. Use cookie login – Verified by adding a breakpoint to RepositoryUserDetailsService.loadUserByUsername(String) and see that we log the user in.
            4. Log out – This now does delete the record from the database.
            5. Log in - This adds a token back to the database.
            6. Log out - This should delete the record and does not.
            7. Log in - This causes the browser to spin and then ends with a database lock error.

            Its is like the issue is still there but not as prevalent. I also want to note that if I never exercise a cookie login I can log out and then over and over with no issues.

            I am going to table this issue for a week or so but I will build a new project taking away any intellectual property and see if I can reproduce it. If I can I will share that GIT project with you. I will let you know.

            Thanks again...

          • Petri Feb 11, 2014 @ 10:37

            Jeff,

            I will try to reproduce this issue today by following your new instructions.

          • Petri Feb 11, 2014 @ 20:00

            Jeff,

            for some reason I cannot reproduce this problem by following the steps you gave in your last comment. What database are you using? I am using MySQL 5.6.10.

  • Suvajit Feb 5, 2014 @ 13:57

    Hi Petri,

    I took a few pointers from this tutorial. I have used most of the things ditto, but I have replaced JPA with JCR JackRabbit as I need to store the data in a filesystem (local) and not in a DB. Everything has been fine-tuned except the Social Context part, where you have used a dataSource in JdbcUsersConnectionRepository. I need to find an alternative to that approach as in my case data is stored in a local filesystem. Can you please help me with that?
    Thanks in advance.

  • Pracede Feb 18, 2014 @ 0:05

    I've just clone your project from Github but i am getting the following error in eclipse :Plugin execution not covered by lifecycle configuration: org.jacoco:jacoco-maven-plugin:0.6.3.201306030806:prepare-agent (execution: pre-unit-test, phase: initialize). I don't know what to do ?

    • Petri Feb 19, 2014 @ 20:04

      This is an Eclipse specific problem (See this M2E wiki page for more details).

      If you cannot solve the problem by following the advice given in M2E wiki, you can always delete the JaCoCo Maven plugin. It should solve your problem, but if you do this, you cannot create test coverage reports.

  • Shahar Feb 26, 2014 @ 12:59

    Hi,

    I would like to add a social login option without being redirected to the registration form.
    What should I do?

    Best,
    Shahar

    • Petri Feb 27, 2014 @ 19:24

      I have never done this so I am not sure what is the best way to do this. The first thing that comes to my mind is to follow these steps:

      1. Create a service method which saves a new user account to the database.
      2. Modify the SignUpController class to extract the required information from a Connection object (You can get a reference to a Connection object by calling the static getConnection() method of the ProviderSignInUtils class).
      3. Call the service method which you created in step 1.
      4. Log the created user in (You can use the static logInUser() method of the SecurityUtil class for this purpose).
      5. Persist the connection to the UserConnection database table by calling the static handlePostSignUp() method of the ProviderSignInUtils class.
      6. Redirect the request to the preferred url.

      One possible problem of this approach is that you cannot get the email address from all social sign in providers. In other words, you cannot use the email address as username if you have to support a social sign in provider which doesn't return the email address of the user.

      I hope that this answered to your question.

  • Cassio Apr 7, 2014 @ 22:58

    Hi Petrik,

    Congrats you made an awesome job, really cool! The apps architecture is very interesting, union of many designs patterns in one app.

    Maybe you can help me with one question, I need to create a web directory free from SpringSecurity auth, I tried to modify the SecurityContext but no success, the files I need are in .html and maybe It's not possible to work with sitemesh, or Spring MVC. Can you give me a light how I can do it?

    I have poor experience with SpringMVC, and then if I choose JSF framework instead, do you think it's possible integrate JSF works with SpringSecurity, SpringData and SpringSocials?

    Regards
    Cassio

    • Petri Apr 7, 2014 @ 23:51

      Hi Cassio,

      I am happy to hear that this blog post and the example application were useful to you.

      Let's assume that the directory which contains your HTML files is called foo and it is found from the web application root (src/main/webapp/foo). You can permit all requests to the files found from this directory by using modifying the configure(HttpSecurity http) method of the SecurityContext class.

      You can should modify this method by replacing the original authorization configuration with this one:

      
      .and()
      	.authorizeRequests()
      		//Anyone can access these urls
      		.antMatchers(
      			"/auth/**",
      			//This grants access to your directory
      			"/foo/**",
      			"/login",
      			"/signin/**",
      			"/signup/**",
      			"/user/register/**"
      		).permitAll()
      		//The rest of the our application is protected.
      		.antMatchers("/**").hasRole("USER")
      
      

      Remember that these matchers are processed in the same order than they are declared. That is why you need configure the urls which are not protected before configuring the matcher which protects the remaining urls. If you do this in the "wrong" order, the matcher '/**' matches with all urls of your application, and only users who have the role 'ROLE_USER' can access it.

      Unfortunately I haven't used JSF so I cannot answer to your second question. However, I managed to find a few tutorials / so questions about this:

      • Cassio Apr 8, 2014 @ 4:42

        Thks Petri, with your help I can do what I need. My problem was in the order of matches.

        About JSF, I did a integration of JSF and SpringSecurity a 3 years ago when SpringSecurity was in v2 and the configuration was in XML format. In the current times with SpringSecurity in v3 and anotations I don't have any idea if this works like in the past. Mainly because I think Spring team don't have interest in facilitate the life of a concurrent project JSF+ejb. Both are good, JSF have many nice features, but I think some important concepts like Dependency Injection was implemented in Spring first.

        It's time to make a decision and I'm entering in world of Spring, I'll choose it for believe in his potential and many required framework like SpringSecurtiy, SpringSocial are essentials for my Project and will work better together with less adaptations. I'll choose it, despite I have little knowledge in SpringMVC, almost zero... your article is helping me make this decison, thks a lot very clear article. Sorry about my English mistake, it's not my native language.

        Cheers

        • Petri Apr 8, 2014 @ 22:51

          Hi Cassio,

          It is good to hear that you were able to solve your problem. If you have very little experience from Spring MVC, you might want to check out Spring Boot. It takes an opinionated view of building Spring powered applications and you can avoid a lot of configuration if you are happy with the defaults.

          By the way, don't worry about your English. I think that it is just fine. :)

          • Cassio Seffrin Apr 9, 2014 @ 22:46

            Hi Petri,

            Yesterday I have been in travel, now I saw your link about SpringBoot and appreciate your help.

            Thks so much, best regards!

  • Cassio Seffrin Apr 20, 2014 @ 5:55

    Hi Petri,

    I'm doing some customizations in the user profile, I appreciate if you can check if I'm making the correct lesson.

    I created an edit page where the user can update your informations. The way I'm doing it is creating an "User Profile" link pointing to the RegistrationController with the id of the user. Then I created a new interface and implemented it (findUserById(Long id)), and then when the user fill the .jsp page and submit through POST method the another controller method are invoked, received the form data and process it, In the controller I call a service to merge the information of user profile in the database.

    Do you think I'm in the correct way?

    • Petri Apr 20, 2014 @ 9:53

      Sounds good. I would probably create a new UserAccountController class and put the new controller methods to that class. The reason for this is that these are two separate functions (registration and update your information), and I prefer to keep their code separated. But other that, it seems that you have found the "right" way to do this.

  • Cassio Apr 20, 2014 @ 6:09

    I forgot one important detail,

    In the jsp pages how I can discover all information we can get with the the tag .username, etc... Where it's mapped? The property "principal.id" solve my problem, but maybe is interesting use this taglib in another pages that don't needed to be edited to reduce app overhead.

    Regards and happy Easter!!

    • Petri Apr 20, 2014 @ 10:30

      The reference manual of Spring Security 3.2 explains how you can use the authentication tag.

      Because we know that the principal property of the Authentication object is an ExampleUserDetails object, we can access its properties by using this tag.

      For example, if we want to get the String which identifies the used social sign in provider, we can do it in this way:

      
      //Get social sign in provider and store it to a variable called signInProvider.
      <sec:authentication property="principal.socialSignInProvider" var"signInProvider"/>
      
      

      I hope that this answered to your question. Happy easter!

      • Cassio Apr 21, 2014 @ 3:12

        Yes your explanation is very clear... After read your blog and anothers articles including JPA Data I conclude you are a high level development in Java and Spring. The best I can found in the web :P

        I'm starting now in this frameworks, but how I had some experience with EJB3 and JSF I'm going good with Spring. Just one thing about another frameworks I used... JSF handle with AJAX and there is great features, another framework I recommend for view tier is DWR for AJAX too!... very interesting what they can do, very exiting, The DWR integration is very easy too, no obstacles with anothers frameworks.

        But I'm really good impressioned with your Spring articles and your apps architectures! I'll follow your recommendation in the another answer about UserController. Thks for the really good job Petri, long life for you!!!

        • Petri Apr 21, 2014 @ 19:30

          Again, thank you so much for your kind words. I really appreciate them!

          I think that one of the most important thing to remember is that you can learn new things every time you use new technologies. That is why think that everyone should try new things once in a while. I am sure that using Spring will teach you new skills which you can use if / when you decide to use Java EE in a new project.

  • Cassio Apr 23, 2014 @ 23:20

    Hi Petri,

    What's the best way to get the UserConnection.imageUrl attribute from database after authentication process. After create a new social user or authenticate an existent social user the RegistrationController persists the connection to the UserConnection table.

    My intension is display the user profile image in some view area after social user are logged in webapp. I saw you done alguns JUnit tests doing it, but it's seems to be a little complex.

    Cheers

  • Cassio Seffrin Apr 26, 2014 @ 3:56

    I found a way to do it, off corse it isn't the best, but the time is short and I needed to do it the fast way possible. This solution make easy to get any field from UserConnection table.

    Basic steps
    1. A new UserConnection Model was created, with all the attributes of the table.
    2. A new service interface UserAccountServiceInterface was created with this signature public String getImageProfile(Integer id);

    3. Added a new method in UserRepository class with the follow code:

    @Query("SELECT userCon.imageUrl FROM UserConnection userCon, User user WHERE userCon.userConnectionPK.userId=user.email AND user.id=:userId")
    public String findProfileImage(@Param("userId") Integer userId);

    4. This interface was implemented in Class UserAccountServiceImpl with the follow code:
    @Override
    public String getImageProfile(Integer id) {
    String imageProfile="";
    try {
    imageProfile = userRepository.findImageProfile(id);
    } catch (Exception e) {
    e.printStackTrace();
    }
    return imageProfile;
    }

    5. My new UserAccountController just calI the @Autowired service of the interface UserAccountServiceInterface:
    String imageProfile = userAccountService.getImageProfile(id);
    Then set it in the form, finaly the image profile is showed in the web interface. If there isn't profile imagem (UserRegistration case) the service will return a blank string and the view tier can use an appropriate imagem showin a X or something else.

    Cheers!

    • Petri Apr 28, 2014 @ 20:50

      Hi,

      it is good to hear that you were able to solve your problem. I am sorry that I couldn't answer to your question sooner but I was a bit busy. Have you considered adding a profileImageUrl field to the ExampleUserDetails class?

      This way you could get the profile image information in the loadUserByUsername() method of the RepositoryUserDetailsService class, and you could access it by using the authentication tag.

  • Titto Apr 30, 2014 @ 17:05

    Hi Petri,
    please help me...
    when I connect with Facebook, i see a registration page with name, surname and email (and all ok!). But when I click on "register" I get an 500 error and a redirect on my error page.
    I'm very stuck!

    • Petri May 1, 2014 @ 17:42

      Hi Titto,

      Can you find the error message / stack trace from the log? If so, add the relevant part here and I will take a look at it.

      • Titto May 2, 2014 @ 10:36

        Hi Petri, I've re-download the project... my issue was wrong library's version.

        Now I've another question.... this is the flow:
        1) go to login page and click on "sign in with Facebook"
        2) Facebook screen appear and I insert my email and pass
        3) redirect to registration page and I insert my email again
        4) User is registered correctly in database

        but if i close my browser and come back in login page is impossible to log in with Facebook. The registration screen appears again.... why?

        • Petri May 3, 2014 @ 0:08

          Hi Titto,

          Can you check out the log and see what kind of log messages is written to it when you come back to the login page for the second time (after you have registered a user account and closed the browser)?

          One possible reason for this is that for some reason the user account isn't found from the database, but it is hard to say what is going on without seeing the log file.

          • Titto May 3, 2014 @ 20:38

            hi,
            This is my catalina.out on tomcat7 log.
            user "titto for" is correctly present in database.

            this is what happens when I click on "sign in with Facebook" button :

            DEBUG - wire - http-outgoing-27 << "{"id":"639610986","first_name":"titto","gender":"male","last_name":"for","link":"https:\/\/www.facebook.com\/profile.php?id=639610986","locale":"it_IT","name":"titto for","timezone":2,"updated_time":"2013-12-14T23:09:31+0000","verified":true}"
            DEBUG - headers - http-outgoing-27 << HTTP/1.1 200 OK
            DEBUG - headers - http-outgoing-27 << Last-Modified: 2013-12-14T23:09:31+0000
            DEBUG - headers - http-outgoing-27 << ETag: "856ba655350437b7f130bf5d4d46aa13d4e5162b"
            DEBUG - headers - http-outgoing-27 << Content-Type: application/json
            DEBUG - headers - http-outgoing-27 << Pragma: no-cache
            DEBUG - headers - http-outgoing-27 << Access-Control-Allow-Origin: *
            DEBUG - headers - http-outgoing-27 << X-FB-Rev: 1232856
            DEBUG - headers - http-outgoing-27 << Cache-Control: private, no-cache, no-store, must-revalidate
            DEBUG - headers - http-outgoing-27 << Expires: Sat, 01 Jan 2000 00:00:00 GMT
            DEBUG - headers - http-outgoing-27 << X-FB-Debug: JHGQ8LlVhKkdZ+YXhZZ2Vo7S1ZVHoKcB2e9v3SI06zI=
            DEBUG - headers - http-outgoing-27 << Date: Sat, 03 May 2014 17:27:43 GMT
            DEBUG - headers - http-outgoing-27 << Connection: keep-alive
            DEBUG - headers - http-outgoing-27 <graph.facebook.com:443] can be kept alive indefinitely
            DEBUG - ttpClientConnectionManager - Connection released: [id: 27][route: {s}->graph.facebook.com:443][total kept alive: 1; route allocated: 1 of 5; total allocated: 1 of 10]
            DEBUG - RegistrationController - Rendering registration form with information: com.mypackage.user.dto.RegistrationForm@32cc89f3[email=,firstName=titto,lastName=for,signInProvider=FACEBOOK]

            After this I get the login screen again with first name, last name and email (blank)

            maybe some configuration on my Facebook's panel?
            thanks

            Update: I cut the log file a bit and left only the "relevant" part. - Petri

          • Petri May 4, 2014 @ 10:39

            Hi,

            It seems that one possible reason for this is that the connection isn't found from the UserConnection table. If this happens, the application assumes that user in question isn't registered yet (and renders the registration form).

            You can verify if this is the case by following these steps:

            1. Put a break point to the last line of the findUserIdsWithConnection() method of the JdbcUsersConnectionRepository class.
            2. Start you application in a debug mode.
            3. Sign in by using Facebook and see what that method returns.
          • Titto May 4, 2014 @ 18:58

            I can't see JdbcUsersConnectionRepository class.
            I'm using Windows Azure to connect at the db.

            I realized that nothing is written in the UserConnection table, maybe Facebook must read (or write) at my database? In this case Windows Azure don't allow to do this.

            where is the class for access at UserConnection table?

          • Petri May 4, 2014 @ 19:33

            You can find the JdbcUsersConnectionRepository class from the SocialContext class. The JdbcUsersConnectionRepository class is the class which is used to add new rows to the UserConnection table and find information from it.

            The registerUserAccount() method of the RegistrationController class calls the handlePostSignUp() method of the ProviderSignInUtils class. This method inserts a new row to the UserConnection table.

          • Titto May 4, 2014 @ 19:47

            I also say that in my local environment all works fine (In local I'm using jetty and mysql). Then the problem is with windows azure environment.

          • Titto May 4, 2014 @ 20:39

            Hi, I've resolve the problem:
            the problem was a filed in my azure database "nvarchar" rather than "varchar".
            and did not write anything in the table UserConnection.

            Thanks for your help!

          • Petri May 6, 2014 @ 17:53

            Great! This discussion has a good description about the dangers of storing utf8 characters to varchar column when you use MS SQL Server.

  • Justen Britain May 6, 2014 @ 1:00

    Hi,

    I have setup my security and social very similarly to this tutorial and your previous one, however in the JSP the code wrapped in "" is being hidden for all users. When debugging I am finding that there is no securityContext for anonymous users. I have verified that my "httpSecurity" configuration is identical to yours. The only notable difference between my config and yours is that I am using "@EnableWebMvcSecurity" where as you are using "@EnableWebSecurity". Can you point me in the right direction as to what may be causing the problem?

    Thanks,
    J

    • Petri May 6, 2014 @ 18:02

      It seems that my Wordpress "ate" the JSP markup which you added to your comment. Could you add it to Pastebin and attach the link to a new comment?

      • Justen Britain May 8, 2014 @ 5:37

        My security configuration class is as follows: http://pastebin.com/eHqqEtQK. The JSP file is identical to what you have posted here. I have stepped through the JSP tag

        and the problem seems to be that no anonymous securityContext.

        • Petri May 8, 2014 @ 21:33

          Thank you for providing additional information. I couldn't figure out a solution to your problem but I will take a closer look at this during the weekend. I will let you know when I find the solution.

          • Justen Britain May 9, 2014 @ 6:42

            I found the issue, I needed to add the security filter to the web.xml.

          • Petri May 10, 2014 @ 10:06

            Great! It is good to hear that you were able to solve your problem.

  • Fabian May 12, 2014 @ 22:10

    Thanks for the post! Very useful!

    But I still have a question: when I sign in using facebook, I got redirected to my homepage with "#_=_" append to the URL (a facebook thing, no big deal). But what is bothering me is that I don't want to go on the homepage after a successful connection but I want to display the dashboard. Is there a way to configure that?

    I have the default-target-url attribute (for the normal login form) but there is no link with Spring social...

    • Fabian May 13, 2014 @ 12:54

      And I am using Spring security. After some investigations, the default URL is set from org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler using org.springframework.security.web.DefaultRedirectStrategy used someway by the new filter "socialAuthenticationFilter". I cannot figure out how to override the default URL only for this filter.

      • Fabian May 13, 2014 @ 14:53

        I finally succeed :) Here is the code, maybe it will help other people http://pastebin.com/EtXu2Bcw

        • Petri May 13, 2014 @ 18:26

          It is good to hear that were able to solve your problem! However, there is an easier way to do this:

          If you use XML configuration, you can set the value of the postLoginUrl property of the SocialAuthenticationFilter class in your XML configuration file. You can do this by using this configuration:

          
          <beans:bean id="socialAuthenticationFilter" 
          			class="org.springframework.social.security.SocialAuthenticationFilter">
          	<beans:constructor-arg index="0" ref="authenticationManager" />
          	<beans:constructor-arg index="1" ref="userIdSource" />
          	<beans:constructor-arg index="2" ref="usersConnectionRepository" />
          	<beans:constructor-arg index="3" ref="connectionFactoryLocator" />
          	<!-- Sets the url of the registration - use in case the sign in has failed -->
          	<beans:property name="signupUrl" value="/user/register/" />
          	<!-- Sets the url of the dashboard - use in case the sign in has succeed -->
          	<beans:property name="postLoginUrl" value="/dashboard" />
          </beans:bean>
          
          

          If you use Java configuration, you can set this url by calling the postLoginUrl() method of the SpringSocialConfigurer class in your @Configuration class.

          • Fabian May 13, 2014 @ 20:44

            Why did I miss that? Maybe reading some Javadocs would have been useful ;) Thanks!

          • Petri May 13, 2014 @ 21:32

            This happens to all of us. ;)

            By the way, I read the source code of the SocialAuthenticationFilter class, and noticed that you might want to set the value of the alwaysUsePostLoginUrl property to true. Otherwise the user might be redirected to another page if it is found from the request cache.

  • Anonymous Jun 2, 2014 @ 15:56

    hi,

    while running this example, i am getting pom exception.

    please provide pom file

    • Petri Jun 2, 2014 @ 20:46

      The example application of this blog post is available at Github. Check out its pom.xml file.

  • Ram Jun 29, 2014 @ 17:09

    Petri,
    This is such a comprehensive app, for the reference for a whole lot of things. Thanks a lot!!!!
    Ram

    • Petri Jun 30, 2014 @ 17:07

      You are welcome! I am happy to hear that this blog post (and the example application) was useful to you.

  • Ron Jun 30, 2014 @ 12:38

    Hi,

    First, thanks a lot for this article. It was the best tutorial in using spring social!

    I have successfully setup spring-social with spring security in my local but when I tried to deploy it on a tomcat running behind an nginx server, then my callback url is not working as it is using the tomcat's internal domain name.

    I tried to set applicationUrl on the ConnectController but it seems that it is not using the value. here's how I did it.

    Do you know where to properly setup the callback url?

    Thanks a lot for your help!

    • Petri Jun 30, 2014 @ 17:31

      It seems that Wordpress ate your XML configuration. Anyway, are you using the same Facebook or Twitter application for local development and your Tomcat server? If so, this might be the root cause of your problem.

      The Facebook application has two settings which might cause problems if you use the same application:

      1. The App Domains setting configures the domain name of your application.
      2. The Server Url configures the url address of the website which uses Facebook login.

      You should verify that the values of these settings are pointing to your Tomcat server.

      On the other hand, if you use the same Twitter application for local development and Tomcat server, you will probably encounter this issue because Twitter allows only one callback URL per application.

      I have typically created one Facebook and Twitter application per environment (local dev, test, and production) and I haven't faced this problem yet. Although I have to admit that I haven't used Spring Social in a production application yet (I have used only Spring Social 1.0).

      Let me know if this answer didn't solve your problem!

  • yahoo Jul 3, 2014 @ 10:52

    I'm not sure where you're getting your information, but
    great topic. I needs to spend some time learning much more or understanding more.
    Thanks for great information I was looking for this information for my mission.

    • Petri Jul 3, 2014 @ 10:58

      You are welcome! I am happy to hear that this blog post was useful to you.

  • Onur Jul 15, 2014 @ 11:50

    Hi,
    I try your codes, but i take below error. Could you help me?

    Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:2.6:resources (default-resources) on project spring-mvc-normal: Error loading property file '/Users/OnurYilmaz/NetBeansProjects/spring-social-examples/sign-in/spring-mvc-normal/profiles/dev/socialConfig.properties' -> [Help 1]

    Thanks in advance.

    • Petri Jul 15, 2014 @ 12:08

      The problem is that you haven't created the socialConfig.properties file to the profiles/dev directory. Check out the README for more details about this.

      • Onur Jul 15, 2014 @ 13:14

        Thanks for your quick reply. This time, I take this error, please could you give me an advice?

        Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.16:test (default-test) on project spring-mvc-normal: Execution default-test of goal org.apache.maven.plugins:maven-surefire-plugin:2.16:test failed: The forked VM terminated without saying properly goodbye. VM crash or System.exit called ?
        Command was/bin/sh -c cd /Users/OnurYilmaz/NetBeansProjects/spring-social-examples/sign-in/spring-mvc-normal && /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/bin/java -javaagent:/Users/OnurYilmaz/.m2/repository/org/jacoco/org.jacoco.agent/0.6.3.201306030806/org.jacoco.agent-0.6.3.201306030806-runtime.jar=destfile=/Users/OnurYilmaz/NetBeansProjects/spring-social-examples/sign-in/spring-mvc-normal/target/coverage-reports/jacoco-ut.exec -jar /Users/OnurYilmaz/NetBeansProjects/spring-social-examples/sign-in/spring-mvc-normal/target/surefire/surefirebooter5815438427920763798.jar /Users/OnurYilmaz/NetBeansProjects/spring-social-examples/sign-in/spring-mvc-normal/target/surefire/surefire3143572413950208887tmp /Users/OnurYilmaz/NetBeansProjects/spring-social-examples/sign-in/spring-mvc-normal/target/surefire/surefire_06176340583243847328tmp

        • Petri Jul 15, 2014 @ 20:12

          This problem is caused by the JaCoCo Maven plugin. The easiest way to solve this problem is to remove the plugin configuration from the pom.xml file. You can do this by following these steps:

          1. Remove the JaCoCo Maven plugin.
          2. Remove the argLine element from the configuration of the Maven Surefire plugin.
          3. Remove the argLine element from the configuration of the Maven Failsafe plugin.

          Are you by any chance using Java 8? I remember facing a some kind of an exception when I tried the JaCoCo Maven plugin with Java 8, but I did not have time to investigate it at the time.

          • Eugene Nov 13, 2014 @ 19:08

            This works. I am using Java 8 and after these instructions the application built and worked. Thanks!

          • Petri Nov 13, 2014 @ 21:01

            You are welcome! Also, thank you for reminding me about this. I will fix this problem when I update my Spring Social tutorial.

  • MP Jul 16, 2014 @ 21:40

    I added the socialConfig.properties. But I have 2 problems.
    1. the jacoco maven plugin is not actually present in the mvn central repository
    2. http://pastebin.com/UmHtm4pp
    [ERROR] Failed to execute goal org.liquibase:liquibase-maven-plugin:3.1.1:update (default-cli) on project spring-mvc-normal: Failed to resolve the properties file. -> [Help 1]
    org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.liquibase:liquibase-maven-plugin:3.1.1:update (default-cli) on project spring-mvc-normal: Failed to resolve the properties file.

    • Petri Jul 16, 2014 @ 23:20

      1. the jacoco maven plugin is not actually present in the mvn central repository

      To be honest, I have no idea what is going on. When I removed the JaCoCo Maven plugin from my local Maven repository and ran the build, it was download from the Maven central repository:

      Downloading: http:/repo.maven.apache.org/maven2/org/jacoco/jacoco-maven-plugin/0.6.3.201306030806/jacoco-maven-plugin-0.6.3.201306030806.pom
      Downloaded: http:/repo.maven.apache.org/maven2/org/jacoco/jacoco-maven-plugin/0.6.3.201306030806/jacoco-maven-plugin-0.6.3.201306030806.pom (4 KB at 5.3 KB/sec)
      Downloading: http:/repo.maven.apache.org/maven2/org/jacoco/jacoco-maven-plugin/0.6.3.201306030806/jacoco-maven-plugin-0.6.3.201306030806.jar
      Downloaded: http:/repo.maven.apache.org/maven2/org/jacoco/jacoco-maven-plugin/0.6.3.201306030806/jacoco-maven-plugin-0.6.3.201306030806.jar (36 KB at 174.7 KB/sec)

      (I removed one '/' character from the log so that Wordpress doesn't create a hyperlink)

      ERROR] Failed to execute goal org.liquibase:liquibase-maven-plugin:3.1.1:update (default-cli) on Failed to resolve the properties file. -> [Help 1]

      The reason of this problem is that the Liquibase Maven plugin cannot find the liquibase.properties file. You should create that file to the src/main/resources directory (see the liquibase.properties file of the example app).

      • JC Jan 7, 2016 @ 22:42

        I am having the same problem, MP - Did you resolve the issue?

        [ERROR] Failed to execute goal org.liquibase:liquibase-maven-plugin:3.1.1:update (default-cli) on project spring-mvc-normal: Error setting up or running Liquibase: liquibase.exception.DatabaseException: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
        [ERROR]

        • JC Jan 7, 2016 @ 22:44

          Nevermind - realized mysql server had stopped on me.

  • AM Aug 11, 2014 @ 6:53

    Thanks for your posts. You have a great tutorial about Spring Social.

    I have one thing still not understand. It's about the bean 'connectionFactoryLocator', I cannot find its declaration in your config but I see you config other bean reference to it. Below is your config that has reference to 'connectionFactoryLocator' bean:

    • Petri Aug 11, 2014 @ 18:23

      The ConnectionFactoryLocator bean is provided automatically by Spring Social.

      For example, if you annotate a configuration class with the @EnableSocial annotation, Spring Social will load beans configured in the SocialConfiguration class (this class configures the ConnectionFactoryLocator bean).

      I haven't paid a lot of attention to the XML configuration so unfortunately I am not sure which XML element enables the same behavior. :(

  • Anonymous Aug 16, 2014 @ 12:22

    Where is the source of "CommonConstraintValidator" in "CommonConstraint". I am getting error "For non-composed constraints a validator implementation must be specified using @Constraint#validatedBy()".

    • Petri Aug 16, 2014 @ 12:35

      I use the @CommonConstraint annotation as an example that explains the steps required to create new constraint annotations. In other words, you don't have create this annotation. Do you think that this should be mentioned in the blog post?

      • Mohit Aug 16, 2014 @ 13:01

        Thanks Petri for quick reply, I am following this post from a week and trying to integrate piece by piece, I have used your code as it is, and i am not sure how to fix this issue. I thought you have explained every piece in so detail, so i was following things blindly, it would be great if you can help in this.

        You have mentioned every possible detail, if you could provide some details on this also, i am sure it would help other followers as well.

      • Mohit Aug 16, 2014 @ 13:03

        Oh do you mean, its nowhere used actually in your flow, and its just for reference?

        • Petri Aug 16, 2014 @ 13:05

          Yes. I think that I will add a comment that makes this clear.

  • repkin Sep 2, 2014 @ 1:20

    Hello Petri,

    Firstly I would like to thanks for this example.

    I am trying to use successHandler and failureHandler with SpringSocialConfigurer together but as I understand SpringSocialConfigurer() is overriding my special success handler. So default successHandler is invoked after auhtentication completed. How can I solve this problem, how can I use them tohether?

    My SecurityConfig.configure method is like that:

    http
    .csrf().disable()
    .logout()
    .deleteCookies("JSESSIONID")
    .logoutUrl("/logout")
    .logoutSuccessUrl("/login")
    .and()
    .authorizeRequests()
    .antMatchers("/**")
    .permitAll()
    .antMatchers(
    "/setting**", "/password**", "/create**",
    "/edit**", "/createforme**", "/upload**"
    )
    .authenticated()
    .and().apply(new SpringSocialConfigurer()).and()
    .formLogin()
    .loginPage("/login")
    .loginProcessingUrl("/login/authenticate")
    .failureHandler(loginAuthenticationFailureHandler())
    .successHandler(loginAuthenticationSuccessHandler());

    • Petri Sep 2, 2014 @ 19:38

      Hi Repkin,

      I tested this and it seems that:

      • If you configure the authentication success or failure handlers to by using a FormLoginConfigurer object, these handlers are called only when a form login is successful or it fails.
      • At the moment the SpringSocialConfigurer class provides very limited configuration options. You can only set post login url and post failure url.

      If you need to use a sophisticated authentication success handler and authentication failure handler when the user of your application is using social sign in, you have to "modify" the SpringSocialConfigurer class to support custom authentication success handler and authentication failure handler. You can do this by following these steps:

      1. copy-paste the source code of the SpringSocialConfigurer into a new class and add the setter methods that used to set the authentication success and failure handlers.
      2. Modify the configure() method of your new class so that it sets the authentication success and failure handlers to the SocialAuthenticationFilter by using the setAuthenticationSuccessHandler() and setAuthenticationFailureHandler() methods.
      3. Use your copy of the SpringSocialConfigurer class when you configure Spring Security.

      I haven't tried this out but it should do the trick.

  • Nikolay Sep 8, 2014 @ 12:52

    Hello Petri!
    Thanks for this great tutorial!
    I have two questions:
    1) In your answer about configuring authorized requests two different routs regarding redirects from spring social plugins: /signup and /signin. But in application exists only mapping to /signup. But I implement /signin mapping in mu application and I get strange requests to this route with empty connection info. What for this route? Should I implement it or not?
    2) How I can implement saving of existing authorizations for keep users alive after redeploy?
    Thanks!

    • Petri Sep 8, 2014 @ 21:58

      Hi Nikolay,

      I think that I simply forgot to remove the '/signIn/**' ant pattern from the security configuration. I removed it from my local copy of the example application and everything was working as expected. In other words, you should remove it from your configuration as well.

      Thank you for pointing this out. I will fix this tomorrow and update this blog post.

      If you want to add remember me function to your application, you should take a look at this Spring Social sample application. To more specific, you have to enable the remember me functionality in your security configuration class and add the remember-me HTTP parameter to the request when the user is signing in (either by using social sign in or form login).

      • Petri Sep 9, 2014 @ 20:43

        I updated the example application and the relevant blog post.

  • Frankbeerstein Sep 26, 2014 @ 20:56

    I'm trying to load this in Tomcat 7, and after removing the glassfish servlet dependency, ExampleApplicationConfig won't compile unless I comment out the glassfish ServletRegistration.Dynamic stuff. After commenting out the first rootContext block and uncommenting the second, I'm able to compile and deploy to Tomcat, but the regular home page won't show. This is my complete onStartup:

    
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
         XmlWebApplicationContext rootContext = new XmlWebApplicationContext();
         rootContext.setConfigLocation("classpath:exampleApplicationContext.xml");
    }
    
    

    What else should I do?

    • Petri Sep 26, 2014 @ 21:14

      You need to use the original web application configuration class and the original pom.xml file.

      The scope of the Servlet API 3.0 dependency is provided. The introduction to Maven's dependency mechanism describes the different dependency scopes. That article describes the provided scope as follows:

      This is much like compile, but indicates you expect the JDK or a container to provide the dependency at runtime. For example, when building a web application for the Java Enterprise Edition, you would set the dependency on the Servlet API and related Java EE APIs to scope provided because the web container provides those classes. This scope is only available on the compilation and test classpath, and is not transitive.

      By the way, if you plan to use XML configuration instead of Java configuration, you should also remove the Spring profile configuration from the application context configuration file that configures Spring Social.

      • Frankbeerstein Sep 27, 2014 @ 8:53

        Ah yes. Its groupId was 'org.glassfish' so that was the confusion, although of course I replaced it with another servlet dependency - forgot to mention that. Anyway, I eventually got it working just fine using the java config. The XML config is what I want to use though, and it keeps throwing:

        No bean named 'usersConnectionRepository' is defined

        I have added 'spring.profiles.active=dev' to catalina.properties. @Profile was already missing from SocialContext.

        After that, I tried it two ways. First, I followed the instructions in ExampleApplicationConfig, commenting and uncommenting. I left all the Filter definitions in place there. Got the above error. Here's the java for review:

        public void onStartup(ServletContext servletContext) throws ServletException {
        //If you want to use the XML configuration, comment the following two lines out.
        //AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
        //rootContext.register(ExampleApplicationContext.class);

        //If you want to use the XML configuration, uncomment the following lines.
        XmlWebApplicationContext rootContext = new XmlWebApplicationContext();
        rootContext.setConfigLocation("classpath:exampleApplicationContext.xml");

        ServletRegistration.Dynamic dispatcher = servletContext.addServlet(DISPATCHER_SERVLET_NAME, new DispatcherServlet(rootContext));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping(DISPATCHER_SERVLET_MAPPING);

        EnumSet dispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD);

        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        characterEncodingFilter.setForceEncoding(true);

        FilterRegistration.Dynamic characterEncoding = servletContext.addFilter("characterEncoding", characterEncodingFilter);
        characterEncoding.addMappingForUrlPatterns(dispatcherTypes, true, "/*");

        FilterRegistration.Dynamic security = servletContext.addFilter("springSecurityFilterChain", new DelegatingFilterProxy());
        security.addMappingForUrlPatterns(dispatcherTypes, true, "/*");

        FilterRegistration.Dynamic sitemesh = servletContext.addFilter("sitemesh", new ConfigurableSiteMeshFilter());
        sitemesh.addMappingForUrlPatterns(dispatcherTypes, true, "*.jsp");
        servletContext.addListener(new ContextLoaderListener(rootContext));
        }

        Next, I deleted ExampleApplicationConfig and put all that stuff in a web.xml. I get the same error. Here's the web.xml I made:

        Petrika App

        contextConfigLocation
        classpath:exampleApplicationContext.xml

        webAppRootKey
        /petrika-spring-social

        springSecurityFilterChain
        org.springframework.web.filter.DelegatingFilterProxy

        sitemesh
        com.opensymphony.sitemesh.webapp.SiteMeshFilter

        CharacterEncoding
        org.springframework.web.filter.CharacterEncodingFilter

        encoding
        UTF-8

        forceEncoding
        true

        CharacterEncoding
        /*

        sitemesh
        *.jsp

        springSecurityFilterChain
        /*

        org.springframework.web.context.ContextLoaderListener

        spring
        org.springframework.web.servlet.DispatcherServlet
        1

        spring
        /

        There doesn't appear to be a @Bean defined anywhere with the name UsersConnectionRepository. Shouldn't there be?

        • Petri Sep 27, 2014 @ 10:39

          The XML config is what I want to use though, and it keeps throwing: No bean named 'usersConnectionRepository' is defined

          This error message is given when you use the XML configuration and don't set the active Spring profile.

          Anyway, I removed the Spring profile configuration from the exampleApplicationContext-social.xml file yesterday. In other words, the example application should be easier to run even if you use XML configuration.

          You can solve your problem by updating the exampleApplicationContext-social.xml file to the latest version (see the link above).

          There doesn’t appear to be a @Bean defined anywhere with the name UsersConnectionRepository. Shouldn’t there be?

          If you use XML configuration, there is no need to configure the UsersConnectionRepository bean because the jdbc-connection-repository element of the social namespace creates it for you.

  • Roman Oct 23, 2014 @ 1:06

    I want show on all pages user's name from facebook (after he is authorized), now I saved it to database after signup, but if he change it in facebook it will not be changed on my site, what is the best way to do it? The only way is fetch user profile on login and get from there username?

    • Petri Oct 26, 2014 @ 10:43

      My first thought was that you could do this by creating a custom AuthenticationSuccessHandler. I tried this out and noticed that it isn't so simple.

      First, you have to create your own copies of the SpringSocialConfigurer and SocialAuthenticationFilter classes (otherwise you cannot set the used AuthenticationSuccessHandler).

      Second, you have to extract the Connection object from the request. The problem is that the ProviderSignInUtils class doesn't find the Connection object if it is used in a AuthenticationSuccessHandler. I assume that the reason is that the request that is passed to the AuthenticationSuccessHandler object is the request that was created when a user clicks a social sign in link. This request is not the same request that is send to the social sign in provider, and that is why the Connection object is not found.

      In other words, I don't know how you can do this. I will add your question to my Trello board and take a closer look at it when I have got time to do it.

  • tapan Oct 27, 2014 @ 23:03

    Hi Petri,

    When I am trying to integrate your code with my project I am getting the below error.

    java.lang.NoSuchMethodError: org.springframework.expression.spel.SpelParserConfiguration.(Lorg/springframework/expression/spel/SpelCompilerMode;Ljava/lang/ClassLoader;)V
    at org.springframework.context.expression.StandardBeanExpressionResolver.(StandardBeanExpressionResolver.java:98)
    at org.springframework.context.support.AbstractApplicationContext.prepareBeanFactory(AbstractApplicationContext.java:553)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:455)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)

    Any idea why this is coming?

    • Petri Oct 27, 2014 @ 23:21

      I think that your Spring version is not be compatible with Spring Social 1.1.0. Which Spring version are you using?

      Also, you might want to check out the version of the spring-expression module because the SpelParserConfiguration class belongs to that module.

      If I remember correctly, Spring Social 1.1.0 should work with Spring Framework 3.2.X or newer.

      • Tapan Nov 2, 2014 @ 14:25

        Spring framework version is 4.0.0.RELEASE
        Spring-expression module version is 3.2.4.RELEASE

        • Petri Nov 5, 2014 @ 20:06

          Have you tried to update the spring-expression module to the version 4.0.0.RELEASE?

          • tapan Nov 11, 2014 @ 3:55

            Issue solved,Wrong version of spring-expression was provided.
            thanks petri

          • Petri Nov 11, 2014 @ 20:15

            You are welcome! I am happy to hear that you were able to solve your problem.

  • Tapan Nov 12, 2014 @ 20:21

    Hi Petri

    One more issue i am facing now is .
    getting error
    Severe: PWC6117: File "C:\glassfish-4.0\glassfish4\glassfish\domains\domain1\applications\humagi-1.0-SNAPSHOT\WEB-INF\jsp\error\error.jsp" not found

    whenever I am clicking on any button.
    No logs getting generated :(

    Please help if possible

    Thanks in advance

    • Tapan Nov 13, 2014 @ 6:56

      Hi Petri ,
      Looks like Routing from Login page to Registration page is not taking place when i am integrating your code with mine in Glassfish.

      which is a simple peace of code .I am new to spring so dont know whats causing the issue.
      Any idea if I am missing anything?

      • Petri Nov 13, 2014 @ 9:26

        I haven't used Glassfish myself, but I can try to deploy the example application to Glassfish and see if I can find a problem. I can do this today after work.

        Which Glassfish version are you using?

        • Tapan Nov 13, 2014 @ 20:14

          Thanks a lot.
          I am using 4.0

          • Petri Nov 13, 2014 @ 21:06

            I cannot start Glassfish 4.0 on OS X. I will use Glassfish 4.1 instead.

          • Petri Nov 13, 2014 @ 21:34

            I was able to reproduce this problem with Glassfish 4.1. When I clicked the 'Create user account' link, the server rendered an error page with response status 404. I found the following lines from the log file of my application:

            DEBUG - RegistrationController - Rendering registration form with information: net.petrikainulainen.spring.social.signinmvc.user.dto.RegistrationForm@661c247[email=,firstName=,lastName=,signInProvider=]]]
            [2014-11-13T21:30:57.387+0200] [glassfish 4.1] [SEVERE] [] [org.apache.jasper.servlet.JspServlet] [tid: _ThreadID=28 _ThreadName=http-listener-1(2)] [timeMillis: 1415907057387] [levelValue: 1000] [[
            PWC6117: File "null" not found]]

            I am not sure what is wrong, but I will try to solve this problem.

          • Tapan Nov 13, 2014 @ 23:52

            Thanks Petri
            This is the exact issue I am facing .

          • Petri Nov 16, 2014 @ 14:10

            I have been trying to find the solution to this problem, but I haven't found anything useful. I found this StackOverflow question but it didn't solve this problem. I think that I am not able to solve this problem. :(

            Also, because it is "impossible" to find new tutorials that describe how I can deploy a Spring web application in Glassfish, I assume that no one is using Glassfish for running Spring web applications. That is why I recommend that you should replace Glassfish with Tomcat.

  • arahansa Nov 21, 2014 @ 10:37

    Hello Petri. I'm a korean developer. I read your posts such as JPA and this article..
    I'm very appreciate of your posts.

    Because It's hard to find good and practical Spring tutorials in korea blogs.
    So.. Can I translate your post into Korean post and share with develop community member?
    If you say yes. I will be very pleased.
    Of course I'll mark origin link and your name in my translated post.

    hm.I'll wait for you answer.
    have a good day!

    • Petri Nov 21, 2014 @ 10:49

      Hi Arahansa,

      Thank you for you kind words. I really appreciate them!

      And yes, you can translate my articles into Korean as long as you as link back to the original article.

      By the way, I am going to update my Spring Data JPA tutorial because the old one is becoming obsolete. You might want to wait for the new tutorial before you translate it because it will be a lot better.

      • Sona Dec 9, 2014 @ 12:00

        Hi Petri,

        One feedback I would like to provide & I hope you don't take it wrong. Would it be possible for you to share an implementation which relies on just JPA and not Spring Data? Many users don't use Spring Data (including me) and may find the implementation helpful.

        • Petri Dec 10, 2014 @ 19:46

          Hi Sona,

          Thank you for the suggestion! I will think about this when I update my Spring Social tutorial. I will do it next year (I am not sure when I have got the time to do it).

          In any case, this seems to be a problem => I have to find the best way to solve it.

  • Byron Dec 7, 2014 @ 3:36

    Hi nice tutorial, i have a issue when i sign in with facebook appears this message
    org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [select userId from UserConnection where providerId = ? and providerUserId = ?]; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'test.userconnection' doesn't exist

    Any idea why this is coming?

    • Petri Dec 7, 2014 @ 10:21

      Hi Byron,

      You need to add the UserConnection table to your MySQL database. Check the section 2.3.1. JDBC-based persistence of Spring Reference Manual for more details about this.

      Thank you for asking this question. You are not the first person who has asked this -> I will add this information to this tutorial when I update it (I will do this next spring).

      Again, thank you for helping me to make this tutorial a bit better.

  • Sona Dec 9, 2014 @ 11:58

    Hi Petri,

    I am back with more questions :)

    I am using JPA/Hibernate and not using Spring Data for accessing my database. I want to know which steps I should perform for persisting a social user in UserConnection table in RegistrationController.registerUserAccount().

    Your example makes a call to ProviderSignInUtils.handlePostSignUp(registered.getEmail(), request) which in turn relies on Spring Data for persisting information about new user. How should I do this in my code? Which interfaces/functions I have to implement so that they get called automatically by the framework for inserting / searching contacts? I don't see you anywhere inserting records in userconnection table but in my case, I think I need to create DAO/Service layers for the same.

    Thanks in advance.

    • Petri Dec 9, 2014 @ 17:39

      Hi Sona,

      Your example makes a call to ProviderSignInUtils.handlePostSignUp(registered.getEmail(), request) which in turn relies on Spring Data for persisting information about new user.

      Actually, it persists the connection to the UserConnection table by using the UsersConnectionRepository bean. My example application uses the JdbcUsersConnectionRepository that persists the connection by using JDBC.

      How should I do this in my code? Which interfaces/functions I have to implement so that they get called automatically by the framework for inserting / searching contacts? I don’t see you anywhere inserting records in userconnection table but in my case, I think I need to create DAO/Service layers for the same.

      If you only want to insert data to the UserConnection table, you don't have to make any changes to the application because this information is persisted by using JDBC.

      • Sona Dec 11, 2014 @ 20:29

        Thanks for confirming this Petri. Looks like I was doing mistake elsewhere and the code was all set. I just rebuilt everything and called the postSignup() and realized that connection information was getting stored properly.
        I am able to log in through FB, LI and Twitter and I thank you for all your support.

        • Petri Dec 11, 2014 @ 20:33

          You are welcome. It is good to hear that you were able to solve your problem.

          Also, thank you for coming back and asking these questions. Every question helps me to get a better understanding of the problems that other developers face, and this helps me to do a better job when I update this tutorial.

  • Meghna Dec 13, 2014 @ 10:44

    Hi Petri,

    I am trying to use web.xml in this application but after using web.xml your application is not able to recognize the jsp pages.

    below is web.xml i am using .

    Please let me know what mistake i am doing .
    I am new to web application.

    • Petri Dec 13, 2014 @ 10:49

      Hi Meghna,

      It seems that Wordpress "ate" your web.xml (I assume that it is a security feature). Anyway, could you add the same web.xml file to Pastebin.com and share the link with me?

      • Meghna Dec 13, 2014 @ 11:23
        • Meghna Dec 13, 2014 @ 14:18

          When i am using http://pastebin.com/5yUAntMD web xml

          application is not getting deployed in tomcat.

          getting below error.

          
          SEVERE: Error filterStart
          Dec 13, 2014 5:42:12 PM org.apache.catalina.core.StandardContext startInternal
          SEVERE: Context [/humagi] startup failed due to previous errors
          
          

          I removed the irrelevant lines from this comment - Petri

          • Petri Dec 13, 2014 @ 15:41

            I assume that the your log has a stacktrace and / or other errors before the error message which states that the application context couldn't be started because of previous errors. Could you add those errors here as well?

            Also, it seems that you are loading two "root context" configuration files: WEB-INF/spring/root-context.xml and /WEB-INF/spring/exampleApplicationContext.xml. Do these files contain configuration that can cause a conflict?

          • Meghna Dec 15, 2014 @ 17:17

            Menaged to remove all the errors.
            It was a dependency jar conflict .

            http://pastebin.com/JkxHLnUb

            Above is my current web.xml

            now i am able to launch it but the login.jsp file is not getting decorated with sitemesh3.xml

            I have kept the sitemesh3.xml file in WEB-INF folder.

          • Petri Dec 16, 2014 @ 20:16

            I have to admit that I am not sure what is wrong. Your web.xml file looks correct to me, and it should work if you are using the correct version of Sitemesh and your sitemesh3.xml file has no errors in it.

            Have you tried lowering the log level to DEBUG? If so, did you find anything useful from the log file?

            Also, could you add the sitemesh3.xml file to pastebin?

          • rahul Jul 16, 2015 @ 15:18

            hi meghna,
            i am also facing the same problem. my web.xml is exactly the same as yours. were you able to solve this problem??

          • Petri Jul 16, 2015 @ 16:55

            Hi Rahul,

            I took a second look at the web.xml file, but I couldn't find any problems from it. I have two questions to you:

            • Does your sitemesh3.xml file look like this?
            • If you lower the log level to DEBUG, do you see anything interesting in the log file?
  • Jay Dec 23, 2014 @ 19:42

    Hi Petri,

    Nice tutorial, keep it up.

    • Petri Dec 23, 2014 @ 22:02

      Thank you. I will :)

  • Sona Dec 25, 2014 @ 20:28

    Hi Petri,

    Merry Xmas & Wish you a very happy new year in advance :)

    Hope you are having a great time !!!!

    • Petri Dec 26, 2014 @ 0:24

      Hi Sona,

      Thank you! Merry Christmas and happy new year to you too!

  • Jagger Jan 13, 2015 @ 17:38

    Hi Petri. I have a question to the tutorial. After signing up with Facebook the info I get is only the first and last name. There is no e-mail address. I researched a bit and it turned out that to get it I have to specify it in so called scope as 'email'. Unfortunately all the examples are with a form with POST parameter while in your tutorial there is a simple link 'auth/facebook'. How can I set the scope in your example?

    • Petri Jan 13, 2015 @ 17:52

      That is a good question, and you are not the only one who has asked this question. Unfortunately it seems that this is a bug in Spring Social (the bug report has a solution this problem).

      By the way, if you transform the 'Sign in with Facebook' link into a form, you should be able to add the hidden email parameter to the request. I haven't tried this myself, but it would be interesting to know if it is possible to pass the scope as a hidden POST parameter.

      • Sergii Shapoval Jan 25, 2015 @ 13:45

        I have just added a parameter in a link: ?scope=email, it helps to access email in google and facebook:

        
        <a href="${pageContext.request.contextPath}/auth/facebook?scope=email" rel="nofollow">
          Login with Facebook
        </a>
        
        
  • ak Jan 18, 2015 @ 17:36

    Hi Petri. Great tutorial. I want to extend this model further and include email verification of new accounts. After the user creates the account, the account would be in an unverified state until the user clicks on a verification link in his email. If the user attempts to login with an unverified state, the user would be redirected to a page with the text indicating the account is unverified as of yet. Any attempt to go to any url on the website after login would always redirect the user. I know how to send email with verification token and how to process if the user comes in with the link, but how do I intercept url if user does not come in with the link? Do I have to setup my own filter and check if user is unverified?

    • Petri Jan 19, 2015 @ 21:22

      Hi,

      thank you for your kind words. I really appreciate them.

      I would probably create a servlet filter that would check if the logged in user hasn't verified his/her user account. The reason for this is that I don't want to "pollute" the authorization logic with code that is required only to fulfill this special requirement.

      If you figure out another solution to this problem, I would love to hear it!

  • matteo Jan 21, 2015 @ 9:25

    does not work with spring core 4.1x only spring core 3.2.13

    • Petri Jan 21, 2015 @ 9:36

      What does not work with Spring 4.1.x?

      The example application uses Spring 4.0, but I haven't tested it with Spring 4.1. I will do that later today.

      If everything works as expected, I will update the dependency version. If not, I will make the required changes.

      Thanks you for reporting this!

      • Sumit Rathore Feb 7, 2015 @ 7:12

        Hi Petri,

        First of all, a big thanks for such a detailed tutorial. I appreicate all your hardwork.
        Now, I am running a spring MVC ( broadleaf ecom) and trying to setup the social login / Register.
        I have gone through your post and did not understand one thing.
        What exactly is happening on the register page ?

        How Do, I show or the customer specify that they want to use the social profile details for login.
        I mean, its Just Login, there is NOTHING like social registration. I mean, I can have my registration form, but I cannot have something like "Register using facebook" . Its just that on the sign-in page, I can show "Login with Facebook" and once the user logs in , I may get the profile details and create a user in my DB. But when the user returns, he will again use "Login with Facebook" ?

        Its more of a clarification than confusion.

        • Petri Feb 7, 2015 @ 12:38

          First of all, a big thanks for such a detailed tutorial. I appreicate all your hardwork.

          Thank you for your kind words. I really appreciate them!

          How Do, I show or the customer specify that they want to use the social profile details for login.

          The user can select this by either entering his/her email and password to the login form or clicking the appropriate social sign in link. The UI is probably a bit rough but it is good enough for this purpose (this is just a tutorial).

          I mean, its Just Login, there is NOTHING like social registration. I mean, I can have my registration form, but I cannot have something like “Register using facebook” . Its just that on the sign-in page, I can show “Login with Facebook” and once the user logs in , I may get the profile details and create a user in my DB. But when the user returns, he will again use “Login with Facebook” ?

          Actually this is the way social sign in works in most of the websites that use form login and social sign in (and require additional information from the user). You can of course skip the registration form if you don't need to ask additional information from the user. I wanted to add this step here because it might be required if you need to get the email address of the user (all social sign in providers don't return the email address).

          I think that you can probably to make this more obvious by adding a help text which explains how the login/registration flow works.

  • Alireza Mar 11, 2015 @ 18:15

    hi, Petri
    I tried to go around with this great tutorial, but I find it very customized and full of unnecessary stuff,
    hmm and some methods are deprecated.
    it would be awesome if you could summarize this tutorial for those who already have their app running and just want to plug it into spring social to sign in a user
    still this is a great work and helped me very much, though I couldn't get my app running yet
    regards,
    Ali

    • Petri Mar 11, 2015 @ 22:02

      Hi,

      Thank your for the feedback. I will update my Spring Social tutorial later this year, and I will address your concerns when I do that.

  • Liu May 5, 2015 @ 15:16

    Hi Petrik,
    Problems again.
    When I update the Spring Security to 4.0.1 the project gave me an error : "java.lang.NoSuchMethodError: org.springframework.social.security.SocialAuthenticationFilter.getFilterProcessesUrl()Ljava/lang/String;" when I try to run it on Tomcat.
    I have no idea with this problem.

    • Petri May 5, 2015 @ 18:35

      Hi,

      Spring Social 1.1 is not compatible with Spring Security 4.0. There is an open pull request that fixes this problem. I hope that it will be merged as soon as possible.

    • Bogdan May 26, 2015 @ 13:40

      Hi Liu,

      Upgrade spring social config, core, security and web to 1.1.2.RELEASE and the error will be fixed.

      • Petri May 26, 2015 @ 17:23

        Hi Bogdan,

        Thank you for sharing the solution to Liu's problem. I am sure that it will be useful to my readers.

  • Liu May 9, 2015 @ 12:17

    Hi, Petri,
    Thanks a lot.

    • Petri May 9, 2015 @ 14:48

      Hi Liu,

      You are welcome! I am happy to hear that this blog post was useful to you.

  • Vivek Bohra May 11, 2015 @ 14:05

    HI,

    Its nice post. I have one doubt that how application authenticate through facebook credential and redirect to " /signup" . Basically i don't understand that where we have configure about application redirect to "/signup" page" after successfully sign in via Facebook , please explain in detail.

    • Petri May 13, 2015 @ 19:58

      Hi,

      I am sorry that it took me couple of days to answer to your question. Anyway, Spring Social redirects user to the url '/signup' when it doesn't found a user account from the database. If you want to configure this url, you have two options:

      • If you configure your application by using Java configuration, you cannot change this url without creating a custom SpringSocialConfigurer class. That is why I implemented a simple controller method that redirects user from the url '/signup' to the url '/user/register'.
      • If you configure your application by using XML configuration, you can configure this url by setting the value of the signUpUrl property of the SocialAuthenticationFilter. If you want to see how this is done, check out the application context configuration file named: exampleApplicationContext-security.xml.

      On the other hand, if you want to configure the url to which an existing user is redirected after a successful social sign in, you have two options:

      I hope that this answered to your question.

  • DƯơng Jun 23, 2015 @ 9:53

    Please send source code to [censored] thank you !

    Removed the email address so that spam bots won't get it - Petri

  • shashwat Jul 13, 2015 @ 22:00

    Hi Petri,
    the ProviderSignInUtils.getConnection(request) method which is used in Registration controller is now deprecated. instead it is suggested to use a getConnectionFromSession method. can you please help in how to use this method as it requires a number of parameters which are not available.

    regards,
    shashwat

    • Petri Jul 13, 2015 @ 22:30

      You can simply pass a WebRequest object as a method parameter to the controller method which invokes the getConnectionFromSession() method of the ProviderSignInUtils class. Because the WebRequest interface extends the RequestAttributes interface, you can then pass it as a method parameter to the getConnectionFromSession() method.

      Here is a simple example:

      
      @Controller
      public class RegistrationController {
      
      	private final ProviderSignInUtils providerSignInUtils;
      	
      	@Autowired
      	public RegistrationController(ProviderSignInUtils providerSignInUtils) {
      		this.providerSignInUtils = providerSignInUtils;
      	}
      	
          @RequestMapping(value = "/user/register", method = RequestMethod.GET)
          public String showRegistrationForm(WebRequest request) {
      		//Get connection
      		Connection connection = providerSignInUtils.getConnectionFromSession(request);
      	}
      }
      
      

      If you use Spring Social 1.1.0 (like this tutorial), you can create the ProviderSignInUtils bean by using its default constructor. The relevant part of your SocialContext class looks as follows:

      
      @Configuration
      @EnableSocial
      public class SocialContext implements SocialConfigurer {
      
      	@Bean
      	public ProviderSignInUtils providerSignInUtils() {
      		return new ProviderSignInUtils();
      	}
      }
      
      

      If you use the latest Spring Social (1.1.2), you can use this constructor. The relevant part of your SocialContext class looks as follows:

      
      @Configuration
      @EnableSocial
      public class SocialContext implements SocialConfigurer {
      
      	@Bean
      	public ProviderSignInUtils providerSignInUtils(
      				ConnectionFactoryLocator connectionFactoryLocator,
      				UsersConnectionRepository connectionRepository) {
      		return new ProviderSignInUtils(connectionFactoryLocator,
                         connectionRepository
      		);
      	}
      }
      
      

      I haven't tested these code samples => They might contain some errors. You should be able to understand the general idea though. If you have any further questions, don't hesitate to ask them.

      • krekon Jul 6, 2016 @ 12:11

        Hi,

        I have some issues with this configuration:
        First I use the config for version 1.1.0 of spring social, and the result of the connection variable on '@RequestMapping(value = "/user/register", method = RequestMethod.GET)' is null.

        Then I tried to use the second config for version 1.1.2. The problem here is that there is the following exception on Glassfish:

        org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'providerSignInUtils' defined in class net.petrikainulainen.spring.social.signinmvc.config.SocialContext: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.springframework.social.connect.web.ProviderSignInUtils net.petrikainulainen.spring.social.signinmvc.config.SocialContext.providerSignInUtils(org.springframework.social.connect.ConnectionFactoryLocator,org.springframework.social.connect.UsersConnectionRepository)] threw exception; nested exception is java.lang.NoSuchMethodError: org.springframework.social.connect.web.ProviderSignInUtils.(Lorg/springframework/social/connect/ConnectionFactoryLocator;Lorg/springframework/social/connect/UsersConnectionRepository;)V

        After some search online I discovered that it may have some relation to the version dependencies between the plugins of spring, but when I tried it nothing changed. The same error appears.

        Do you know what may is the problem?

        • Petri Jul 7, 2016 @ 11:49

          Hi,

          If you use the second configuration, you need to use Spring Social 1.1.2 or newer. The exception is thrown because the constructor in question was not found for some reason. For example, you might have multiple Spring Social jars in the classpath and a class loader might load the wrong one. Unfortunately it's kind of impossible to say what the exact problem is without debugging the problematic application.

          • krekon Jul 7, 2016 @ 15:55

            I fixed the error and the app runs normally, but the connection variable is still null.
            Do you think is it an issue of the first request the app does on the granted service?
            If it is that how I could control what the app is asking to do the granted service?

        • Nikhil Aug 16, 2016 @ 17:48

          Hello Petri , thank you so much for the great article. I have been facing the issue from couple of days, couldnt find what going wrong. I am trying to login with github authentication and when i comes back from github app, the connection is always null. I dont have any object user.
          private UserService service;

          private final ProviderSignInUtils providerSignInUtils;

          @Autowired
          public RegistrationController(ProviderSignInUtils providerSignInUtils,UserService service) {
          this.service = service;
          this.providerSignInUtils = providerSignInUtils;
          }
          ------------------------------
          @Bean
          public ProviderSignInUtils providerSignInUtils(
          ConnectionFactoryLocator connectionFactoryLocator,
          UsersConnectionRepository connectionRepository) {
          return new ProviderSignInUtils(connectionFactoryLocator,
          connectionRepository
          );
          }
          Thank you for your help !

          • Petri Aug 20, 2016 @ 18:33

            Hi,

            Unfortunately I have never used Spring Social Github and that is why I don't know what could be wrong :( Have you tried asking help from StackOverflow?

  • shachi Jul 15, 2015 @ 22:51

    Hi Petri,
    i am using your application with xml configurations and have defined a web.xml with following servlet mapping.

    dispatcher
    *.hop

    However my spring security functions are not working properly, as in the code inside
    and
    are not getting executed, however if i comment the spring security tags things are working fine. can you please specify the issue, as i am not able to find one.

    regards,
    shachi

    • Petri Jul 15, 2015 @ 23:05

      Hi Shachi,

      However my spring security functions are not working properly, as in the code inside
      and are not getting executed, however if i comment the spring security tags things are working fine.

      What did you meant when you said that your Spring Security functions aren't working properly? If you meant that the Spring Security JSP tags aren't working, you should take a look at this discussion.

      • shachi Jul 16, 2015 @ 14:06

        Hi Petri,
        yes the related link is exactly the same problem i am facing. i am using a web.xml with just one servlet mapping. i have not mapped anything for spring security filter chain mapping or site mesh filter. However the springSecurityXml and sitemesh xml is the same as your code on git.
        Can you please help me with the configuration for web.xml.
        currently i am not able to use any spring security tags on jsp pages (i have included security libs on jsp pages).
        My web.xml just contains the following mapping.

        servlet
        servlet-name dispatcher servlet-name
        servlet-class org.springframework.web.servlet.DispatcherServlet servlet-class
        init-param
        param-name contextConfigLocation param-name
        param-value /WEB-INF/classes/dispatcher-servlet.xml param-value
        init-param
        load-on-startup 1 load-on-startup
        servlet

        servlet-mapping
        servlet-name dispatcher servlet-name
        url-pattern / url-pattern
        servlet-mapping

        (removed opening "" so that it is not considered as html).

        regards,
        shachi

        • Petri Jul 16, 2015 @ 16:23

          Are your filter mappings in the correct order? In other words, did you configure the Sitemesh filter after the Spring Security filter?

  • shagun Jul 16, 2015 @ 23:33

    Hi Petri,
    I tried the app with your git code. it works fine. there is just one small thing. i am not able to load any css for the whole app, all the designs are blank and i am not able to see any design on the app. i think there is something wrong with sitemesh configuration. here is my filter mapping of web.xml

    filter
    filter-name sitemesh filter-name
    filter-class org.sitemesh.config.ConfigurableSiteMeshFilter filter-class
    filter
    filter-mapping
    filter-name sitemesh filter-name
    url-pattern *.jsp url-pattern
    dispatcher REQUEST dispatcher
    dispatcher FORWARD dispatcher
    filter-mapping

    • Petri Jul 17, 2015 @ 0:00

      I cannot find any errors from your Sitemesh configuration. I noticed that you configure your web application by using web.xml (the example application uses Java config). Did you make any other changes to it?

      Also, which HTTP status code is returned when the browser requests the CSS file from the server?

  • black_glix Oct 9, 2015 @ 18:17

    Hi,

    Firstly thanks for the great document and example codes. What I want to ask is what if I want to allow my users connect their account with their social media accounts? For example, I have an account, and I want to connect it my facebook AND twitter accounts. What is the tips you can give me for this purpose?

    Thanks a lot again.

    • Petri Oct 12, 2015 @ 20:02

      Hi,

      Do you mean that the users should be able to log in by using both Facebook and Twitter accounts after they have connected their account with their social media accounts?

      If so, the user has to be logged in when she/he tries to connect her/his user account with her/his social media accounts because you need to store the correct userId to the UserConnection table. This is required even if you use email address as username because all social media services don't return the email address of the user.

      Of course, the user can still create a user account by using a social sign in provider, but she/he cannot connect it with another social media account unless she/he is logged in.

  • Keun Oct 15, 2015 @ 10:40

    Hi Petri,
    Thank you for your interest to write a good article .
    It is being applied to the project . Hit the obstacles I wanted one thing but the question to you.
    Create and apply during the spring social as xml. But the problem is not the action.
    As you talk to the older 1.1.2 version you should have set the providerSignInUtils as bean.
    Is this job include non- xml ?

    • Petri Oct 15, 2015 @ 19:18

      Hi,

      If you are using Java configuration, you can create the ProviderSignInUtils bean by following the instructions given in this comment.

      Or did you mean that you cannot create an XML configuration file that creates the ProviderSignInUtils bean?

      • keun Oct 26, 2015 @ 4:29

        Hi Petri,
        Thank you kindly answer . It doesnt set a bean , as you've said .

        Solved by setting the following:

        One problem has been resolved.
        May I still wonder it remains a question yet ?

        I would like to move to a specific url according to the examples and situations differently .

        I tried to make a success handler makes sense solved well .
        Would you ever go some way to set the target url after authentication through the spring social?

        Have a good day.
        Thank you.

        • keun Oct 26, 2015 @ 4:39

          Oh, this lack of explanation Incidentally little more .

          Although this method using a similar postLoginUrl over , I want the page to be displayed at the point as web login by clicking it wants to move to different places.

          • Petri Oct 27, 2015 @ 20:46

            I have to admit that I am not entirely sure what you are trying to do. If you want to configure the page in which the user is redirected after he/she has signed in, you have to use one of the following (or both) methods:

            • If you want to configure the page in which the user redirected after social sign in, you have to set the value of the postLoginUrl property.
            • If you want to configure the page in which the user is redirected after form login, you have to use an AuthenticationSuccessHandler. The section 12.4.1: Application Flow on Authentication Success and Failure of the Spring Security's Reference Manual provides more information about this.

            I hope that this answered to your question. If not, don't hesitate to ask additional questions!

  • Ramit Oct 21, 2015 @ 4:18

    Great tutorial,
    I want to remove sitemesh and integrate anjular js with the application how can we do that

    • Petri Oct 23, 2015 @ 22:08

      Thank you for your kind words. I really appreciate them.

      I want to remove sitemesh and integrate anjular js with the application how can we do that

      I asked this from the creator of Spring Social and he said that at the moment Spring Social doesn't have a "good" support for REST APIs. You need to do a normal GET request and let the backend handle the authentication dance.

  • Jeff Nov 19, 2015 @ 7:23

    Hello,

    No good ideas come to mind.
    How can I add Facebook information to existing user ID ?

    plz, some idea :D
    thx

    • Petri Dec 2, 2015 @ 18:15

      Unfortunately, I have to admit that I don't know how you can do this (without sacrificing the safety of your existing users). I did a fast Google search, but I couldn't find anything useful. It is surprising since this sounds like a pretty common use case.

  • Guilherme Dec 9, 2015 @ 2:54

    Petri,
    I would like to get some Facebook data other than the name, email. I want to get birthday, gender, and others. I put these params on the .jsp login facebook button to require the permission, and I'm trying to get these on createRegistrationDTO() using the object Connection, but I have no idea how can I call FacebookTemplate or othe object that retrieve these fields.. Can you suggest something?
    Thanks. Nice tutorial.

    • Petri Dec 9, 2015 @ 22:43

      Hi Guilherme,

      Spring.io has a tutorial titled: Accessing Facebook Data that describes how you can read user's profile from Facebook (it has an example application on Github as well). Does it help you to solve your problem?

      • Guilherme Dec 17, 2015 @ 5:30

        Thanks Petri. I solved the problem using other code with some changes over the spring tutorial.

        Connection connection = (Connection) providerSignInUtils.getConnectionFromSession(request);
        ConnectionKey providerKey = connection.getKey();
        User facebookProfile = connection.getApi().userOperations().getUserProfile();

  • Guilherme Dec 17, 2015 @ 5:42

    Petri,
    I want to redirect the user after the login to internal page, like a "dashboard". Actually is redirecting to front page.
    I tried to set 'authentication-success-handler-ref="myAuthenticationSuccessHandler"' on but didn't work. Didn't enter on the method "onAuthenticationSuccess". My bean refers to a 'SimpleUrlAuthenticationSuccessHandler' implementation. I was following this tutorial to do this (http://www.baeldung.com/spring_redirect_after_login). I tried too to put 'default-target-url="/dashboard"' on but didn't work.
    I just want redirect my user after pass through UserDetailsService or SocialDetailsService to my internal page. Can you suggest something?
    Thanks

    • Petri Dec 17, 2015 @ 9:40

      Hi,

      The SocialAuthenticationFilter class has a property called postLoginUrl. You have to set the value of this property.

      • Guilherme Dec 22, 2015 @ 2:41

        It's exactly what I needed Petri. Thanks again!

        • Petri Dec 22, 2015 @ 19:52

          You are welcome :)

  • ASIM Dec 19, 2015 @ 13:38

    Error while running unit tests.

    Hi
    I am worndking on a web application, new in spring and want to login using facebook or twitter account using spring.

    I have gone through your project in detail and want to learn things using it. Problem is when I run unit test I get error. Please let me know if you can figure it out.

    Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:386)
    FATAL ERROR in native method: processing of -javaagent failed
    at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:401)
    Caused by: java.lang.RuntimeException: Class java/util/UUID could not be instrumented.
    at

    Update: I removed the irrelevant part of the stack trace - Petri

    • Petri Dec 19, 2015 @ 18:05

      If you are using Java 8, you need to either update the JaCoCo Maven Plugin to the version 0.7.5.201505241946 or remove it from the pom.xml file.

  • jagadish Dec 24, 2015 @ 7:51

    Given URL is not permitted by the Application configuration: One or more of the given URLs is not permitted by the App's settings. It must match the Website URL or Canvas URL, or the domain must be a subdomain of one of the App's domains.

  • Jagadish Dec 28, 2015 @ 15:02

    Hi Petri,
    i am beginner for spring social . I have been observed the total example,every thing i am able to understand.But as part of socialUsersDetailService class why userDetailsService is taken as reference.Can we please elaborate the relation between those two classes .

    • Petri Dec 29, 2015 @ 9:50

      Hi Jagadish,

      The example application has a social sign in and a normal form login. This means that:

      • When the user signs in by using social sign in, Spring Social loads the logged in user by using the class which implements the SocialUserDetailsService interface.
      • When the user signs in by using social sign in, Spring Security loads the logged in user by using the class which implements the UserDetailsService interface.

      Also, the application uses the email address of the user as a userId which is saved to the UserConnection table. Because the email address is also the username of the user, the SimpleSocialUserDetailsService class can load it by using the functionality that is provided by the RepositoryUserDetailsService class.

      Did this answer to your question?

      • jagadish Dec 30, 2015 @ 6:52

        Hi Petri,
        Great tutorial.
        I understand the loading data from database.
        Can you please explain how the details are saving into database . How to differentiate form login and social login.

        • Petri Dec 31, 2015 @ 22:59

          The section: Implementing the Registration Function explains the registration flow and describes how you can implement the registration function.

          If you want only to find out how the information entered to the registration form is saved to the database, you should start from the section: Processing the Form Submissions of the Registration Form.

          • Jagadish Jan 2, 2016 @ 8:55

            Hi Petri,
            Thank you every much for your answer.
            Here i am unable to understand functionality of SignInUtils class .
            I have return providerSignInUtils.doPostSignUp(---,-----); to save details into user connection table .
            But the user details are not storing into user connection table.
            Thanks In advance.

          • Petri Jan 3, 2016 @ 19:30

            Do you mean that nothing is inserted to the UserConnection table when you submit the registration form? If so, is the created user account saved to the user_accounts table?

  • jagadish Dec 29, 2015 @ 7:53

    Hi Petri,
    i tried with the example which is provided in the git hub.I am getting Sql exception

    org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [select userId from UserConnection where providerId = ? and providerUserId = ?]; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'userId' in 'field list'
    org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:231)

    com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'userId' in 'field list'

    For first time getting access how we get the values from table.
    Any one please reply me.

    • Petri Dec 29, 2015 @ 9:56

      Hi Jagadish,

      Did you create the database by using the command: mvn liquibase:update -P dev before you started the application? Also, did you run the application by using the Jetty Maven plugin?

      If the answer to these questions is yes, this might be a compatibility problem between Spring Social and your MySQL version. What MySQL version do you use?

  • Guilherme Jan 4, 2016 @ 14:10

    Hi Petri.

    I created an dashboard to the user interact with the backend and the data of the system. I want to show some info for the user in the mainpage, and in your configuration page he can change some things of your account (by example: last name, date of birth, etc..).

    The best approach to show the info for the logged user is retrieving in the controller and sending to the JSP, like this? : http://www.baeldung.com/get-user-in-spring-security

    
    User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    String name = user.getUsername();
    
    

    Taking advantage of the first question, when I try to use tag, like this:

    Test

    I'm getting error, and I don't know what exactly is the error. See a part of the stacktrace:

    java.lang.NoSuchMethodError: org.springframework.security.web.context.support.SecurityWebApplicationContextUtils.findWebApplicationContext(Ljavax/servlet/ServletContext;)Lorg/springframework/web/context/WebApplicationContext;

    I declared on the top of JSP the taglib
    My spring security version is 4.0.3.RELEASE!

    Thanks again!
    Regards

    Update: I removed the irrelevant part of the stack trace - Petri

  • Armagan Jan 6, 2016 @ 3:38

    Hey Petri!

    Your tutorial really helps me! I keep following you! thank you! However, I have a problem. I tried so much to fix it but nothing helped me :(

    Here is my problem:

    SEVERE: Exception sending context initialized event to listener instance of class

    org.springframework.web.context.ContextLoaderListener
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class com.broccolinisoup.config.PersistenceContext: Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: javax/transaction/SystemException
    at

    Here is my pom:

    
    org.springframework.data
    spring-data-jpa
    1.9.2.RELEASE
    		
    org.eclipse.persistence
    eclipselink
    2.6.0
    		
    org.hibernate
    hibernate-core
    5.0.6.Final
    			
    org.hibernate
    hibernate-entitymanager
    5.0.6.Final
    		
    org.hibernate
    hibernate-annotations
    3.5.6-Final
    						
    	hibernate-jpa-2.0-api
    	org.hibernate.javax.persistence
    
    

    The most common solution in stackoverflow was exclude hibernate-jpa-2.1-api from the mvn dependencies because of the conflict. But it didn't help me.

    I am stuck. Could you help me?

    Update: I removed the irrelevant part of the stack trace and irrelevant dependencies - Petri

    • Petri Jan 6, 2016 @ 13:05

      I have not used Hibernate 5 yet, and I am not sure if this problem is related to it. However, you should remove the eclipselink and hibernate-annotations dependencies from your pom.xml. There are two reasons for this:

      1. If you use Hibernate as a JPA provider, you don't need Eclipselink.
      2. The hibernate-annotations dependency is not required anymore.

      You could take a look at the pom.xml file of my example application. It should help you to figure out the required dependencies (you can naturally update the dependency versions).

  • Tim Jan 17, 2016 @ 1:41

    Hi Petri,

    I am not receiving an email address for a facebook user. Do you have any ideas on why?

    Thanks,
    Tim

    • Petri Jan 17, 2016 @ 12:52

      Hi Tim,

      If you want to get user's email address from Facebook, you need to configure your application to request the email permission. If I remember correctly, you have to configure the value of the scope property of your FacebookConnectionFactory object.

      You can do this by using one of the following methods:

      • If you use XML configuration, follow the instructions given in this StackOverflow question.
      • If you use Java configuration, you can set the correct scope by invoking the setScope() method of the OAuth2ConnectionFactory class (The FacebookConnectionFactory class extends this class). If you use my example application, you can find the relevant configuration from the SocialContext class.

      Let me know if this did the trick.

  • John Jan 18, 2016 @ 9:12

    Hi Petri ,
    Thanks for great Tutorial.
    I have been completed half of the application.
    Your application simply says user contain only one role but from side user contains list of roles.
    How can i handle this task.Thanks in advance.

    • Petri Jan 19, 2016 @ 18:30

      Hi John,

      I would store the user roles in a separate table and add these roles into my user entity by using the @ElementCollection and @CollectionTable annotations. If you have any additional questions, don't hesitate to ask them!

  • John Jan 19, 2016 @ 14:18

    Hi Petri,
    I have completed total application.
    i had a doubt regarding welcome page.I kept index page as welcome page.
    when OAuth dance is completed,the request is coming back to index page.
    i have written home controller to map "/" request .
    please help me how to write login as welcome page in xml configuration.

    • Petri Jan 19, 2016 @ 18:33

      Hi John,

      Read this comment. It explains how you can configure the post login url by using XML configuration.

  • John Jan 20, 2016 @ 14:20

    Hi Petri
    Thank you very much. i have done with face book .Every thing working fine.
    Give me few guide lines how to provide google+ sign in.

    • Petri Jan 20, 2016 @ 19:02

      Hi John,

      Unfortunately I cannot provide an exact answer to your question because I have never used Google as an authentication provider. However, I found an example application that uses Spring Social Google.

      I assume that you should be able to use Google as an authentication provider by following these steps:

      • Add the Spring Social Google dependency into your pom.xml file.
      • Add a new GoogleConnectionFactory object into the used ConnectionFactoryRegistry.
      • Create a new sign in link that uses the "correct" url. I am not sure what the correct url is, but I assume that it is something like: /auth/google.

      Could you let me know if this did the trick.

      • john Jan 21, 2016 @ 8:46

        Hi Petri
        i have followed all your instructions still i am getting this error
        400. That’s an error.
        Error: invalid_request
        Missing required parameter: scope
        Could you please suggest me any other approach.

        • john Jan 21, 2016 @ 9:17

          Small improvement from previous exception
          I refer from stack over flow "the request should be post in case google sign in".
          here the error page after login into google is
          400. That’s an error.
          Error: redirect_uri_mismatch
          I have hope your experience will able to solve this exception.

          • john Jan 21, 2016 @ 12:35

            I apologize for many comments.
            Google redirect the user to /signin after accepting on the google page where you allow my application access to your google+ account. /signin is a 404.
            i hope your answer could helpful for me.

          • Petri Jan 21, 2016 @ 19:54

            Hi John,

            There is no need to apologize. The problem is that I don't know the answers to your questions because I have never used Spring Social Google. :( I tried to find some answers from Google as well, but I couldn't find anything useful.

          • John Jan 22, 2016 @ 7:29

            Hi Petri
            Thanks for your answer.In spring social client modules documentation i did not find google.
            Please find below link
            http://docs.spring.io/spring-social/docs/1.1.x/reference/htmlsingle/#table_clientModules
            May be is that the reason for our failure?

          • John Jan 22, 2016 @ 13:07

            Hi Petri
            I am Happy to say that i have completed the google sign in.
            Here /sign is given as default authentication failure in social authentication filter.
            I enabled few services in google developer console.
            Then remaining as usual.
            Thanks for helping to make this task complete.

          • Petri Jan 22, 2016 @ 19:11

            Hi John,

            I am happy to hear that you were able to solve your problem. Keep up the good work.

  • John Jan 27, 2016 @ 14:00

    Hi Petri,

    Thanks.
    I have another query "How to convert these implementation into rest full web services".
    i hope your answer could helpful for me.

    • Petri Jan 27, 2016 @ 20:22

      Hi John,

      Take a look at this comment.

      • John Jan 28, 2016 @ 7:21

        Hi Petri,
        i could not found any comment ,don't mind check once.

        • Petri Jan 28, 2016 @ 17:04

          That is weird. I tested the link with FF, Chrome, and Opera. It seems that for some reason Chrome doesn't open the correct comment :( You can find it by clicking the link and searching for the text: 'wojciech' (the alias of the person who asked the original question).

  • Guilherme Feb 27, 2016 @ 19:07

    Petri, What do you think about code the front-end using Thymeleaf instead of JSP?
    I'm thinking to migrate my projects to use thymeleaf..I read in the web a lot of stuffs saying that is much better to work with Thymeleaf, and JSP is too old.

    • Petri Feb 28, 2016 @ 16:47

      I have never tried Thymeleaf because nowadays I write mostly single page web applications that have a REST backend. That being said, I have noticed that Thymeleaf is becoming quite popular, and that is why I would evaluate it if I would have to write a "normal" web application.

      By the way, I don't think that you should give much value to opinions you read from the internet because you cannot know the motives of these persons. In other words, if something is working for you, you should use it.

      • Guilherme Feb 28, 2016 @ 23:21

        Cool. thanks for the answer. What frameworks are you using to write the single page applications nowadays? Are you combining the SPA with the Spring backend?
        I'm thinking to buy a administrator dashboard template and use Angular to work on the front-end. But I'm studying the ways. Maybe I can work with SPA instead of this..

  • Paweł Mar 2, 2016 @ 7:51

    Hi, I am trying to add google plus flavor to your application. Unfortunately after logging in the control is redirected to the LoginController page instead of RegistrationController.

    In the logs I could see the following warning.

    WARN - RestTemplate - GET request for "https://www.googleapis.com/plus/v1/people/me" resulted in 403 (Forbidden); invoking error handler

    then

    DEBUG - LoginController - Rendering login page.

    I have forked your code and add my changes and published it on GitHub

    https://github.com/PawelJagus/spring-social-examples/tree/master/sign-in/spring-mvc-normal

    I have also post a stackoverflow question but have not received many attention yet.

    http://stackoverflow.com/questions/35728924/spring-social-google-authentication-does-not-redirect-to-register-form-page

    I know that you are a busy man, but could you possibly look at it when you have some spare time?

    Thanks for this superb tutorial!

    Regards,
    Paweł

    • Petri Mar 3, 2016 @ 20:19

      Hi Pawel,

      I noticed from the StackOverflow question that you mentioned that you are redirected to the url: '/signin' after you have signed in with Google+. The thing is that another reader had the same problem, and he was able to fix it by enabling some services on Google developer console (unfortunately he didn't mention which services he enabled).

      In other words, I suggest that you enable all services on Google developer console and see if the Google+ sign in flow is working correctly. If it does work, you can disable these services one by one and figure out what services have to be enabled.

  • Maxi Wu Apr 8, 2016 @ 12:02

    Thank you Petri, I've read a lot spring social and spring security articles and yours is the best. I am trying to create the same application with spring boot, but without hibernate, just Jdbc, since I've learnt hibernate yet.

    I follow this tutorial first, http://callistaenterprise.se/blogg/teknik/2014/09/02/adding-social-login-to-a-website-using-spring-social/
    so far I understand the followings:
    1. use spring security and jdbc to create a local login application.
    2. login with spring social and read feed, just like spring social sample quickstart
    3. create local account with social information, login with social account.

    But there are a few problems which I still could not solve after reading this tutorial and the previous one. Here is my source code https://github.com/maxiwu/SocialLogin
    1. I look into your project, couldn't find any controller that handle login POST, does this mean by configuring spring security correctly, it handles it for us? My application needs me to explicitly write method to handle login POST which I learn from some spring social example.
    2. My project is not functioning properly, If I local in with a registered local user, it does render the /home, but the url is showing /login. and if I request for /home again, it bounces me back and render login page.
    If I login with social user, I am trying facebook, it does redirect me to facebook login and after login, gives me 403 error.
    I think this is caused by spring security not correctly configure, I just not yet figure out how.
    3. The spring social sample use a Form POST with /signin/facebook, with a hidden field name 'scope' which define facebook scope, for example email,user_friends,user_posts,user_about_me.
    But your sample is using hyperlink /auth/facebook without any scope. What is the difference between /signin/facebook and /auth/facebook? How do I defined scope with a hyperlink? Do I need to do so?

    • Petri Apr 10, 2016 @ 16:10

      Hi Maxi,

      I look into your project, couldn’t find any controller that handle login POST, does this mean by configuring spring security correctly, it handles it for us? My application needs me to explicitly write method to handle login POST which I learn from some spring social example.

      If you configure Spring Security to use form login, it will handle login requests for you. This example application uses configuration that is explained in this blog post.

      My project is not functioning properly, If I local in with a registered local user, it does render the /home, but the url is showing /login. and if I request for /home again, it bounces me back and render login page. If I login with social user, I am trying facebook, it does redirect me to facebook login and after login, gives me 403 error. I think this is caused by spring security not correctly configure, I just not yet figure out how.

      It seems that your project uses Spring Boot. Unfortunately I have never used Spring Social in a Spring Boot application, and that is why I don't know how you should configure your application. I recommend that you take a look at this example application that uses both Spring Boot and Spring Social.

      What is the difference between /signin/facebook and /auth/facebook?

      When I wrote this blog post, the SocialAuthenticationFilter processed requests send to the url: /auth/provider_id, and there was no way to configure this url. It is possible that the default url has been changed after I published this blog post OR it is now possible to configure it.

      How do I defined scope with a hyperlink? Do I need to do so?

      If you want to specify a scope, you need to transform the hyperlink into a form.

      • Maxi Wu Apr 11, 2016 @ 9:10

        Hi Petri, Your articles contain a lot of information, helps us understand more about how to use spring social and security. But I do not know liquibase, so I ran into a little problem. I am using mariadb instead of mysql, people say they are 100% compatible.

        I followed the readme on github. When did table like user_accounts get created? I execute the command 'mvn liquibase:update -P dev', two tables created databasechangelog and databasechangeloglock. I guess they are for liquibase.
        Then I execute 'mvn jetty:run -P dev', it gives me Missing table exception. So there is something wrong when I try to initialize database with liquibase?

        • Petri Apr 11, 2016 @ 20:27

          Hi Maxi,

          I followed the readme on github. When did table like user_accounts get created?

          My example projects creates them when you run the command mvn liquibase:update -P dev on command prompt. The Liquibase change log is found from the etc/db directory. However, nowadays I configure my Spring application to make the required changes to the database when the application is started. If you use Spring Boot, you should check out its reference documentation.

          I execute the command ‘mvn liquibase:update -P dev’, two tables created databasechangelog and databasechangeloglock. I guess they are for liquibase.

          You are correct. The databasechangelog table contains the Liquibase metadata that identifies the invoked change sets. The databasechangeloglock table is used to ensure that only one Liquibase instance can be running at one time.

          Then I execute ‘mvn jetty:run -P dev’, it gives me Missing table exception. So there is something wrong when I try to initialize database with liquibase?

          Yes, it seems so. However, it's kind of impossible to say what it is because I cannot see the Liquibase output. Could you add the output to pastebin.com?

  • Stefano Cazzola May 4, 2016 @ 16:40

    Hi Petri!
    Thanks for the excellent tutorial: detailed and clear!
    I just wanted to note that clearly Spring API have changed - weird, since the version I'm using is 1.1.4 (only build has changed, I didn't expect modifications to the contract).
    At any rate the static method ProviderSignInUtils.getConnection(Request) you mentioned isn't available any longer.
    For anyone else facing this change, here's the official doc: http://docs.spring.io/spring-social/docs/current/reference/htmlsingle/#signing-up-after-a-failed-sign-in
    Be aware that the method getConnection has been renamed in getConnectionFromSession

    • Petri May 4, 2016 @ 22:23

      Hi Stefano,

      You are right! I cannot remember anymore why they made these changes, but I have a vague memory that it had got something to do with session management. Anyway, thank you for your comment. I am sure that it will be useful to people who run into this problem.

  • Chabane Jun 30, 2016 @ 21:02

    Hi,

    Thank's for this tutorial,

    I have a Cross-Origin Request error when I am calling /auth/facebook or /auth/google. Do you know what's the problem please?

    • Petri Jul 1, 2016 @ 11:16

      Hi,

      Are you by any change invoking the request to /auth/facebook or /auth/google from a Javascript web application or is the request triggered by a normal link?

  • Testo Thunder Testosterone Booster Oct 11, 2016 @ 19:53

    I wanted to check up and allow you to know how , a great
    deal I treasured discovering your web blog today. I might consider it the honor to work at my workplace and be able to make real use of the tips contributed on your web-site
    and also take part in visitors' reviews like this.
    Should a position regarding guest article writer become on offer at your end, make sure you let
    me know.

  • Aditya Mittal Oct 17, 2016 @ 6:16

    Hi Petri,

    this is a very informative blog, and I am able to at least start with spring social now.

    I had setup this in an xml configuration, but as soon as I click on the facebbok button (/auth/facebook), it gives me a 404 error, I have spend a week no, but seems there is no way out to get rid of it. look like it is not finding the correct controller to handle /auth/facebbok request.

    can you please advice what am I doing wrong, or which controller will handle this?

    Thanks in advance.

    Aditya Mittal

    • Petri Oct 18, 2016 @ 11:29

      Hi,

      The url: '/auth/facebook' is processed by the SocialAuthenticationFilter class. If you get 404 error when you try to access it, your configuration might not be correct (it's hard to say for sure without seeing your code). You could check that you are using the configuration that is explained in this blog post.

      • Aditya Oct 20, 2016 @ 17:56

        Hi Petri,

        I followed your post to setup, and my spring-security-config.xml looks like below :

        My server starts fine, and the container is able to find all the dependencies and initialise them.
        Can you please have a look at the above config fine and advise, what am i missing?

        Thanks
        Aditya

        Update: Removed the XML configuration because Wordpress stripped all XML elements and the remaining part was unreadable - Petri

      • Aditya Oct 20, 2016 @ 19:08

        Hi Petri,

        Here is my spring security file :

        https://github.com/amittal89/Stock-Application/blob/master/spring-security-config.xml

        P.S. - Sorry for messing up this comments section, with those incomplete comments, i hope you can clear them for me.

        Thanks
        Aditya

        • Aditya Oct 22, 2016 @ 7:30

          Hi petri,

          After debugging spring classes, I found out that authProviders are coming null.
          SocialAuthenticationFilter.java class at line 172

          Set authProviders = authServiceLocator.registeredAuthenticationProviderIds();

          I cannot see this propert of 'SocialAuthenticationServiceRegistry' being set in you example.

          -Aditya

          • Petri Oct 24, 2016 @ 23:56

            Hi,

            First, I need to apologize that it took me so long to answer to your comment.

            I took a look at your XML configuration and noticed you don't configure the ConnectController bean. If you take a look at my Spring Social configuration, you should find that bean from the bottom of the XML file. You need to configure this bean because it creates connections to Social sign in providers and persists the connections by using the ConnectionRepository bean.

            I have to admit that I don't know if this solves your problem, but you might try it. Also, keep in mind that this tutorial is based on a very old version of Spring Social, and the newer versions might have some changes that are not compatible with my example. What Spring Social version are you using?

            I cannot see this propert of ‘SocialAuthenticationServiceRegistry’ being set in you example.

            As far as I know, you don't need to set this property if you want to use the default behavior.

  • Aditya Oct 28, 2016 @ 9:36

    Hi Petri,

    Thankyou for your response.
    I was using 1.1.4 version, but then switched to versions used in your example and I could solve this issue. I could move forward but still seem to be stuck.
    I have modified my security xml as follows :

    https://github.com/amittal89/Stock-Application/blob/master/spring-security-config.xml

    I have configured SocialContext implementing SocialConfigurer .

    Now my request goes to facebook, gives me a valid token, but i am getting error in
    org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository, where ConnectionSignUp object is null. I am not sure where to configure this bean, or do I need to write an implementation for this.

    You help is much appreciated.
    -Aditya

  • Bash Dec 18, 2016 @ 0:53

    Hi Petri,
    All my requests to the social login provider are going with a redirect_uri of localhost:8080 (I need it to point to my app).
    Do you have any pointers on how I can achieve this?

    • Petri Dec 22, 2016 @ 18:26

      Hi,

      Do you mean that the social sign in provider always redirects the user to localhost (even if the user tried to log in to an application that is deployed to another server)?

  • Chandan Dec 21, 2016 @ 21:41

    Hi Petri,

    Can you please look into this issue mentioned here and provide guidance?

    http://stackoverflow.com/questions/41270641/social-signin-using-spring-social-integrated-with-spring-security

    • Petri Dec 22, 2016 @ 18:23

      Hi,

      What would you like to do when the social sign in provider redirects user back to your service? You mentioned that you want to handle to code that is returned by Facebook. What do you want to do with it?

  • Patryk Mar 24, 2017 @ 13:18

    Hi Petri!

    What if wanted to create for example I'd choose react as client but as a server I'd choose Java. Then I'd like to show registration popup. How to pass registration data to controller ?

    • Petri Mar 27, 2017 @ 21:24

      Hi Patryk!

      The last time I checked, Spring Social didn't have "any support" for Javascript web applications. If you want to use Spring Boot, you have to basically use the backend sign-in and sign-up flows. That being said, I was able to find this blog post that explains how you can integrate React with Spring Social (kind of). I hope that it helps you to solve your problem.

  • vivek May 6, 2017 @ 14:28

    Hi Petri,

    thank you so much for great tutorial,while deploying in tomcat 8 i am getting error
    ERROR - ContextLoader - Context initialization failed
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class net.petrikainulainen.spring.social.signinmvc.config.PersistenceContext: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build EntityManagerFactory
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1553)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)

    • Petri May 8, 2017 @ 18:51

      Hi,

      Unfortunately your stack trace doesn't reveal the root cause of the problem. Can I see the entire stack trace?

  • Justinarors May 20, 2018 @ 0:34

    Many thanks, I enjoy this!

    • Petri May 22, 2018 @ 9:55

      You are welcome!

  • Amdrew J. Nov 21, 2018 @ 19:41

    Hello Petri,

    Thank you for tutorial. I was able to configure login via facebook. However I faced the issue with redirect after successful login.
    I tried to override ConnectController like described here https://stackoverflow.com/questions/13110813/overriding-default-redirect-view-in-spring-social/13111047
    however I discovered that ConnectController doesn't used at all, I removed its bean from social context but signin via facebook still works fine.

Leave a Reply