In the good old days users logged in by using the combination of username and password. Although nowadays some people still prefer the traditional way, a growing number of users want to sign in by using their social media accounts.
This is a what makes Spring Social (and its sub projects) an useful addition to the Spring project portfolio. However, integrating Spring Social with Spring Security has been a bit cumbersome.
Spring Social 1.1.0 changes all this. It provides seamless integration with Spring Security, and the Java configuration support of Spring Security makes the configuration feel like a walk in the park.
You don't have to take my word for it. Keep on reading and you will learn how this is done.
The requirements of our solution are the following:
- It must be possible to create an user account by using a normal registration form.
- It must be possible to create an user account by using a social sign in.
- It must be possible to login by using username and password.
- It must be possible to login by using a SaaS API provider.
- The application must support Facebook and Twitter.
- The application must use "regular" Spring MVC controllers (no REST).
Let's start by taking a look at the prerequisites of this tutorial.
Prerequisites
This tutorial assumes that you have already created the Facebook and Twitter application used by the example application. You can create these applications by following these links:
If you don't know how to do this, you can check out the following links:
- Facebook Developers - Creating an App Details Page (Select "website with Facebook login" when you are asked how your application integrates with FB).
- How to Create a Twitter App in 8 Easy Steps (Enable the "allow this application to be used to Sign in with Twitter" checkbox).
Let’s move on and find out how we can get the required dependencies with Maven.
Getting the Required Dependencies with Maven
The first thing that we have to do is to get the required dependencies with Maven. We can do this by declaring the following dependencies in our POM file:
- Spring Security (version 3.2.0.RELEASE).
- The core module contains core authentication and and access control components.
- The config module contains the code used to parse XML configuration files using the Spring Security XML namespace.
- The taglibs module contains the Spring Security JPS tag libraries.
- The web module contains filters and all other code related to web security.
- Apache HttpClient (version 4.3.2). Apache HttpClient is an optional dependency (but recommended) dependency of Spring Social. If it is present, Spring Social will use it as a HTTP client. If not, Spring social will use the standard Java SE components.
- Spring Social (version 1.1.0.RELEASE).
- The config module contains the code used to parse XML configuration files using the Spring Social XML namespace. It also adds support for Java Configuration of Spring Social.
- The core module contains the connect framework and provides support for OAuth clients.
- The security module integrates Spring Security with Spring Social. It delegates the authentication concerns typically taken care by Spring Security to service providers by using Spring Social.
- The web module contains components which handle the authentication handshake between our web application and the service provider.
- Spring Social Facebook (version 1.1.0.RELEASE) is an extension to Spring Social and it provides Facebook integration.
- Spring Social Twitter (version 1.1.0.RELEASE) is an extension to Social Social which provides Twitter integration.
The relevant part of the pom.xml file looks as follows:
<!-- Spring Security --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-core</artifactId> <version>3.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>3.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>3.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>3.2.0.RELEASE</version> </dependency> <!-- Use Apache HttpClient as HTTP Client --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.3.2</version> </dependency> <!-- Spring Social --> <dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-config</artifactId> <version>1.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-core</artifactId> <version>1.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-security</artifactId> <version>1.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-web</artifactId> <version>1.1.0.RELEASE</version> </dependency> <!-- Spring Social Facebook --> <dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-facebook</artifactId> <version>1.1.0.RELEASE</version> </dependency> <!-- Spring Social Twitter --> <dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-twitter</artifactId> <version>1.1.0.RELEASE</version> </dependency>
You might also want to read the following documents which give you more information about the dependencies of the frameworks discussed in this blog post (Spring Security and Spring Social):
Next we have to create a properties file for the configuration properties of our application. Let’s find out how this is done.
Creating the Properties File
We can create the properties file by following these steps:
- Create a file called application.properties and ensure that it is found from the classpath.
- Configure the database connection.
- Configure Hibernate.
- Add the Facebook application id and application secret to the properties file.
- Add the Twitter consumer key and consumer secret to the properties file.
The contents of the application.properties file looks as follows:
#Database Configuration db.driver=com.mysql.jdbc.Driver db.url=jdbc:mysql://localhost:3306/socialtwitter db.username=socialtwitter db.password=password #Hibernate Configuration hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect hibernate.format_sql=true hibernate.hbm2ddl.auto=validate hibernate.ejb.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy hibernate.show_sql=false #Facebook facebook.app.id=foo facebook.app.secret=bar #Twitter twitter.consumer.key=foo twitter.consumer.secret=bar
Before we can configure our application, we have to create a few common components. Let's find out what these components are, and how we can create them.
Creating the Common Components
We have to create three components which are used during the authentication process. These components are:
- We have create a class which contains the user details of an authenticated user.
- We have to create a class which implements the UserDetailsService interface. This class is used to load user information when the user uses form login.
- We have to create a class which implements the SocialUserDetailsService interface. This class is used to load user information when the user uses social sign in.
Let’s move on and find out how we can implement these classes.
Creating the User Details Class
We have to take the following requirements into account when we are creating the class which contains the user details of the authenticated user:
- The class which stores the user details of a user who uses form login must implement the UserDetails interface.
- The class which stores the user details of a user who uses social sign in must implement the SocialUserDetails interface.
Spring Social has a SocialUser class which fulfils both of these requirements. However, often we want to add application specific information to our user details class.
We can do this by following these steps:
- Create the user details class.
- Extend the SocialUser class.
- Add application specific fields to the created class. The application specific fields of our example application are: id, firstName, lastName, role, and socialSignInProvider.
- Create a constructor which takes the username, password and a collection of granted authorities as parameters. Pass these parameters forward to the constructor of the SocialUser class.
- Create getters for application specific fields.
- Add an inner builder class which is used to build new ExampleUserDetails objects.
The source code of our user details class looks as follows:
import org.apache.commons.lang3.builder.ToStringBuilder; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.social.security.SocialUser; import java.util.Collection; import java.util.HashSet; import java.util.Set; public class ExampleUserDetails extends SocialUser { private Long id; private String firstName; private String lastName; private Role role; private SocialMediaService socialSignInProvider; public ExampleUserDetails(String username, String password, Collection<? extends GrantedAuthority> authorities) { super(username, password, authorities); } //Getters are omitted for the sake of clarity. public static class Builder { private Long id; private String username; private String firstName; private String lastName; private String password; private Role role; private SocialMediaService socialSignInProvider; private Set<GrantedAuthority> authorities; public Builder() { this.authorities = new HashSet<>(); } public Builder firstName(String firstName) { this.firstName = firstName; return this; } public Builder id(Long id) { this.id = id; return this; } public Builder lastName(String lastName) { this.lastName = lastName; return this; } public Builder password(String password) { if (password == null) { password = "SocialUser"; } this.password = password; return this; } public Builder role(Role role) { this.role = role; SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.toString()); this.authorities.add(authority); return this; } public Builder socialSignInProvider(SocialMediaService socialSignInProvider) { this.socialSignInProvider = socialSignInProvider; return this; } public Builder username(String username) { this.username = username; return this; } public ExampleUserDetails build() { ExampleUserDetails user = new ExampleUserDetails(username, password, authorities); user.id = id; user.firstName = firstName; user.lastName = lastName; user.role = role; user.socialSignInProvider = socialSignInProvider; return user; } } }
The Role is a simple enum which specifies the "legal" user roles of our example 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 when user created an user account to our example application. Its source code looks as follows:
public enum SocialMediaService { FACEBOOK, TWITTER }
Implementing the UserDetailsService interface
We can create our own implementation of the UserDetailsService interface by following these steps:
- Create a class which implements the UserDetailsService interface.
- Add a UserRepository field to created class.
- Create a constructor which takes a UserRepository as a constructor argument and annotate the constructor with the @Autowired annotation.
- Implement the loadUserByUsername(String username) method of the UserDetailsService interface. The implementation of this method consists of following steps:
- Get the user by calling the findByEmail() method of the UserRepository interface. This method returns the user whose email matches with the username given as a method parameter.
- If the user is not found, throw a new UsernameNotFoundException.
- Create a new ExampleUserDetails object.
- Return the created object.
The source code of the RepositoryUserDetailsService class looks as follows:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; public class RepositoryUserDetailsService implements UserDetailsService { private UserRepository repository; @Autowired public RepositoryUserDetailsService(UserRepository repository) { this.repository = repository; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = repository.findByEmail(username); if (user == null) { throw new UsernameNotFoundException("No user found with username: " + username); } ExampleUserDetails principal = ExampleUserDetails.getBuilder() .firstName(user.getFirstName()) .id(user.getId()) .lastName(user.getLastName()) .password(user.getPassword()) .role(user.getRole()) .socialSignInProvider(user.getSignInProvider()) .username(user.getEmail()) .build(); return principal; } }
The UserRepository is a simple Spring Data JPA repository, and its source code looks as follows:
import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository<User, Long> { public User findByEmail(String email); }
The User is the only entity of our example application, and it contains the information of a user who has created user account to our example application. The relevant part of its source code 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; public User() { } //Getters and other methods are omitted for the sake of clarity. }
Implementing the SocialUserDetailsService interface
We can implement the SocialUserDetailsService interface by following these steps:
- Create a class which implements the SocialUserDetailsService.
- Add a UserDetailsService field to the created class.
- Create a constructor which takes a UserDetailsService object as a constructor parameter, and annotate the constructor with the @Autowired annotation.
- Implement the loadUserByUserId(String userId) method of the SocialUserDetailsInterface.
- Get the correct UserDetails object by calling the loadUserByUsername() method and pass the user id as a method parameter. We can do this because our application uses the username of the user as the user id.
- Cast the returned object to SocialUserDetails object and return it.
The source code of the SimpleSocialUserDetailsService class looks as follows:
import org.springframework.dao.DataAccessException; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.social.security.SocialUser; import org.springframework.social.security.SocialUserDetails; import org.springframework.social.security.SocialUserDetailsService; public class SimpleSocialUserDetailsService implements SocialUserDetailsService { private UserDetailsService userDetailsService; public SimpleSocialUserDetailsService(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } @Override public SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException, DataAccessException { UserDetails userDetails = userDetailsService.loadUserByUsername(userId); return (SocialUserDetails) userDetails; } }
That is all. We are now ready to configure the application context of our application. Let's find out how we can do that.
Configuring the Application Context
This section describes how we can configure the application context of our example application by using Java configuration. The application context configuration has been divided into multiple configuration classes by following these guidelines:
- Each configuration class contains configuration which is associated with a specific part of our example application. This make it easy to find out the relevant configuration if we have to check something out or change something a few months (or years) after we created the initial configuration.
- The configuration has been divided in a way which makes it easy to write unit tests for the web layer by using Spring MVC Test. We will talk more about this in the third part of this tutorial where we will write unit tests for the web layer of our application.
- The configuration makes it easy remove dependencies to external resources when we are writing integration tests for our application. We will talk more about this in the fourth part of this tutorial which describes how we can write integration tests for our application.
Let's start by configuring the persistence layer of our application.
Configuring the Persistence Layer
The persistence layer of our application stores the user account information and provides a way to access this information. This important for two reasons:
- We can provide a way to sign in by using username and password.
- We can store application specific information and link this information to the user who uses social sign in.
Let's find out how we can configure it by using both Java configuration class.
We can configure our persistence layer by following these steps:
- Create the configuration class and annotate the created class with the @Configuration annotation.
- Annotate the class with the @EnableJpaRepositories annotation and set the base package of our Spring Data JPA repositories.
- Enable the Spring transaction management by annotating the configuration class with the @EnableTransactionManagement annotation.
- Add an Environment field to the class and annotate the field with the @Autowired annotation. We don't need to configure the properties file by using the @PropertySource annotation because it is already configured in the "parent" application context configuration class.
- Configure the data source bean. This bean provides database connections to the entity manager but it has also another purpose. It is used by Spring Social when it persists connections to the database and loads them from the database.
- Configure the transaction manager bean.
- Configure the entity manager factory bean.
The source code of the PersistenceContext class looks as follows:
import com.jolbox.bonecp.BoneCPDataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.annotation.Resource; import javax.sql.DataSource; import java.util.Properties; @Configuration @EnableJpaRepositories(basePackages = { "net.petrikainulainen.spring.social.signinmvc.user.repository" }) @EnableTransactionManagement public class PersistenceContext { @Resource private Environment env; @Bean public DataSource dataSource() { BoneCPDataSource dataSource = new BoneCPDataSource(); dataSource.setDriverClass(env.getRequiredProperty("db.driver")); dataSource.setJdbcUrl(env.getRequiredProperty("db.url")); dataSource.setUsername(env.getRequiredProperty("db.username")); dataSource.setPassword(env.getRequiredProperty("db.password")); return dataSource; } @Bean public JpaTransactionManager transactionManager() { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); return transactionManager; } @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); entityManagerFactoryBean.setDataSource(dataSource()); entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); entityManagerFactoryBean.setPackagesToScan({ "net.petrikainulainen.spring.social.signinmvc.common.model", "net.petrikainulainen.spring.social.signinmvc.user.model" }); Properties jpaProperties = new Properties(); jpaProperties.put("hibernate.dialect", env.getRequiredProperty("hibernate.dialect")); jpaProperties.put("hibernate.format_sql", env.getRequiredProperty("hibernate.format_sql")); jpaProperties.put("hibernate.hbm2ddl.auto", env.getRequiredProperty("hibernate.hbm2ddl.auto")); jpaProperties.put("hibernate.ejb.naming_strategy", env.getRequiredProperty("hibernate.ejb.naming_strategy")); jpaProperties.put("hibernate.show_sql", env.getRequiredProperty("hibernate.show_sql")); entityManagerFactoryBean.setJpaProperties(jpaProperties); return entityManagerFactoryBean; } }
Let's move on and find out how we can create the security configuration for our application.
Configuring Spring Security
Spring Security provides authentication mechanism for users who uses either form login or social sign in, and it is also responsible of authorization.
We can configure Spring Security by following these steps:
- Create the configuration class and annotate the created class with the @Configuration annotation.
- Annotate the class with the @EnableWebSecurity annotation. This makes it possible to configure Spring Security by implementing the WebSecurityConfigurer interface.
- Ensure that our configuration class extends the WebSecurityConfigurerAdapter class which is a base class for creating WebSecurityConfigurer instances. After we have done this, we can customize the security configuration by overriding methods.
- Add an UserRepository field to the configuration and annotate the field with the @Autowired annotation.
- Override the configure(WebSecurity web) method of the WebSecurityConfigurerAdapter class. Ensure that Spring Security ignores requests made to static resources such as CSS and Javascript files.
- Override the configure(HttpSecurity http) method of the WebSecurityConfigurerAdapter class and implement it by following these steps:
- Configure form login by following these steps:
- Set the login page url to '/login'.
- Set the url which processes login form submissions to '/login/authenticate'.
- Set the login failure url to '/login?error=bad_credentials'.
- Configure the logout function by following these steps:
- Ensure that a cookie called JSESSIONID is deleted after logout.
- Set the logout url to '/logout'.
- Set the logout success url to '/login'.
- Configure url based authorization. The main point of this phase is to ensure that anonymous users can access all urls which are related to the sign in / registration process, and protect the rest of our application from anonymous users.
- Add the SocialAuthenticationFilter to the Spring Security filter chain. We can do this by creating a new SpringSocialConfigurer object and ensuring that this object is used when Spring Security is configured.
- Configure form login by following these steps:
- Configure the PasswordEncoder bean which is used to hash the password of the user (if the user uses form registration and login). We can do this by creating a new BCryptPasswordEncoder object and returning the created object.
- Configure the UserDetailsService bean. We can do this by creating a new RepositoryUserDetailsService object and passing the UserRepository as a constructor argument.
- Override the configure(AuthenticationManagerBuilder auth) method of the WebSecurityConfigurerAdapter class. We use this method for configuring authentication requests if the user uses form login. Implement this method by following these steps:
- Pass the UserDetailsService bean to the AuthenticationManagerBuilder object given as a method parameter.
- Pass the PasswordEncoder bean to the AuthenticationManagerBuilder object given as a method parameter.
- Configure the SocialUserDetailsService bean. We can do this by creating a new SimpleSocialUserDetailsService object and passing the UserDetailsService bean as a constructor argument. This bean loads the user specific data when social sign in is used.
The source code of our application context configuration class looks as follows:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.social.security.SocialUserDetailsService; import org.springframework.social.security.SpringSocialConfigurer; @Configuration @EnableWebSecurity public class SecurityContext extends WebSecurityConfigurerAdapter { @Autowired private UserRepository userRepository; @Override public void configure(WebSecurity web) throws Exception { web //Spring Security ignores request to static resources such as CSS or JS files. .ignoring() .antMatchers("/static/**"); } @Override protected void configure(HttpSecurity http) throws Exception { http //Configures form login .formLogin() .loginPage("/login") .loginProcessingUrl("/login/authenticate") .failureUrl("/login?error=bad_credentials") //Configures the logout function .and() .logout() .deleteCookies("JSESSIONID") .logoutUrl("/logout") .logoutSuccessUrl("/login") //Configures url based authorization .and() .authorizeRequests() //Anyone can access the urls .antMatchers( "/auth/**", "/login", "/signup/**", "/user/register/**" ).permitAll() //The rest of the our application is protected. .antMatchers("/**").hasRole("USER") //Adds the SocialAuthenticationFilter to Spring Security's filter chain. .and() .apply(new SpringSocialConfigurer()); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(userDetailsService()) .passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(10); } @Bean public SocialUserDetailsService socialUserDetailsService() { return new SimpleSocialUserDetailsService(userDetailsService()); } @Bean public UserDetailsService userDetailsService() { return new RepositoryUserDetailsService(userRepository); } }
Let's move on and find out how we can configure Spring Social.
Configuring Spring Social
Spring Social provides integrations with SaaS API providers such as Facebook and Twitter. We can configure Spring Social by following these steps:
- Create the application context configuration class which implements the SocialConfigurer interface and annotate the created class with the @Configuration annotation. The SocialConfigurer interface declares callback methods which can be used to configure Spring Social.
- Annotate the class with the @EnableSocial annotation. This enables Spring Social and imports the SocialConfiguration configuration class.
- Add a DataSource field to the configuration class and annotate the field with the @Autowired annotation.
- Add the addConnectionFactories() method of the SocialConfigurer interface to the created configuration class. This method takes two method parameters which are described in the following:
- The first parameter is a ConnectionFactoryConfigurer object which can be used to register connection factories.
- The second parameter is an Environment object which represents the environment in which our example application is running.
- Implement the addConnectionFactories() method by following these steps:
- Create a new TwitterConnectionFactory object, and pass the consumer key and the consumer secret as constructor arguments.
- Register the created TwitterConnectionFactory object by calling the addConnectionFactory() method of the ConnectionFactoryConfigurer interface. Pass the created TwitterConnectionFactory object as a method parameter.
- Create a new FacebookConnectionFactory object, and pass the application id and the application secret as constructor arguments.
- Register the created FacebookConnectionFactory object by calling the addConnectionFactory method of the ConnectionFactoryConfigurer interface. Pass the created FacebookConnectionFactory object as a method parameter.
- Add the getUserIdSource() method of the SocialConfigurer interface to the created class. The UserIdSource object returned by this method is responsible of determining the correct account id of the user. Because our example application uses the username of the user as an account id, we have to implement this method by returning a new AuthenticationNameUserIdSource object.
- Add the getUsersConnectionRepository() method of the SocialConfigurer interface to the created class. This method takes a ConnectionFactoryLocator object as a method parameter and returns a UsersConnectionRepository object.
- Implement the getUsersConnectionRepository() method by following these steps:
- Create a new JdbcUsersConnectionRepository object and pass the following objects as constructor arguments:
- The first argument is a DataSource object. We pass the value of the dataSource field as the first method parameter.
- The second argument is a ConnectionFactoryLocator object. We pass the value of the connectionFactoryLocator method parameter as the second method parameter.
- The third parameter is a TextEncryptor object which encrypts the authorization details of the connection established between a SaaS API provider and our application. We create this object by calling the noOpText() method of the Encryptors class. This means that our example application stores these details as plaintext. This is handy during the development phase but we should not use it in production.
- Return the created object.
- Create a new JdbcUsersConnectionRepository object and pass the following objects as constructor arguments:
- Configure the ConnectController bean. The method which configures this bean has two parameters. The first parameter is the ConnectionFactoryLocator bean. The second parameter is the used ConnectionRepository bean. Pass these parameters as constructor arguments when you are creating a new ConnectController object.
The source code of our configuration class looks as follows:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.*; import org.springframework.core.env.Environment; import org.springframework.security.crypto.encrypt.Encryptors; import org.springframework.social.UserIdSource; import org.springframework.social.config.annotation.ConnectionFactoryConfigurer; import org.springframework.social.config.annotation.EnableSocial; import org.springframework.social.config.annotation.SocialConfigurer; import org.springframework.social.connect.ConnectionFactoryLocator; import org.springframework.social.connect.ConnectionRepository; import org.springframework.social.connect.UsersConnectionRepository; import org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository; import org.springframework.social.connect.web.ConnectController; import org.springframework.social.facebook.connect.FacebookConnectionFactory; import org.springframework.social.security.AuthenticationNameUserIdSource; import org.springframework.social.twitter.connect.TwitterConnectionFactory; import javax.sql.DataSource; @Configuration @EnableSocial public class SocialContext implements SocialConfigurer { @Autowired private DataSource dataSource; @Override public void addConnectionFactories(ConnectionFactoryConfigurer cfConfig, Environment env) { cfConfig.addConnectionFactory(new TwitterConnectionFactory( env.getProperty("twitter.consumer.key"), env.getProperty("twitter.consumer.secret") )); cfConfig.addConnectionFactory(new FacebookConnectionFactory( env.getProperty("facebook.app.id"), env.getProperty("facebook.app.secret") )); } @Override public UserIdSource getUserIdSource() { return new AuthenticationNameUserIdSource(); } @Override public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) { return new JdbcUsersConnectionRepository( dataSource, connectionFactoryLocator, Encryptors.noOpText() ); } @Bean public ConnectController connectController(ConnectionFactoryLocator connectionFactoryLocator, ConnectionRepository connectionRepository) { return new ConnectController(connectionFactoryLocator, connectionRepository); } }
Our next step is to configure the web layer of our application. Let's get to work.
Configuring the Web Layer
We can configure the web layer of our application by following these steps:
- Create the configuration class by following these steps:
- Extend the WebMvcConfigurerAdapter class.
- Annotate the created class with the @Configuration annotation.
- Ensure that all controller classes are found by annotating the class with the @ComponentScan annotation and setting the base packages of our controllers.
- Enable the annotation driven web mvc by annotating the class with the @EnableWebMvc annotation.
- Ensure that static resources are served by container’s default servlet.
- Configure the static resources by overriding the addResourceHandlers() method of the WebMvcConfigurerAdapter class.
- Ensure that requests made to static resources are delegated forward to the container's default servlet. This is done by overriding the configureDefaultServletHandling() method of the WebMvcConfigurerAdapter class.
- Configure the exception resolver bean.
- Configure the ViewResolver bean.
The source code of the WebAppContext class looks as follows:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.servlet.view.JstlView; import java.util.Properties; @Configuration @ComponentScan(basePackages = { "net.petrikainulainen.spring.social.signinmvc.common.controller", "net.petrikainulainen.spring.social.signinmvc.security.controller", "net.petrikainulainen.spring.social.signinmvc.user.controller" }) @EnableWebMvc public class WebAppContext extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**").addResourceLocations("/static/"); } @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } @Bean public SimpleMappingExceptionResolver exceptionResolver() { SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver(); Properties exceptionMappings = new Properties(); exceptionMappings.put("java.lang.Exception", "error/error"); exceptionMappings.put("java.lang.RuntimeException", "error/error"); exceptionResolver.setExceptionMappings(exceptionMappings); Properties statusCodes = new Properties(); statusCodes.put("error/404", "404"); statusCodes.put("error/error", "500"); exceptionResolver.setStatusCodes(statusCodes); return exceptionResolver; } @Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setViewClass(JstlView.class); viewResolver.setPrefix("/WEB-INF/jsp/"); viewResolver.setSuffix(".jsp"); return viewResolver; } }
Let's find out how we can tie this all together and create a "parent" application context configuration class for our application.
Tieing It All Together
The last application context configuration class has three responsibilities:
- It configures general components used throughout our example application.
- It ensures that the service classes of our application are found during the classpath scan.
- It is the root application context configuration class of our application.
We can create this configuration class by following these steps:
- Create the configuration class and annotate the created class with the @Configuration annotation.
- Ensure that our service classes are found during the component scan by annotating the class with @ComponentScan annotation and setting the base package of our services.
- Import the other application context configuration classes by annotating the class with the @Import annotation.
- Annotate the class with the @PropertySource annotation, and configure it to look for a properties file called application.properties from the classpath. This ensures that the configuration properties can be accessed in the imported application context configuration classes.
- Configure the MessageSource bean.
- Configure the PropertySourcesPlaceholderConfigurer bean.
The source code the ExampleApplicationContext class looks as follows:
import org.springframework.context.MessageSource; import org.springframework.context.annotation.*; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.context.support.ResourceBundleMessageSource; @Configuration @ComponentScan(basePackages = { "net.petrikainulainen.spring.social.signinmvc.user.service" }) @Import({WebAppContext.class, PersistenceContext.class, SecurityContext.class, SocialContext.class}) @PropertySource("classpath:application.properties") public class ExampleApplicationContext { @Bean public MessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasename("i18n/messages"); messageSource.setUseCodeAsDefaultMessage(true); return messageSource; } @Bean public PropertySourcesPlaceholderConfigurer propertyPlaceHolderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } }
We have now configured the application context of our example application. However, we still have to configure our web application. Let's see how we can do this by using Java configuration.
Configuring the Web Application
Our last step is to configure our example application. We can do this without web.xml as long as our application is deployed to a servlet 3.0 compliant container.
We can configure the web application by following these steps:
- Create a class which implements the WebApplicationInitializer interface.
- Configure our application by overriding the onStartup() method of the WebApplicationInitializer interface. We can implement this method by following these steps:
- Create the root context of the application and register the ExampleApplicationContext class to the created root context.
- Configure the dispatcher servlet.
- Configure character encoding filter.
- Configure the Spring Security filter chain.
- Configure Sitemesh.
- Add the context loader listener to the servlet context.
The source code of the ExampleApplicationConfig class looks as follows:
import org.sitemesh.config.ConfigurableSiteMeshFilter; import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.context.support.XmlWebApplicationContext; import org.springframework.web.filter.CharacterEncodingFilter; import org.springframework.web.filter.DelegatingFilterProxy; import org.springframework.web.servlet.DispatcherServlet; import javax.servlet.*; import java.util.EnumSet; public class ExampleApplicationConfig implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext(); rootContext.register(ExampleApplicationContext.class); ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext)); dispatcher.setLoadOnStartup(1); dispatcher.addMapping("/"); EnumSet<DispatcherType> 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)); } }
What is Next?
We have now successfully configured our example application by using Java configuration. This tutorial has taught us two things:
- We learned how we can implement the components required by Spring Security and Spring Social.
- We learned to integrate Spring Security and Spring Social by using Java configuration.
The next part of this tutorial describes how we can add registration and login functions to our example application.
As always, the example application of this blog post is available at Github.
Looking forward to second part of tutorial...
I start writing it tomorrow. I think that I can publish it next week.
great post, helped me very much. I'm waiting for the next.
obrigado
I am happy to hear that I could help you out.
Petri, I have made a pause in a Spring article writing, but you inspired me to return to this =)
Hi Alexey,
It is good to hear from you! Also, continue writing Spring articles. :)
Great, I'm from Brazil and this post helped to understand spring's configuration.
congratulations
Thank you! I appreciate your kind words.
Hi Petri,
nice detail articular. i am struggling to get this working, appreciate you help.
1. i could not working, hence i configured below in xml then, at deployment it failed with below error
exception
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'socialAuthenticationFilter' defined in ServletContext resource [/WEB-INF/spring/LBSWeb/security-config.xml]: Unsatisfied dependency expressed through constructor argument with index 2 of type [org.springframework.social.connect.UsersConnectionRepository]: Could not convert constructor argume
nt value of type [com.sun.proxy.$Proxy198] to required type [org.springframework.social.connect.UsersConnectionRepository]: Failed to convert value of type 'com.sun.proxy.$Proxy198 implementing org.springframework.social.connect.ConnectionRepository,java.io.Serializable,org.springframework.
aop.scope.ScopedObject,org.springframework.aop.framework.AopInfrastructureBean,
org.springframework.aop.Spring
Proxy,org.springframework.aop.framework.Advised' to required type 'org.springframework.social.connect.UsersConnectionRepository'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [com.sun.proxy.$Proxy198 implementing org.springframework.social.connect.ConnectionRepository,java.io.Serializable,
org.springframework.aop.scope.ScopedObject,org.springframework.aop.framework.
AopInfrastructureBean,
org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [org.springframework.social.connect.UsersConnectionRepository]: no matching editors or conversion strategy found
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray
(ConstructorResolver.java:702)
hope you can help me out.
Hi Sam,
It seems that Wordpress ate your XML configuration. However, it seems that Spring cannot convert the constructor argument with index 2 to required type (
UsersConnectionRepository
). It is kind of hard to figure out what could be wrong without seeing the XML configuration file. Can you paste it to Pastebin?Also, have you compared the XML configuration of the example application to your application context configuration?
The configuration files which are relevant to you are: exampleApplicationContext-social.xml and exampleApplicationContext-security.xml.
Thank you for the quick revert.
i posted the xml on Pastebin ' SamJay - spring social xml configuration issue'. i am using spring.social.version>1.1.0.M4
actually i tried to make the configuration work as give by you. but for some reason social:jdbc-connection-repository did not work or recongnized, it failed to compiler with nodefine bean expectation for usersConnectionRepository, that's why i switched to xml configuration at first place.
i understand there are 2 place where usersConnectionRepository has being used (socialAuthenticationFilter and socialAuthenticationProvider ), i get the exception with socialAuthenticationProvider .
thank you.
For some reason I cannot find the XML from Pastebin by using the search term: 'SamJay – spring social xml configuration issue'. Could you provide a direct link to it?
By the way, this example assumes that you use Spring Social version 1.1.0.BUILD-SNAPHOT. The reason for this is that some classes which makes the configuration a lot simpler are not available in the version 1.1.0.M4.
here is the link Petri.
http://pastebin.com/uu9K2tJH
i did try with the as is configuration given by you but it still does not pick the social:jdbc-connection-repository so its failed at deployment to JBoss.
thanks.
The reason why you cannot use Java configuration if you deploy to JBoss is that JBoss doesn't support Spring Java configuration yet. Have you tried to deploy the application to Tomcat 7? It could be useful because this way you could figure out if this is a JBoss related problem.
I noticed that you don't set the value of the
index
attribute when you use theconstructor-arg
element. Have you tried to set it?Also, some of your configuration files use the old Spring versions (3.1). You should probably update them to the latest versions.
Have you tried to update your Spring Social version to 1.1.0.BUILD-SNAPSHOT? If you would do that, you should be able to use my versions of the XML configuration files. This would make your application context configuration files easier to read and less brittle.
thanks,
i am not using Java configuration at all only old faction xml, and adding constructor-arg element also made no difference.
i will deploy the app to the tomcat to eliminate the server.
below has simile issue being discussed, but could not really help.
http://forum.spring.io/forum/spring-projects/web/social/118162-exception-faced-while-migrating-social-s-java-based-configuration-style-to-xml-based
thanks
will write to you with update.
After I read that discussion, I realized that is probably an AOP related issue. I noticed a small difference between your XML configuration file and a file mentioned in that thread. Have you tried to declare the
JdbcUsersConnectionRepository
bean as follows:thanks, i tried that, but still get the same error,
one other think i tried to get the Spring Social version 1.1.0.BUILD-SNAPHOT from.
http://projects.spring.io/spring-social/core.html, but it also failed downloading the jar's.
dependencies>
org.springframework.social
spring-social
1.1.0.BUILD-SNAPSHOT
spring-snapshots
Spring Snapshots
http://repo.spring.io/snapshot
true
Hi,
You can get the correct dependencies from the Spring snapshot repository by following these steps:
Check the POM file of the example application for more details about this.
Also, It seems that the required modules are found from the snapshot repository. Perhaps the download failed because of a network issue or something.
Hi Petri,
deployment failed on tomcat 7 as well with same exception.
I was expecting that. The problem is related to Spring AOP and not to the used server. I noticed that you answered to this thread.
Let's hope that you get an answer soon (I want to hear what the problem was)!
Will keep you posted as soon as i get a answer.
on a different note can you please enplane the below please.
with the ConnectController, called back (redirect) into your app: GET /connect/facebook?code=xxx, which ends up with page not found.
how should i capture the call back here and seamlessly integrate with the app
If you want to redirect the user a specific page after the connection has been created, you should override the connectionStatusRedirect() method of the ConnectionController class.
Hi
do u know the reason for this error please
[ServerImpl] JBoss (Microcontainer) [5.0.1.GA (build: SVNTag=JBoss_5_0_1_GA date=200902231221)] Started in 1m:27s:96ms
[STDOUT] WARN : org.springframework.social.connect.web.ConnectController - Exception while handling OAuth2 callback (The OAuth2 'state' parameter doesn't match.). Redirecting to facebook connection status page.
I haven't run into this problem but I found some additional information it. You might want to check out the Github issue titled: Facebook connection infinite redirect loop workaround.
Hi Petri,
I have added /web-inf/jsp/js/app.js and web-inf/jsp/js/controller.js and updated
layout.jsp with below includes.
<script type="text/javascript" src="">
getting below errors in javascript console
Refused to execute script from 'http://localhost:8080/springsocialsignin/js/app.js' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled. login:1
Refused to execute script from 'http://localhost:8080/springsocialsignin/js/controller.js' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled. login:1
do i need to update webapp? any pointers??
sridhar
Hi Sai,
The problem is that tou put your Javascript files to WEB-INF/jsp/js/ directory, and servlet containers do not serve any content put to the WEB-INF directory.
You can solve this by moving your Javascript files to the src/main/webapp directory. If you use the same approach which I used in the example application, you should move the app.js and controller.js files to the src/main/webapp/static/js/app directory and add the following code to the layout.jsp file:
I hope that this answered to your question.
I think there is a dependency missing: spring-social-config
You are right! Thanks for pointing this out. It seems that the spring-social-config module is a transitive dependency but I agree that it might be better to explicitly specify it (at least in this case). I will update the blog post and the example application.
oops, forgot to say first, awesome article, thanks a lot for sharing :)
Thanks! I appreciate your kind words.
Hi petri, i'am newbie in spring. How to add your example source to my project in eclipse?
Thank you
Hi Davi,
I haven't been using Eclipse for several years but let's see if I can help you out. Which Eclipse version are you using?
hi Petri, What are you using for this project?
I use IntelliJ Idea, but the example application uses Maven. In other words, you can compile / package / run it without using an IDE. All you have to do is to clone it from the Github repository and you are ready to go (if you have installed JDK + Maven).
Thank you for your reply. I'm using eclipse kepler.
It seems that you should be able to do this by navigating to:
File -> Import -> Browse to general -> Maven Projects
This wiki page has more information about this (including screenshots).
I hope that this solved your problem.
Great, thank you. Great tutorial petri.
uhmm. It's should not be this complicated.
I agree. It will be interesting to see if Spring Boot will be integrated with Spring Social.
New to this , I am getting the following error when I import the code to the eclipse
Error loading property file '/Users/akumar/Documents/development/tracks/git/spring-social-examples/sign-in/spring-mvc-normal/profiles/dev/socialConfig.properties' (org.apache.maven.plugins:maven-resources-plugin:2.6:testResources:default-testResources:process-test-resources)
You need to create the
socialConfig.properties
file yourself. This file contains the configuration of your Facebook and Twitter applications. See the README of the example application for more details about this.Hello, im having huge problems adopting new facebook api to our application. Before i knew ill have to add it, i've created normal spring security besed User management. But now i have to add facebook. With new Social Security have been added XML based configuration with UserIdSource etc. But i've no idea how to use it. Could you be so nice and also create tutorial for XML based configuration that can be adopted to already existing spring security projects :( ?
Huge thx for all help.
Hi,
Have you checked out the XML configuration of the example application? It should help you get started if you want to configure your application by using XML.
I was planning to describe it in this blog post as well but I noticed that the blog post would probably be too long. That is why I decided to skip it and provided a link to the XML configuration instead.
If you cannot solve problem by reading the configuration files, let me know and I will write a brief description about the required steps.
Hi Petri,
I have managed to get the FB logging to work. Now i see that data are being populated to ‘userconnection’ table through ConnectController and when i disconnected from the service, the data from the table get deleted as well (hope the expected behavior).
My query is:
I have a table called ‘user’ which maintains the application form logging users information’s and authenticates via spring-security.
What i want to figure out is, i would like to sync userconnection data which maintains FB user data with ‘user’ table which maintain the form application local user accounts. So in a situation where a client logging with a FB, i should be able to create an account in the site as well and pass that information (ex: a passwrod) via a mail. So that user has the ability to either use FB or site account.
Can you please help me to understand am i thinking on the right direction? And what are the steps that i should do to achieve this.
Thanks
Sam
Hi Sam,
If your application uses email as username and you get the email address of the user from Facebook, you can create a row to the
user
table when a user signs in by using Facebook.The way I see this, you have two options:
The first option provides a better user experience. The problem is that you cannot use it if your application has to support social sign in providers which don't return the email address of the user or if you don't use email address as the username.
The second option is easier to implement but it can be annoying because often users expect that they don't have to enter password information if they use social sign in.
I hope that this answered to your question. If you need more advice about this, don't hesitate to ask additional questions.
HiPetri,
Thank you very much for the detail explanation & ill go through the links you provided and get back to you on the outcome. I do want to support FB, Twitter, Googal+, hence need to check whether email is being returned by those services. but my current implementation does not use email as the username, yet i am able to get the username with below.
Regarding the second point:
i am not clear on this, what do you mean is; at the end of authentication success, inject a page to capture a password, is it?
another query that i came across is,
once the FB authentication is successful, default behaviors is, the flow returns to the facebookConnected.jsp. what is the configuration (bean) to allow the flow to be continued to the application since the user is now authenticated ?
Thanks
Saranga
Hi Sam,
First, I am not sure if you have tried the example application of this blog post but its registration flow goes like this:
What I meant was that you could ask the password information from users who are using social sign in. If you want to know more about the registration flow of the example application, you should read my blog post titled Adding Social Sign In to a Spring MVC Web Application: Registration and Login.
Second, have you integrated Spring Social with Spring Security or are you using only Spring Social?
If you have integrated Spring Social with Spring Security, you can configure the
AuthenticationSuccessHandler
bean. If you are using Java configuration, you should take a look at this StackOverflow question.On the other hand, if you are using only Spring Social, you could try to set the url by calling the
setPostSignInUrl()
method of theProviderSignInController
class. I haven't tested this though so I am not sure if this is the right way to handle this.Hi Petri,
i am using the Spring Social with Spring Security and i have gone though your example which user xml, configuration.
i have posted my 2 xml file here for your reference. http://pastebin.com/P6u6cpyv (social-security xmls). hope it will make sense.
i am going through the clinks that you are given. i am stuck on what to do with when the authentication call back return to facebookConnected.jsp. i guess, i want to capture the callback and take the control to spring-security and let the application work flow proceed.
as you can see, i have used default spring provided controller, i guess i need to overwrite this and configure a way to let the flow run through the application flow.
thank you very much for your help.
thanks
saranga
Hi Sam,
I just realized something. Do you use the url /auth/[provider] (in other words, for Facebook this url would be /auth/facebook) to start the social sign in flow or do you use the url /connect/[provider]?
If you only want to authenticate the user, you should use the first url (/auth/[provider]) because requests send to that url are processed by the
SocialAuthenticationFilter
.I took a very quick look at your configuration files and they seemed to be fine.
I want to make a full example integrating spring social and spring security using MongoDB , i need some examples , links or tuorials that help me to achieve that. i don't know the needed changes to make in order to use mongodb instead of mysql because the problem that i faced is that Spring Social project already provides a jdbc based connection repository implementation to persist user connection data into a relational database. i don't know if this is only used by relational databases :(
Hi Moussi,
The
JdbcUsersConnectionRepository
class persist connection information to a relational database. If you want to save this information to MongoDB, you have implement theUsersConnectionRepository
interface.I have never done this but I found a blog post titled Customize Spring Social Connect Framework For MongoDB (unfortunately this blog post has been removed). It explains how you can persist connection information to MongoDB. You can get the example application of this blog post from Github.
I hope this helps you to achieve your goal!
I am looking forward on using this solution for testing in my environment.
As i have had no contact yet with sitemesh, here's my question.
How would i do sth. like this:
https://groups.google.com/forum/#!topic/sitemesh3-users/KjxbrtC0fFI
I think this work should be done in ExampleApplicationConfig, but i am stuck with this.
Is there some easy solution to add things like ?
Hi there, forget about my last post.
I made a small change in "ExampleApplicationConfig" on setting up the sitemesh
FilterRegistration.Dynamic sitemesh = servletContext.addFilter("sitemesh", new TagBundlerFilterForSite());
sitemesh.addMappingForUrlPatterns(dispatcherTypes, true, "*.jsp");
While adding the new Class to the same package:
public class TagBundlerFilterForSite extends ConfigurableSiteMeshFilter {
public TagBundlerFilterForSite(){
this.applyCustomConfiguration(new SiteMeshFilterBuilder());
}
@Override
protected void applyCustomConfiguration(SiteMeshFilterBuilder builder) {
builder.addTagRuleBundle (new DivExtractingTagRuleBundle ());
}
}
I can now do this:
Template:
JSP-Page:Some more data
for me this really helps to use my template well, maybe u or others can use that.
I know the setup on the constructor is a bad thing, but as i really tried to get this working, i was happy for now. If there is a better solution, let me know ! :)
It is great to hear that you were able to solve your problem!
Also, thanks for coming back and posting your solution to my blog. Now I know where to look if I need similar functionality (I have never needed it yet).
Heh,
This is really an awesome post.This will help me a lot.
Can you please mail me the zip file of the complete code? I tried to copy the code and and run, but it's not working. Am trying to remove errors since last 3 days, but not able to do so.
Please help me out.
Mail me as soon as possible.
You can get the code from Github (you can either clone the repository or download the code as a Zip file). Remember to read the README as well.
Hi petri,
I have one doubt.How to set the anonymous user for authentication without xml configuration?
Hi,
If you are talking about the anonymous authentication support of Spring Security, it should be enabled by default. The default role of the anonymous user is ROLE_ANONYMOUS, and you can use this role when you specify the authorization rules of your application.
Unfortunately I don't how you can customize the anonymous authentication support without using XML configuration. :(
Hello! I'm trying to follow this tutorial, but I have a problem downloading the dependencies.
Can you help me out?
Thanks
The following artifacts could not be resolved: org.springframework.social:spring-social-config:jar:1.1.0.BUILD-SNAPSHOT, org.springframework.social:spring-social-core:jar:1.1.0.BUILD-SNAPSHOT, org.springframework.social:spring-social-security:jar:1.1.0.BUILD-SNAPSHOT, org.springframework.social:spring-social-web:jar:1.1.0.BUILD-SNAPSHOT, org.springframework.social:spring-social-facebook:jar:1.1.0.BUILD-SNAPSHOT: Failure to find org.springframework.social:spring-social-config:jar:1.1.0.BUILD-SNAPSHOT in http://repo.springsource.org/libs-milestone was cached in the local repository, resolution will not be reattempted until the update interval of spring-milestone has elapsed or updates are forced
Hi Roman,
It seems that I forgot to mention that you have to add the Spring Snapshot repository to the pom.xml file (see the pom.xml file of the example application).
Also, you might want to use version 1.1.0.RC1 because it was released this week (I will update the example during the weekend).
I hope that this answered to your question. If not, let me know!
Please let me know if I'm misunderstanding, but it appears that this application permits a user to associate exactly one social-media account--of any type--with their application account, so that a user can't associate both Facebook and Twitter accounts simultaneously. It appears that the SocialUserDetails interface has a massive flaw in that its getUserId() method takes no parameter specifying *which* social service for which we're looking up the user's identity. Did I overlook some out-of-band information to the persistence layer about which social network is being talked about (such as an injectable thread-local holder), or does is this entire setup limited to a single social-media association per user?
You are right. If a user creates a user account by using Facebook, he cannot sign in by using Twitter (or associate a Twitter account with his local user account). However, it is possible to support multiple social sign in providers as well.
I haven't done this myself but I think that you can do this by following these steps:
User
entity and add support for multiple social sign in providers (this is required only if you want to store this information).UserIdSource
interface and create a userId which contains the information required to identify the local user and the used social sign in provider.SocialUserDetailsService
interface and ensure that it can handle the "new" userId (you have to parse the local username from userId and load the correct user).ProviderSignInUtils
class.If you have any further questions, don't hesitate to ask them.
Petri,
I was looking around on the web to find out how to change the scope when doing a Facebook login authorization. I was looking to make the change in the configuration rather having to send a post with a hidden variable on the social login button.
I see in SocialContext that we are adding the Facebook connection factory and it has a method to set the scope. I changed the scope and it does not change the scope on the authorization. Do you know how to change this at the configuration level?
There is an "Authorization scope" section that explains how it is done but not at the configuration level.
http://docs.spring.io/spring-social/docs/1.0.3.RELEASE/reference/html/connecting.html
Have you done this before?
I was able to verify that setting the scope by calling the
setScope(String scope)
method of theOAuth2ConnectionFactory
class (FacebookConnectionFactory
extends this class) doesn't seem to do anything.Unfortunately I cannot provide an answer right away, but I promise that I will take a closer look at this because the Javadoc of that method suggests that it should work. I will let you know if I find a solution.
I looked around tonight and did not find a way to set the scope but I did find that OAuth2AuthenticationService.defaultScope is really what is being used when it adds the scope to the URL. If you don't pass a scope as a hidden variable it will use the defaultScope.
Thanks again for always being so helpful.
Petri,
Have you found a work around for this? I have not. I tried looking through the source code and kept getting a little lost and did not find a path to set the scope.
Hi,
Actually I did found something from the web:
It seems that if you want to set the default scope, you have to use the
FacebookAuthenticationService
class.The
SecurityEnabledConnectionFactoryConfigurer
object given as a method parameter to theaddConnectionFactories()
method of theSocialConfigurer
interface creates the required authentication service objects BUT it doesn't set the default scope.I assume that if you want to set the default scope, you have remove the
@EnableSocial
annotation from theSocialContext
class and create all required beans manually.I can take a closer look at this tomorrow.
I checked this out and I noticed one problem:
I should create a
ConnectionRepository
bean but I am unable to do it because theConnectionRepository
class is package-private (and I don't want to move theSocialContext
class to the same package).You could of course configure Spring Social by using XML configuration but if you don't want to do that, you should create a new issue to the Spring Social project.
Petri,
Thanks again. I created a ticket. With your help I hope they have enough information to find the bug. I am a little lost.
https://jira.spring.io/browse/SOCIAL-436
You are welcome! Let's hope that the Spring Social team can solve this soon.
great post, helped me very much.
I am happy to hear that.
Hi Petri,
I'm trying to implement spring-social-facebook in my application, however I'm stuck in the JdbcUsersConnectionRepository part. I would like to have my own UsersConnectionRepository using Hibernate but without JPA.
Thanks much in advance
The
JdbcUsersConnectionRepository
class uses the JDBC API. In other words, you can use it in an application which uses the "native" Hibernate API.You can of course create your own implementation by implementing the
UsersConnectionRepository
interface but I am not sure if it is worth the effort.Did you mean that you want to create a custom
UserDetailsService
which uses Hibernate instead of Spring Data JPA?HI Petri
At the end of this tutorial I have an error with the servletContext.addServlet, servletContext.addFilter and servletContext.addListener ... I´m workinh with Eclipse and the message that appear is "The method addListener(ContextLoaderListener) is undefined for the type ServletContext".
The solution tath Eclipse suggest me is "Add Cast to servletContext"
What can I do?
Thanks much in advance
You need to use the Servlet API 3.0. You can get the full list of required dependencies by reading the
pom.xml
file of the example application.Hi Petri,
Thank you very much for the detailed tutorials!
I have a question that is similar to the one from Ademir. I would like to integrate spring social into my project however I don't need any of the spring social persistence stuff and just seems to conflict with my application. All I really need is Spring Social's Facebook methods. Is this possible to simplify the setup in this way?
Any help would be greatly appreciated!
Hi,
Do you want that your application is able to read information from Facebook and write information to Facebook (and that is it)? If that is the case, you should read a tutorial titled Accessing Facebook Data. It should help you to get started.
If that guide doesn't solve your problem, let me know!
hello my friend,
i have a trouble to run this sample, in "" class on line 68 we have
.apply(new SpringSocialConfigurer())
but i get can't resolve method ! i'm sure that i provide all maven dependencies correctly,
then i tried to upgrade the "spring.security.version" to 3.2.4.RELEASE but the problem remains.
whats the problem ?
thanks.
Are you getting a compilation error or a runtime error? Also, if you get a runtime error, it would be useful to see the stacktrace.
hello again,
when i start packaging app by using maven directly, i get rid of my first question, because that was just an IDE wrong alert.
but now i have another problem after returning from facebook auth, the page redirected into
http://localhost:8080/signin#_=_
and i an error 404 will raise.
of course i can't find any controller matching /signin url
why!?
whats the problem you think ?
thank you
Hi, Petri
can you help me on my question ?
thanks
Hi,
Yes, I was wondering if your previous problem was an IDE alert because I used to see a similar alert in IntelliJ Idea. However, it disappeared after I updated it to a newer version.
I assume that your problem occurs when a registered user tries to log in to your application (because of the url). If this isn't the case, let me know.
Anyway, you can configure the url in which the user is redirected after a successful login by following the instructions which I gave in this comment.
Let me know if this doesn't solve your problem.
here is my changes
in http:/localhost:8080/login i click on Sign in With facebook then i redirected to
facebook and have a successful login but it still redirected to
http:/localhost:8080/signin#_=_
here is statcktrace
DEBUG - LoginController - Rendering login page.
DEBUG - RequestAddCookies - CookieSpec selected: best-match
DEBUG - RequestAuthCache - Auth cache not set in the context
DEBUG - ttpClientConnectionManager - Connection request: [route: {s}->https:/graph.facebook.com:443][total kept alive: 0; route allocated: 0 of 5; total allocated: 0 of 10]
DEBUG - ttpClientConnectionManager - Connection leased: [id: 0][route: {s}->https:/graph.facebook.com:443][total kept alive: 0; route allocated: 1 of 5; total allocated: 1 of 10]
DEBUG - MainClientExec - Opening connection {s}->https:/graph.facebook.com:443
DEBUG - ttpClientConnectionManager - Connecting to graph.facebook.com/173.252.112.23:443
DEBUG - anagedHttpClientConnection - http-outgoing-0: Shutdown connection
DEBUG - MainClientExec - Connection discarded
DEBUG - anagedHttpClientConnection - http-outgoing-0: Close connection
DEBUG - ttpClientConnectionManager - Connection released: [id: 0][route: {s}->https:/graph.facebook.com:443][total kept alive: 0; route allocated: 0 of 5; total allocated: 0 of 10]
sorry Petri,
can i ask what url you expected to called after return from facebook ?
which controller must catch the request and how can i get auth_token to get all friends of logged in user ?
i have so many quastions, but at first i need to run application properly,
i googling so much for some other examples but yours is best article ever.
thanks again.
No problem. I happy to help but I am on summer holiday at the moment so my response time might be a bit longer than in a normal situation.
Anyway, if you implement the registration and login functions as described in this blog post, the only urls you should care about are:
I have never experienced a situation where the user would have been redirected to the '/signin' url, so I am not sure how you can solve this problem (The log you added to your comment doesn't reveal anything unusual).
I think that the easiest to way to solve this problem is to compare the configuration of your application with the configuration of my example application. Unfortunately it is impossible to figure out the root cause without seeing the source code of your application.
About your second question: I have never used Spring Social Facebook for accessing user's Facebook data (such as list of friends), so I don't know how you can do it. I think that your best bet is to read this guide which explains how you can create a simple web application that reads data from Facebook.
excuse me Petri,
i found that my server doesn't have an access to graph.facebook.com:443
this make's the problem, after i resolve this issue now have a null pointer exception at
org.springframework.social.security.SocialAuthenticationFilter.doAuthentication(SocialAuthenticationFilter.java:301)
Authentication success = getAuthenticationManager().authenticate(token);
and getAuthenticationManager(). return null value !
do you have any suggestion ?
thanks you for your replies,
Have you configured the
AuthenticationManager
bean?You can do this by overriding the
protected void configure(AuthenticationManagerBuilder auth)
method of theWebSecurityConfigurerAdapter
in theSecurityContext
class (assuming that your configuration is similar than mine).finally, all thing worked together successfully.
thanks for all of your advises.
have a good holidays.
Thanks! It is good to hear that you were able to solve your problem.
Hi in RepositoryUserDetailService I am getting error "The method getBuilder() is undefined for the type ExampleUserDetails"...Please Help anyone..
Some of the methods of the
ExampleUserDetails
class were left out from this blog post because I wanted to shorten the code listings. ThegetBuilder()
method was one of them.You can get the source code of the
ExampleUserDetails
class from Github.hello Peter.. thank your for this article.. can u create a video tutorial for this article..?
Hi,
That is a great idea. I will add this to my Trello board, and I will record the video tutorial in the future.
I'm using your example and my question is where the method is implemented
public User findByEmail(String email);?? i dont see (interface UserRepository.class)
The
findByEmail(String email)
is a custom query method which is found from theUserRepository
interface. This has been explained in the section titled 'Implementing the UserDetailsService interface'. You can also get the source code of theUserRepository
interface from Github.yes, but I do not see where is the implementation of the method, see the calling but where is the implementation?
I really doubt that userRepository class implements the interface for the method to work findByEmail
sorry for my English
thanks!!
The example application uses Spring Data JPA which provides a proxy implementations for each interface which extends the
Repository
interface. That is why there is no need to implement theUserRepository
interface.Hi Petri,
I in the process of implemented Spring Security & Spring Social for the website and also would like to allow for the iOS and Android apps to connect via the social as well. Idea i had in mind is to have the centralised api expose in web end and let it handle the social signup/sing in process where mobile end only connect to this api.
Can you please help me on modelling such and how should i go on about this.
Thanks
Hi Sam,
I have never done this myself (surprising but it is the truth).
However, I found an answer to StackOverflow question titled 'Integrate app with spring social website' which looks quite promising. The idea is that you have to first implement the social sign in by using the native APIs (Android, iOS) and then provide the required credentials to your backend and log the user in.
I am planning to extend my Spring Social tutorial to cover REST APIs in the future. I will probably take a closer look at this when I do that.
Thanks Petri. Look forward to it.
Hi Petri,
Did you have a chance to add REST API support in this example?
Hi Armen,
No. I will update my Spring Data JPA and Spring MVC Test tutorials before I will update this tutorial. If everything goes as planned, I will do it at November or most likely at December.
Actually I asked this from the creator of Spring Social because one of my readers asked the same question, and he said that at the moment the best way to do this is to do a normal GET request and let the backend handle the authentication dance.
Hi Petri,
Thank you for your quick response. I tried working with maven but facing below issue. Can you please help me to look into this issue.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.906 s
[INFO] Finished at: 2014-07-09T15:47:17+05:30
[INFO] Final Memory: 13M/154M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:2.6:resources (defau
lt-resources) on project spring-mvc-normal: Error loading property file 'F:\workspace\login\profile
s\dev\socialConfig.properties' -> [Help 1]
[ERROR]
Hi Amit,
the Maven build is a bit different than the setup described in this blog post. It expects to find
socialConfig.properties
file from theprofiles/dev/
directory. This properties file contains the configuration of your Facebook and Twitter application (app id and app secret).Also, the example application doesn't contain this file. This means that you have to create it yourself.The README of the example application explains how you can set up the example application before you can run it.
I hope this answered to your question. If not, feel free to ask more questions!
I deployed the project but not able to access registration page and also authentication page is not available. can you please provide me complete setup of this project to learn more.
Did you follow the steps described in the README of the example application?
If you did follow them, could you check the log and paste the relevant part of it here (also, if you see any stack traces, add them here as well)?
I followed the steps as you have mentioned and getting the login page. But when i tried to login my url is this "http://localhost:8080/spring-social-normal-mvc/login" and after submitting the URL it redirects to "http://localhost:8080/login". So, I feel the problem is redirecting the URL. It's not redirecting properly. Also, After login with facebook the url redirects to "http://localhost:8080/spring-social-normal-mvc/signin#_=_" and it shows 404 error. Please let me know if i have to update anything more in configuration.
The reason for this is that the
action
attribute of the login form ignores the context path. You can fix this by replacing theform
tag with this one:I made the fix to the example application. Thank you for reporting this bug.
Another reader had the same problem, and the reason for this behavior was that his server didn't have access to the Facebook Graph API.
Thank you Petri. It works.... cheers :)
I tried as mentioned above but still it redirects to same url. I pasted my console log. Please have a look and help me to fix this issue.
DEBUG - ttpClientConnectionManager - Connection leased: [id: 0][route: {s}->https:/
/graph.facebook.com:443][total kept alive: 0; route allocated: 1 of 5; total alloca
ted: 1 of 10]
DEBUG - MainClientExec - Opening connection {s}->https:/graph.facebook
.com:443
DEBUG - ttpClientConnectionManager - Connecting to graph.facebook.com/31.13.74.128:
443
DEBUG - anagedHttpClientConnection - http-outgoing-0: Shutdown connection
DEBUG - MainClientExec - Connection discarded
DEBUG - anagedHttpClientConnection - http-outgoing-0: Close connection
DEBUG - ttpClientConnectionManager - Connection released: [id: 0][route: {s}->https
:/graph.facebook.com:443][total kept alive: 0; route allocated: 0 of 5; total allo
cated: 0 of 10]
DEBUG - LoginController - Rendering login page.
DEBUG - RequestAddCookies - CookieSpec selected: best-match
DEBUG - RequestAuthCache - Auth cache not set in the context
DEBUG - ttpClientConnectionManager - Connection request: [route: {s}->https:/graph
.facebook.com:443][total kept alive: 0; route allocated: 0 of 5; total allocated: 0
of 10]
DEBUG - ttpClientConnectionManager - Connection leased: [id: 1][route: {s}->https:/
graph.facebook.com:443][total kept alive: 0; route allocated: 1 of 5; total alloca
ted: 1 of 10]
DEBUG - MainClientExec - Opening connection {s}->https:/graph.facebook
.com:443
DEBUG - ttpClientConnectionManager - Connecting to graph.facebook.com/31.13.74.128:
443
DEBUG - anagedHttpClientConnection - http-outgoing-1: Shutdown connection
DEBUG - MainClientExec - Connection discarded
DEBUG - anagedHttpClientConnection - http-outgoing-1: Close connection
DEBUG - ttpClientConnectionManager - Connection released: [id: 1][route: {s}->https
:/graph.facebook.com:443][total kept alive: 0; route allocated: 0 of 5; total allo
cated: 0 of 10]
Your log file looks pretty similar than farziny's log.
His problem was that his server could not access the Facebook Graph API. I am not exactly sure what he meant by that, but I assumed that either his FB application was not configured properly, or the app secret and app key were not correct.
When you created the FB Application for your web application, did you enable the Facebook login?
Hi Petri,
As, you said its blocking the facebook graph api, you were correct. I fixed the problem and working fine everything. You rocks dude...
Trying to implement to get the post feed of user and friend list also. May be your help required in future again. So, thanks a lot in advance.
Hi Amit, Petri,
Even I am getting the same error you specified earlier. After the Facebook login, the control is not returning back to login page/signUp page. Do you know how to resolve this/ Enable the Facebook graph API on the server?
DEBUG - headers - http-outgoing-1 << Access-Control-Allow-Origin: *
DEBUG - headers - http-outgoing-1 << X-FB-Rev: 1653508
DEBUG - headers - http-outgoing-1 << ETag: "02e90b73697f1bf84bb1c08a06c30817978e2ff1"
DEBUG - headers - http-outgoing-1 << Pragma: no-cache
DEBUG - headers - http-outgoing-1 << Cache-Control: private, no-cache, no-store, must-revalidate
DEBUG - headers - http-outgoing-1 << Facebook-API-Version: v2.0
DEBUG - headers - http-outgoing-1 << Expires: Sat, 01 Jan 2000 00:00:00 GMT
DEBUG - headers - http-outgoing-1 << X-FB-Debug: KntgReJ8rZbpdGdWOho0pLPgYBPEpFQei1a+jQNDJJBs+qoE6Sx9pBiHGMk0MsA5NEv6oa0uEv5ABrrVqMwgJg==
DEBUG - headers - http-outgoing-1 << Date: Sun, 22 Mar 2015 18:07:53 GMT
DEBUG - headers - http-outgoing-1 << Connection: keep-alive
DEBUG - headers - http-outgoing-1 <https://graph.facebook.com:443%5D can be kept alive indefinitely
DEBUG - ttpClientConnectionManager - Connection released: [id: 1][route: {s}->https://graph.facebook.com:443%5D%5Btotal kept alive: 1; route allocated: 1 of 5; total allocated: 1 of 10]
Hi Petri,
I am facing a problem where the Facebook sign-in happens but after that the control is not returning to my applicaiton. It is simply waiting for localhost.
I am simply running this code as is without any modification( except creating a new socialConfig.properties )
what basically happens after the facebook login? Does it check for the user existance? I am asking this because I havent configured the Database.
Hi,
The registration and login process is explained in the second part of my Spring Social tutorial. Does your problem occur when the user tries to create a user account by using social sign in (i.e. he clicks the social sign in link for the first time)?
Hi Petri,
Nice article!!
I have followed exactly the same steps as described here and can go to facebook login page from my apps login page (by clicking on facebook link).After fb login , the app lands back to the apps login page with the url appended with '#_=_' e.g. 'http://localhost:8080/MyApp/#_=_'.
Actually I expected the registration page will be displayed instead.SocialUserService::loadUserByUserId() is not getting called as I put some sop there.
Any hints?
Best regards,
Pradeep
my own mistake, forgot adding /usr/register twith permitAll() in security context.
Can see the register form now.
It is good to hear that you were able to solve your problem!
Well the user registers now,I mean registration screen comes and user details are saved to my Candidate table but UserConnection table has no entries of this new user.So looks like some configuration error still there regarding SocialUserService since it's loadUserByUserId() is still not called.
My securitycontext.xml is:
and social.xml is
My databasecontext.xml has the rest:
3 ? request.getRequestURI().split('/')[3] : 'guest'}" />
Any hints for this problem?
Wordpress ate your XML configuration but I happen to have an idea what might be the problem. You have to persist the user's connection to the
UserConnection
table after the registration has been successful (and the user uses social sign in). You can do this by following the instructions given in the second part of this tutorial.However, please note that the static
handlePostSignUp
method of theProviderSignInUtils
class has been deprecated. You should use thedoPostSignUp()
method instead.I hope that this solves your problem.
That was a perfect hint!!
Actually I have commented the call to ProviderSignInUtils.handlePostSignUp() thinking its not useful in my case.
Thanks a lot :)
You are welcome. It is good to hear that you were able to solve your problem.
This article is not simple. It's difficult to understand so please make simple example
Unfortunately I have no idea how I could create a simpler example application since I think that the example application is already pretty simple.
However, if you let me know which parts of this blog post were hard to understand, I can try to make them easier to understand.
HI Petri,
Logout is not working for facebook. It just redirecting to login page. How can I do logout in facebook.
And for twitter I'm getting this error.
org.springframework.web.client.HttpClientErrorException: 406 Not Acceptable
org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
Do you mean that log out function of my example application is not working, or are you trying to follow the instructions given in this blog post and cannot get the log out to work in your application?
Also, do you mean that after the user clicks the log out link, the user is still logged in to the application and can access protected resources (such as the front page of my example application)?
yes,
when I do click on logout button it's redirecting to login page. but when I try to open facebook.com it's directly shows me home page.(It's not asking me use name and password again) That means logut is not working properly.
As far as I know, Spring Social doesn't support this. However, there are a couple of workarounds which are described in the following forum discussions:
I hope that this answered to your question. Also, remember that you have to use similar workarounds for other social sign in providers as well.
Thanks Petri,
There is one more issue in my application. when I do click on signin with twitter, I am getting following error. Am I missing something??
org.springframework.web.client.HttpClientErrorException: 406 Not Acceptable
org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
Thanks Petri,
There is one more issue in my application. when I do click on signin with twitter, I am getting following error. Am I missing something??
org.springframework.web.client.HttpClientErrorException: 406 Not Acceptable
org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
Hi Petri,
Find the solution. I was not setting callback URL in twitter that was the problem. But when I do login in twitter and when it's successful it's redirecting me to registration form. Why is so? do I need to change my callback URL?
Great! It is good to hear that you were able to solve your problem.
The user is redirected to the registration page because either the user account or the persisted connection is not found from the database. This can be a bit confusing so I will explain it here:
When a user "returns" to the example application after clicking a social sign button, Spring social tries to find the persisted connection from the
UserConnection
table.If the connection is not found, the user is forwarded to the registration page because the application assumes that the user doesn't have a user account yet.
On the other hand, if the connection is found, Spring Social tries to find the correct user account by using the value of the
userId
column (the example application uses email address as user id). If the user is not found, the user is forwarded to the registration page.I hope that this answered to your question.
By the way, if you want to get more information about the social sign in flow, you should read the second part of this tutorial.
Thanks Petri.
Thank you for help. Project is now working properly. :)
You are welcome. :)
Hello,
I have a problem with facebook when I turn on HTTPS for /**. When user is redirected back from Facebook site after successful login, and after granting permissions for my app. It goes back to my SocialAuthenticationFilter and attemptAuthentication method. Everythink is ok for http, but with https this method is called one more time, and the user is already authenticated (in attemptAuthService()) so it tries to addConnection but token.principal is null so entire method returns null. In the end the AuthenticationServiceException("authentication failed") is thrown and user is redirected to defaultFailureUrl. I use XML version of your config.
I tried to force http for /auth/** and it WORKS, but i don't think it's safe to transfer tokens on unsecured channel.
Don't know what to do :(
When the user is redirected back to your web application, is the user redirected by using HTTP or HTTPS? The reason why I ask this is that I am using Spring Social in a web application that is served by using HTTPS, and the Facebook login is working correctly.
I checked the Facebook application configuration of that web application and I noticed that I had entered the website url by using HTTPS (e.g. https://www.example.com instead of http://www.example.com). If I would be you, I would check the website url setting found from the Facebook application configuration and ensure that the website url setting has the 'https' prefix instead of 'http'.
Let me know if this solves your problem.
Aww... I have finally figured it out. The problem was in my social configuration. I have added authenticationSuccessHandler (SavedRequestAwareAuthenticationSuccessHandler) with useReferer=true to my socialAuthenticationFilter.
I have done that because I have bootstrap modal dialog with login form on every page and I wanted to redirect user to the same page after authentication. I had totally forgot about that.
It is good to hear that you were able to solve your problem!
Everytime a sql-query is placed it results in a 404 Error. For example I can load the main page without problems. But if I put
User test = userRepository.findByEmail("test")
somewhere in the code i get the 404 Error again. Also everytime I try to do something like login -> 404. From the logs i see the sql query which works fine if I copy it into phpmyadmin. Except for this logging I se nothing.
If I place a logging before and after the query I only see the first one. I guess the query somehow crashes and produces a 404.
I know 404 means not found but this does not make any sense.
What can I do? Is there a way to turn up more loggings?
I use Glassfish with the source provided from github.
I have never tried to run this with Glassfish but I can try to figure out what is going on. Which version of Glassfish are you using?
Is it possible to override the /auth/{providerId} path?
I need to do this because I have multiple security chains and in one of them the social login is meant to do something a bit different and also direct you to somewhere a bit different to.
Hi Ricardo,
There is a way to override the url that is processed by the social authentication filter, but you have to make your own copies of the
SpringSocialConfigurer
andSocialAuthenticationFilter
classes. I know that this is an ugly solution but unfortunately it seems that there is no other way to do this (yet?).The source code of the
CustomSocialAuthenticationFilter
class looks as follows (check the comments for more details):The source code of the
CustomSpringSocialConfigurer
class looks as follows:After you have created these classes, you need configure your application to use them (modify either the exampleApplicationContext-security.xml file or the
SecurityContext
class).I haven't compiled these classes yet but you probably get the idea anyway. Also, I remember that I removed some irrelevant code to make the code sample more clear. This code must be present in your
CustomSocialAuthenticationFilter
andCustomSpringSocialConfigurer
classes.I hope that this answered to your question.
Hi Petri,
I am using your demo. I have rename one table named "userconnection" to "mg_userconnection" . I have changed table named in script. But when I am redirecting to register page after social login it's throws error like this. "bad SQL grammar [select userId from UserConnection where providerId = ? and providerUserId = ?]; nested exception". My question is how can I rename that table name?
Thanks.
Hi Naitik,
You can configure the table prefix by using
setTablePrefix()
method of theJdbcUsersConnectionRepository
class.The
SocialContext
class implements thegetUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator)
method of theSocialConfigurer
class. You can make the required modifications to this method. After you are done, thegetUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator)
method looks as follows:Now Spring Social is using the table
mg_UserConnection
instead of theUserConnection
table. At the moment it is not possible to configure the "whole" name of this database table. In other words, if your database uses case sensitive table names, you cannot transform the name of created database table into lowercase.Hi Petri,
with a little bit of customization I've this scenario:
when a new user registered (not social) displays the phrase "You cannot create an user account because you are already logged in.". but if I try to go to the home (index.jsp) I get an internal server error, maybe because the context is wrong.
here the code of home button:
<a href="">home</a>
but if I use different path (/upload for example) all works fine (I've a controller)
all works fine if I logout and login again whit the login form... the problem is for new registration only.
can you help me?
Hi Tiziano,
I tested this out by following these steps:
The result is that I can see the home page in my browser. My home link looks as follows:
However, this assumes that the application uses context path '/'. If you want to use another context path, you should create your link by using the following code:
Did I miss something? If so, let me know :)
Thanks Petri,
I forgot to tell you that I already use the contextPath tag. sorry...
I obtain a generic error (500 error). with debug I see that creation time in Object principal (cast to UserDetails) is null inside database until I logout and login again.
something miss in registration process?
Did you remember to log the user in after the registration is completed? You should take a look at the controller method that processes the request created when the user's submits the registration form.
If you are logging the user in after registration, could you add the stacktrace found from the log file here? It is kind of hard to say what is going on without seeing any code or a log.
Hi Petri,
Thanks for posting this tutorial this is really good. I used your xml configuration with web.xml . it is unable to locate the bean of userConnectionRepository and connectionFactoryLocator. Below are the error what i am getting
Cannot resolve reference to bean 'usersConnectionRepository' while setting constructor argument; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'usersConnectionRepository' is defined.
Thank you for pointing this problem out. I just noticed that the XML configuration is still using Spring profiles (I removed these profiles from the Java config). If you want to use the XML configuration, you should either
<beans profile="application">
element and leave the other elements intact.I will remove the Spring profiles from the XML configuration when I have got time to do it. Again, thank you for pointing this out!
thanks Petri for your great tutorial and quick response. I have one clarification that how user/provider is binded with registeration. i want to just persist the data and allow the user what should i do. More clear I dont want the registeration form in the case of social sign in.
Hi,
The second part of this tutorial explains how you can create a registration form that supports "normal" registration and registration via social sign in. If you have any other questions about the registration process, leave a comment to that blog post.
thanks ,very usefully
You are welcome! I am happy to hear that this blog post was useful to you.
Hi,
I cloned the repo from https://github.com/pkainulainen/spring-social-examples/tree/master/sign-in/spring-mvc-normal and executed the below command:
mvn clean install
It failed by giving the below error:
[ERROR] 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 'D:\Personal\BACKUP\Non-Cloud\Projects\workspace\petrikainulainen-spring-social-examples-
master\sign-in\spring-mvc-normal\profiles\dev\socialConfig.properties' -> [Help 1]
[ERROR]
Can you help? Thanks.
Hi,
you need to create a Facebook and a Twitter application before you can run the example application. Also, you need to create the
socialConfig.properties
file and configure the used social sign in providers in that file. The README of the example application provides more details about these steps.Hi Petri,
Thanks for your prompt response. I finished first two parts of this tutorial and sincerely thank you for all your efforts in creating a top-quality example app. I am new to Java & Spring development and learned a lot by going through these two articles and the source code.
I am able to run the app by "mvn jetty:run -P dev" now after reading the README file but facing few issues in which I need your help. I plan to create a tomcat-deployable WAR file so I did: mvn war:war -P dev, and came up with a WAR. Now things don't work as they were working earlier:
1. Once I Log in using my FB account, the app shows me a Welcome page. When I press the Logout button on that page, it takes me to http://localhost:8080/logout instead of the logout success URL: http://localhost:8080/login.
2. Also, if I type http://localhost:8080/spring-mvc-normal/ again after logging out, I am able to see the Welcome page. That means user is still logged in my app as well as in FB.
3. Both problems 1 & 2 are there when I create a new normal user (without FB etc).
4. Create user account also doesn't work now. It comes to http://localhost:8080/user/register and displays a blank page.
I believe either the WAR is not getting created properly or Tomcat is not getting configured. Can you suggest what could the problem here? I found this link "http://maciejwalkowiak.pl/blog/2012/03/27/spring-3-1-profiles-and-tomcat-configuration/" but not sure whether it is relevant to my problem.
Thanks once again for writing such a wonderful tutorial.
I think that these problems are caused by the fact that the application assumes that it is run by using context path '/'. It seems that you are running it by using the context path '/spring-mvc-normal/'. Am I right?
I thought that I already fixed this, but it seems that I didn't fix it after all. Thank you for pointing this out!
I will fix the JSP pages and commit my changes to Github.
Update: I fixed the JSP pages. You can get the working example application from Github.
Thanks Petri, you nailed down the problem. After pulling your last commit, I was able to run the app perfectly fine from Tomcat.
BTW, I want to know whether there was a mistake in my running the app. Is it not the right way to launch when one would deploy the app in production environment?
You are welcome.
The problem was that I didn't think that it should be possible to run this application by using other context paths than '/'. In other words, it was a bug in the application (my mistake, not yours).
Hi Petri,
I am facing one strange problem now. When I try to login / register for the first time, I don't see the "Create user account" link at the top right corner of the page. Once I log in successfully, I don't see the log out button anymore.
I re-used your JSP pages as they are in my test app. But looks like something is missing. What do I need to effectively use "sec:authorize" in my JSP pages?
Thanks for your help.
Hi,
The reference manual of Spring Security states that:
Do you configure your application by using XML configuration or Java configuration?
If you use Java configuration, you should check the source code of the
SecurityContext
class.If you use XML configuration, you should check the
exampleApplicationContext-security.xml
file.Hi Petri,
I have matched the XML and JSP files many times but there is no change except the package names which are as per my application.
During my debugging, what I realized is that none of the spring security tags present in the layout.jsp file is working. I put many debug prints in layout.jsp but nothing came up on the screen. If I do the same in other JSP files,they all show up.
Does this ring any bell? Where have I screwed up? Sorry but since I am on the learning path, hence troubling you too much.
Thanks.
Hi Sona,
Have you declared the Spring Security taglib in every JSP file that uses it?
Don't worry about this. I am happy to help.
Hi Petri,
Finally, months after reporting this issue, I found the root cause and believe me, it is one of most silly solutions I have found for my issues.
Problem is in the filter ordering in my web.xml file. I wasn't aware that ordering plays a critical role and here sitemesh and spring security were not in order. Following thread from SO helped me in identifying this:
http://stackoverflow.com/questions/4370629/spring-security-tags-in-sitemesh-decorator
Thanks for all your support in debugging this :)
Hi Sona,
You are are welcome (although I think that was not able to provide you a lot of help on this matter). In any case, it is great to hear that you finally found the reason for your problem and were able solve it!
Hi Petri,
I am very new to spring security at all and I have implemented spring security with openID authentication(Login through gmail) now I am trying spring facebook integration. For this, I have written custom class which is generic for all i.e. simple security,openId Auth and spring facebook as follows:-
public class UserAuthenticationFilter implements UserDetailsService,AuthenticationUserDetailsService,SocialUserDetailsService {
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
}
public UserDetails loadUserDetails(OpenidAuthenticationToken token)
throws UsernameNotFoundException, DataAccessException {
}
public SocialUserDetails loadUserByUserId(String username)
throws UsernameNotFoundException, DataAccessException {
}
in this way, in above filter, I have overridden the required methods.But I am not able to execute the loadIUserByUserId method at all. I have added following code in security.xml:-
and in jsp code is as follows
<a href=""> Login with facebook
Is it necessary to create the facebook app? Can u plz tell that Is It a correct way to implement this. plz give me the solution to get success on spring facebook login and plz suggest what implemtation is remaining . I am stuck on this.
Thanks in advance
Hi,
Yes. You cannot implement a Facebook login without it because you need a valid app id and app secret. If you haven't created a Facebook application for your application, this is the reason why you your Facebook login is not working.
Unfortunately Wordpress "ate" your XML configuration so I cannot say if there is something wrong with it. However, you can take a look at the
exampleApplicationContext-security.xml
file, and compare it to yoursecurity.xml
file.I hope that this answered to your question.
Hi Petri,
Thanks for reply, Actually I have same xml configuaration as ur exampleApplicationContext-security.xml and exampleApplicationContext-social.xml. And I have already done data source and transactionManger like configuration in application context.xml.
But I have some doubts and issues as follows:-
1)I am getting this error in exampleApplicationContext-security.xml
Line 77 in XML document from ServletContext resource [/WEB-INF/application-security.xml] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 77; columnNumber: 65; cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element 'constructor-arg'
2) We don't have created any object then why are we using the "usersConnectionRepository" this value as ref in security.xml
plz help me to make this integration working. I am not getting clicked anyway here.
Thanks a lot petri......
Hi,
it seems that the problem is that you haven't configured the
UsersConnectionRepository
bean. This bean is required because Spring Social persists the user's connection to a social service provider.My example application uses the
JdbcUsersConnectionRepository
class that persists connection data to a relational database.The section 2.3. Persisting Connections of the Spring Social reference manual provides more information about this.
Hi Petri,
Can u plz tell me that how to integrate facebook login with localhost application.
Plz help me. I am using gwt with spring and hibernate. My generated local URL is http://127.0.0.1:8888/demo
What is the correct way to implement this? Thanks
Thanks in advance
Hi,
I have never used GWT (Google Web Toolkit?), and unfortunately I have no idea if it is possible to integrate Spring Social with it. :(
If you want to use Spring Social, you should use Spring MVC and follow the instructions given in my Spring Social tutorial.
Excellent post for Spring MVC.
How can we make it work for Spring MVC without ViewModel, but plain REST services that return JSON? In that case, the user probably uses some javascript based SPA that already authenticates with the SocialSignInProvider outside the MVC application and already has a token.
Thank you for your kind words. I really appreciate them.
Unfortunately I have never added social sign in to a Spring powered REST API. In other words, I don't know how you can use it with Spring MVC if your application provides only a REST API.
I have been planning to write a separate tutorial about this for a long time. I guess now is a good time to actually do it.
Petri,
One of the finest tutorial available on the internet.
I searched a lot for spring social sign in with Google+. Your example also implements facebook and twitter
Could you please guide or leave comment on how can I implement Google+ sign in using spring MVC?
Again many thanks.
Thank you for your kind words. I really appreciate them.
Unfortunately I have never done this myself :( However, I am going to update my Spring Social tutorial when I have got some free time (I don't know the exact schedule). I will add the Google sign in to my example application and add a new part to my Spring Social tutorial.
Great tutorial ! I was able to integrate Facebook, LinkedIn and Google+ Sign-In and was able to post link on Facebook & LinkedIn. But wondering which API of google to use for the same. Any help please ?
Also I noticed, when logging out, data from UserConnection is not getting deleted. Is it right behavior or I missed something in configuration?
I noticed , record gets deleted when used POST /connect/facebook with delete hidden parameter like below
<form action="" method="post">
Spring Social Showcase is connected to your Facebook account.
Click the button if you wish to disconnect.
Disconnect
Thank you for your kind words. I really appreciate them!
There is a community project called Spring Social Google, but I haven't tried it out yet. If you want to give it a shot, you can get started by readings its reference manual.
This is normal. The data found from the
UserConnection
table links the information received from the social sign in provider with the local user account. If you delete this link, the application cannot log you in (unless you create a new user account).Hi Petri,
I have implemented spring social.When I clicked on the"login with facebook" button then it is get redirected to the facebook login page successfully but now after login the facebook ,its get redirected to signup url means it is not finding the user in database.
1) I have registered the user in my application and able to login alsoby using simple username and password without using the facebook login.
2)But when I login through the facebook with the same user then why it is no finding that user and it is get redirected to signup url.
First it was giving the errror as "userconnection table not exit" now I have created the userconnection table in my database.
I have the following xml configuration :-
--------- xml configuration------
----- End of xml configuration-----
In jsp page, I have used the code like:-
These are my all changes. Apart from this,I couldnt have implemented anything else. So please, what should I do here for the proper working of the applicaiton, my facebook login user should redirect properly to my application and data should be inserted by connect controller in "userconnection" table. I am stuck over here please highligh what should I implement now at this stage.
Many Many thanks in advance...
The reason for this is that the user who tries to log in by using a social sign in provider is not the "same" user than the user who created a user account by using the "normal" registration.
Because the user created a user account by using the "normal" registration, he/she cannot sign in by using a social sign in provider and expect the application to identify his/her existing user account. The application cannot do this because the
UserConnection
table is empty.Also, you cannot just insert a row to this table (unless the user is creating a user account by using social sign in) because you need the information returned by the social sign in provider.
If you need to support only Facebook, you can try to fix this by making the following changes to the controller method that renders the sign up view:
UserConnection
table, and log the user in.Also, you have to configure Spring Social to request the user's email address when the user starts the social sign in flow by using Facebook.
This approach has two limitations:
I hope that this answered to your question.
Thank u so much Petri.
As u suggested I tried a lot to create user connection but not work. Is there any url(signup url) through which user connection is created automatically.
Thanks..
My example application has no such url, but you can persist the user's connection by invoking the
doPostSignUp()
method of theProviderSignInUtils
class.Hi Petri,
I tried to use the example of Maven but appeared this error in building:
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 wascmd.exe /X /C ""C:\Program Files\Java\jdk1.8.0_05\jre\bin\java" -javaagent:C:\Users\Victor\.m2\repository\org\jacoco\org.jacoco.agent.6.3.201306030806\org.jacoco.agent-0.6.3.201306030806-runtime.jar=destfile=C:\Users\Victor\Documents\spring-social-examples\sign-in\spring-mvc-normal\target\coverage-reports\jacoco-ut.exec -jar C:\Users\Victor\Documents\spring-social-examples\sign-in\spring-mvc-normal\target\surefire\surefirebooter5500528348155860158.jar C:\Users\Victor\Documents\spring-social-examples\sign-in\spring-mvc-normal\target\surefire\surefire5217935832005982224tmp C:\Users\Victor\Documents\spring-social-examples\sign-in\spring-mvc-normal\target\surefire\surefire_07294595199859845725tmp"
-> [Help 1]
Can you help me? Thanks
I removed a few lines from this comment since they were irrelevant - Petri
Hi,
I noticed that you are using Java 8. I assume that the JaCoCo Maven plugin 0.6.3.201306030806 is not compatible with Java 8.
If you don't need to code coverage reports, you can simply remove the plugin configuration. If you want to generate the code coverage reports, you should update its version to 0.7.2.201409121644.
Let me know if this solved your problem.
Hi Petri,
Good morning !!!
I made a small application going through your fantastic tutorials and tried hosting it using Apache & Tomcat on a professional Hosting Server. However, I am not able to access the site without having the app name in the URL. I always have to do: http://www.mydomainname.com/my-spring-app-name.
I followed many SO posts and tutorials but no where I could find some one who is having the same environment. So finally thought of asking you as how it should be done. Details of what I have tried are captured here: http://stackoverflow.com/questions/28670450/apache-with-proxypass-and-spring-security
Please see if you can help.
Thanks once again for all your help.
Hi,
The easiest way to solve to solve your problem is to change the name of deployed war file to ROOT.war. When you deploy a war file called ROOT.war to Tomcat, the context path of that application is '/'. This means that you can access your application by using the url http://www.mydomainname.com.
Hi Petri,
Thanks for the suggestion. I tried that and it worked like a charm for my localhost but not for the production app running at my hosting machine. When I tried mydomainname.com, it gave me:
Index of /
[ICO] Name Last modified Size Description
[IMG] favicon.ico 2015-02-26 15:54 822
[TXT] index.html.backup 2014-12-20 10:49 603
I checked both tomcat server.xml files and didn't find any difference. Is there something else which I am missing?
It's kind of hard to say what could be wrong, but the first thing that came my mind was that your Apache might not be configured properly (it seems that is not "forwarding" requests to Tomcat). Are you using an AJP connector?
Hi,
My project run well based on Spring + Spring Security + JSF + WebFlow . Can I integrate Spring Social to my project without Spring Controler.
Thanks in advance
Hi,
Because I don't have any experience from JSF, I cannot answer to your question. However, it seems that using Spring Social without Spring MVC requires a lot of work.
Hi Petri,
You have the best example on spring social. am trying to add 'login with facebook' on my website where form based login is already implemented. You have merged both form based and social login into one and made confusing. Is there a way you can create an example with 'login with facebook' only based on xml configuraiton?
Hi Shashi,
Actually the login functions are not merged. The form login is processed by Spring Security and the social login is processed by Spring Social. However, if a user tries to sign in by using a social sign in provider and he doesn't have an user account, he has to register one by using the registration form. This process is explained in the next part of this tutorial.
You can of course skip the registration phase and create a new user account if the user account isn't found from the database. If you want to do this, you need to modify the
SignUpController
class. You need to read the user's information from the request and use this information to create a new user account.The example application has XML configuration files as well.
By the way, I will update my Spring Social tutorial later this year. I will address your concerns when I do that. For example, I plan to make this tutorial less confusing :) Thank you for the feedback!
Thanks for a wonder post on this! It helped me understand how spring social works quicker than the official spring social post. :)
I wanted to add spring social feature to an Appfuse app (uses spring security) that I had been playing with. Following your 2 posts, I was trying to integrate step by step and trying to see the effect of each step.
But I ran into a problem right immediately :
WARN [qtp937612-50] PageNotFound.noHandlerFound(1118) | No mapping found for HTTP request with URI [/app/auth/facebook] in DispatcherServlet with name 'dispatcher'
Can you help explain where is the "/auth/facebook" request mapped? Is this done by simply declaring a connectController bean in your SocialContext class (which I have copied)?
@Bean
public ConnectController connectController(ConnectionFactoryLocator connectionFactoryLocator, ConnectionRepository connectionRepository) {
return new ConnectController(connectionFactoryLocator, connectionRepository);
}
Thank you for your kind words. I really appreciate them!
The
SocialAuthenticationFilter
class processes requests send to the url: '/auth/facebook'. TheSpringSocialConfigurer
class creates and configures this filter (check out theSecurityContext
class for more details).Thanks for this project, it is very good to understand social core.I only have question, there is any possibility to adapt this project in mobile or rest api ? I have problem with redirecting in process /signup for REST &( angularApp and mobileApp) ?
I have never done this myself, but I think that it is doable. I tried to find information from Google, but I couldn't find anything interesting. My next step was to ask help from my Twitter followers. I hope that someone can shed some light on this problem.
Update: It seems that your only option is to do a normal GET request and let the backend handle the authentication dance.
And how to send response to android or rest api after this dance ?
Hi Wojciech and Petri, did you fix this problem i have the same many thanks!
Hi Mova,
Unfortunately your best option is to do a normal GET request and let the backend handle the authentication dance. However, that doesn't solve your problem because you still need to respond to the client after the dance is complete. I have one idea that might help you to do this:
The easiest way to solve this to create two controller methods which handle failed sign in attempt and successful sign in. Implement these controller methods by setting the preferred HTTP response code to the HTTP response. After you have implemented these controller methods, you have to configure Spring Social to redirect the request to the correct url.
If you use Java configuration, you can do this by setting the values of the
postLoginUrl
andpostFailureUrl
fields of theSpringSocialConfigurer
class.If you use XML configuration, you can do this by setting the values of the
postLoginUrl
andpostFailureUrl
fields of theSocialAuthenticationFilter
filter class. If you don't have any idea how to do this, check out the XML configuration file of my example application. It should help you to get started.Did this answer to your comment?
hi petri,
I tried the first part of this tutorial using xml configurations, however i am getting the following error..
No qualifying bean of type [com.project.service.UserRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
could you please help me out in this, it seems to be an autowiring or dependency injection issue also is there any implementation of UserRepository that we have to write.
thanks,
shashwat
Update: I removed the stack trace since the error message gave enough information about the problem - Petri
Hi Shashwat,
The example application uses Spring Data JPA, and the
UserRepository
is the repository that is used to manage user information.It seems that you have changed the package hierarchy of the project because old package of the
UserRepository
interface was:net.petrikainulainen.spring.social.signinmvc.user.repository
. This is why Spring Data JPA cannot create theUserRepository
bean.You can solve this problem by configuring Spring Data JPA to use the correct package. You can do this by changing the value of the
jpa:repositories
element'sbase-package
attribute. You can find the persistence layer configuration from the exampleApplicationContext-persistence.xml file.Also, if you are not familiar with Spring Data JPA, you should take a look at my Spring Data JPA tutorial.
If have any further questions, don't hesitate to ask them.
Hey Petri,
thanks for a quick revert. So I get the issue, you are using jpa repository. But the main problem i m facing is that i am using hibernate in my whole application and it would be difficult to migrate the whole app to spring data jpa to just implement a login functionality. Could you please help me with the changes that i will have to do in case i have to implement it in hibernate.
I m attaching below my dao setup below which uses a jndi resource. Any help would be appreciated.
com.myproject.pojo.*
org.hibernate.dialect.MySQL5InnoDBDialect
false
false
true
true
true
false
false
org.joda.time.contrib.hibernate.PersistentDateTime
java:comp/env/jdbc/myproject
regards,
shashwat
Hi,
It is actually quite easy to get rid of Spring Data JPA. You need to create a Hibernate repository class that is used to handle user information (or you can use an existing repository) and ensure that the repository implements these methods:
User findByEmail(String email)
method returns the user whose email address is given as a method parameter. If no use is found, it returnsnull
. This method is invoked when the user logs in to the system or creates a new user account.User save(User saved)
method saves the information of theUser
object given as a method parameter and returns the saved object. This method is invoked when a user creates a new user account.If you have any other questions, feel free to ask them.
thanks petri,
got it working, will be moving to part 2 of this tutorial, will let you know if i get stuck anywhere.
Hi Shashwat,
You are welcome! It is nice to hear that you were able to solve your problem.
Hi Petri,
I have integrated spring security with spring social using your example code , configuration is same as you provided in your github code.However after calling /auth/twitter it goes to twitter sigin page and after that it authenticates and redirects back to my login page.What can be the reason for this?
I am using the latest version of spring, spring-security and spring-social.
Hi Viral,
I have a couple of ideas:
First, check that the callback url of your Twitter application is correct.
Second, Does this happen when the user clicks the "Sign in With Twitter" link for the first time? If so, one possible reason for this is that Spring Social cannot find the persisted connection from the
UserConnection
table. When this happens, it will redirect user to the sign up page (see this discussion). Also, if the user account is not found the database, the user is redirected to the sign up page.Do you use your login page as the sign up page?
Hi Petri ,
Thanks for Your help.
It worked ,I was Sign in With Twitter for the first time .Also I had changed the spring social version to 1.1.2 Release ,ProviderSignInUtils class is getting error in 1.1.2 .It is working fine in 1.1.0 Release .
It seems that the Spring Social team did some major changes to the
ProviderSignInUtils
class between Spring Social 1.1.0 and 1.1.2. Anyway, another reader had the same problem, and I posted the solution here.Are you planning to update the tutorial to version 2.0.x of spring.social.facebook? There are big changes there, for example, there is no default constructor for ProviderSignInUtils. I am currently struggling with updating my code to version 2.0.x.
I would love to do it right now, but I am afraid that I won't be able to do it until next year because I need to update two older tutorials before I can move on to this one.
I took a quick look at the Javadoc of the
ProviderSignInUtils
class and noticed that it has a constructor which takesConnectionFactoryLocator
andUsersConnectionRepository
objects as constructor arguments. You could simply inject these beans to the component that uses theProviderSignInUtils
class and pass them as constructor arguments when you create a newProviderSignInUtils
object.If you have any other questions, don't hesitate to ask them!
Hi Petri Kainulainen,
How can I workout with mongoDB having this set of example, please help me out..
Hi Vinodh,
Take a look at this comment.
hi Petri,
I am getting error in POM.xml when I import your project in Eclipse Mars IDE:
Plugin execution not covered by lifecycle configuration: org.jacoco:jacoco-maven-plugin:0.6.3.201306030806:prepare-
agent (execution: pre-unit-test, phase: initialise)
regards,
Laxman
Hi,
You can fix this by removing the JaCoCo Maven Plugin from the pom.xml file.
for those who is also struggle about the Auth setScope issue mentioned at http://www.petrikainulainen.net/programming/spring-framework/adding-social-sign-in-to-a-spring-mvc-web-application-configuration/#comment-453154
Pls update spring-social-config to later version (1.1.2.REALEASE), you should be fine.
Hi,
Thank you for sharing! This is a quite common use case, and that is why it is great to find out how I can solve this problem.
Hi Petri,
I have few questions regarding this tutorial. I am trying to have only 'login with facebook' as the way users can signIn to the application.
Questions:
1. When would user get a session Id, if user tries to login with facebook?
2. Spring Social document says to have a schema for UserConnections table. So when does Spring adds a particular entry (user) to this table ?
Hi Jay,
The user gets a session id when he/she opens the first page of your application. Spring Security can create a new session for him/her after login if you have specified it to do so.
You need to insert a new row into this table by using the
ProviderSignInUtils
class.If you want get more information about this, check out the next part of my Spring Social tutorial.
Hi Petri,
I have deployed the project into amazon aws but it is not working fine. sometimes not able to get repsonse from auth/facebook. getting exception "Page has too many redirection" but all the functionalities are working fine in local tomcat.
Need suggestion..
I think that this is an AWS specific problem. Unfortunately I don't know what the problem is :(
Dear my friends,
I need sample project that use spring MVC framework + restful and use OAuth 2 and use MYSQL database if some body can help me please email to me,thank you in advance.
Hi,
I am not sure if you can find one example that fulfills your requirements. However, if you are willing to do part of the work yourself, you can take a look at these tutorials:
I hope that this helps.
Dear Petri,
when i want to run this project with wildfly i have this error can you help me.
org.springframework.social.config.annotation.SocialConfiguration.connectionFactoryLocator()] threw exception; nested exception is java.lang.IllegalArgumentException: Circular placeholder reference 'twitter.consumer.key' in property definitions
Update: I removed the irrelevant part of the stack trace - Petri
Hi,
How did you create the war file? The reason why I ask this is that Maven should replace the property placeholders found from the properties file with the values found from the profile specific configuration file. However, the error message of the thrown
IllegalArgumentException
states that this did not happen.Dear Petri,
How should i create the war file i run it in Intellij Idea and make the artifact and configure it but this error happened when i select each of AuthServer and RestServer.
ERROR [org.springframework.web.servlet.DispatcherServlet] (MSC service thread 1-3) Context initialization failed: java.lang.IllegalArgumentException
at org.springframework.asm.ClassReader.(Unknown Source) [spring-core-3.2.1.RELEASE.jar:3.2.1.RELEASE]
at org.springframework.asm.ClassReader.(Unknown Source) [spring-core-3.2.1.RELEASE.jar:3.2.1.RELEASE]
at org.springframework.asm.ClassReader.(Unknown Source) [spring-core-3.2.1.RELEASE.jar:3.2.1.RELEASE]
Dear Petri,
Can you tell me how to run this project in Intellij Idea and with wildfly application server with screen shot,thank you in advance.
I don't know how you can run this application with IntelliJ Idea and Wildfly because I have never used Wildfly. However, you should be able to run it as long as you have:
Also, keep in mind that you have to create the deployed .war file by running the package Maven goal by using the dev Maven profile. You can configure IntelliJ Idea to invoke a Maven goal before it starts the application server.
Hi Petri,
Can you tell me this project how to work with Twitter and Facebook.
and i want work this with linkedin and google too.
Hi Morteza,
The example application should work with Facebook and Twitter as long as you have configured it properly (check my previous comment).
These projects provide support for Google and LinkedIn:
The example application doesn't support them at the moment. This means that you have to make some minor configuration changes to the application. These changes are explained in the reference manuals of these projects. Also, you need to add the sign in links to the login page and add the new providers to the
SocialMediaService
enum.Dear Petri,
I have this error when click the link of facebook (Invalid App ID: foo) and this error when i click the link of twitter
org.springframework.web.client.HttpClientErrorException: 401 Authorization Required
org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:576)
org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:532)
org.springframework.web.client.RestTemplate.execute(RestTemplate.java:504)
org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:449)
org.springframework.social.oauth1.OAuth1Template.exchangeForToken(OAuth1Template.java:187)
org.springframework.social.oauth1.OAuth1Template.fetchRequestToken(OAuth1Template.java:115)
what i have to do and i configure the Twitter as you said.
Did you create your own Facebook and Twitter applications? The error messages suggest that the configuration file (socialConfig.properties) which should contain the real API credentials provided by Facebook and Twitter contains my placeholders (foo and bar).
Yes,I create my own Twitter applications and above error has happened.
Did you configure the domains that are allowed to access the application?
how to configure domainds?
Hi,
Unfortunately I don't remember the details anymore, but if I remember correctly, you should be able to configure the domains in the settings of your Twitter and Facebook applications.
Hi,
I am P V REDDY.I am Senior Software Engineer.What I have to in our Project is I need authenticate Either Email Id or Phone Number with OTP or 3 Questions and Answers before Login into the Web application in Spring MVC .
I need some reference and also I need some api's
Hi,
I have never used OTP in my Spring applications, but I found a library that adds OTP support to Spring Security. I think that you should take a look at it.
Dear Petri,
When i use Google+ and configure with this application and click the button of Google+ redirect me to the page with this error:
400. That’s an error.
Error: invalid_request
Missing required parameter: scope
Learn more
Request Details
That’s all we know.
Hi,
It seems that you need to specify the value of the
scope
parameter. Take a look at this StackOverflow answer. It explains how you can the add thescope
parameter in your sign in form.Hi Petri,
I am trying to use social login in my current Spring project. I have added the dependencies, but when I build the project, I am not able to see the /connect url mapped logs in the console.
Unfortunately it's impossible to say what is going on without running your code. Do have a sample project that reproduces this problem?
Superb Post. Thanks a lot
You are welcome.
Hi
I imported the code from Github. And i had maven clean install. But when i run the code using this path url in the browser...
http://localhost:8080/spring-mvc-normal/login
I get 404 Error like this ........ What is the reason ?
HTTP ERROR 404
Problem accessing /spring-mvc-normal/login. Reason:
NOT_FOUND
The 404 error means that the page is not found. Are you running the web application by using Jetty Maven plugin or do you use some other servlet container?
Yes , I am running the web application using Jetty server
Hmm. I have to admit that I don't know what is wrong :( By the way, are you using Java 8? If I remember correctly, the example application doesn't work with Java 8 because it uses a Spring version that doesn't support Java 8.