1.Maven Configuration

Chúng ta sẽ thêm spring-social-facebook dependency vào file pom.xml

<dependency>
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-facebook</artifactId>
</dependency>

2.Security Config – Just Form Login

Đây là một cấu hình security đơn giản chỉ sử dụng form-based authentication

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Autowired
    private UserDetailsService userDetailsService;
 
    @Override
    protected void configure(AuthenticationManagerBuilder auth) 
      throws Exception {
        auth.userDetailsService(userDetailsService);
    }
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        .csrf().disable()
        .authorizeRequests()
        .antMatchers("/login*").permitAll()
        .anyRequest().authenticated()
        .and()
        .formLogin().loginPage("/login").permitAll();
    } 
}

3. Security Config – Adding Facebook

package login.facebook.config;

import login.facebook.security.FacebookConnectionSignup;
import login.facebook.security.FacebookSignInAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository;
import org.springframework.social.connect.web.ProviderSignInController;

import javax.sql.DataSource;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private ConnectionFactoryLocator connectionFactoryLocator;


    @Autowired
    private FacebookConnectionSignup facebookConnectionSignup;

    @Autowired
    private DataSource dataSource;

    @Override
    protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        // @formatter:off
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/login*", "/signin/**", "/signup/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin().loginPage("/login").permitAll()
                .defaultSuccessUrl("/index")
                .and()
                .logout();
    } // @formatter:on

    @Bean
    // @Primary
    public ProviderSignInController providerSignInController() {
        JdbcUsersConnectionRepository usersConnectionRepository = new JdbcUsersConnectionRepository(dataSource,
                connectionFactoryLocator, Encryptors.noOpText());
        usersConnectionRepository.setConnectionSignUp(facebookConnectionSignup);
        return new ProviderSignInController(connectionFactoryLocator, usersConnectionRepository, new FacebookSignInAdapter());
    }
}

Hay nhìn config mới này :

Chúng ta sử dụng ProviderSignInController để enable facebookauthentication

Bằng việc sending một POST “/signin/facebook” controller Này sẽ bắt đầu đăng nhập sử dụng facebook service provider

Chúng ta tạo lớp signInAdapter để handle logic login trong ứng dụng .

Và chúng ta cũng tạo lớp ConnectionSignUp để handel vệc đăng kí user khi lần đầu authenticate với face book .Bạn có thẻ thấy đoạn code :

dbcUsersConnectionRepodbcsitory usersConnectionRepository = new JdbcUsersConnectionRepository(dataSource,
                connectionFactoryLocator, Encryptors.noOpText());
        usersConnectionRepository.setConnectionSignUp(facebookConnectionSignup);

Khi lần đầu tiên login thì một connection sẽ được tạo ra cho user đó với đoạn code này thì tôi dùng JdbcUsersConnectionRepodbcsitory để lưu connection vào db .

Đây là script để tạo db ,bạn hãy chú ý bảng:UserConnection connection được lưu ở bảng này

-- Table structure for table `UserConnection`
--

DROP TABLE IF EXISTS `UserConnection`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `UserConnection` (
  `USERID` varchar(255) NOT NULL,
  `PROVIDERID` varchar(255) NOT NULL,
  `PROVIDERUSERID` varchar(255) NOT NULL,
  `RANK` int(11) NOT NULL,
  `DISPLAYNAME` varchar(255) DEFAULT NULL,
  `PROFILEURL` varchar(512) DEFAULT NULL,
  `IMAGEURL` varchar(512) DEFAULT NULL,
  `ACCESSTOKEN` varchar(255) NOT NULL,
  `SECRET` varchar(255) DEFAULT NULL,
  `REFRESHTOKEN` varchar(255) DEFAULT NULL,
  `EXPIRETIME` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`USERID`,`PROVIDERID`,`PROVIDERUSERID`),
  UNIQUE KEY `USERCONNECTIONRANK` (`USERID`,`PROVIDERID`,`RANK`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `UserConnection`
--

LOCK TABLES `UserConnection` WRITE;
/*!40000 ALTER TABLE `UserConnection` DISABLE KEYS */;
INSERT INTO `UserConnection` VALUES ('Trần Văn Cừ','facebook','2346898372193364',1,'Trần Văn Cừ',NULL,'https://graph.facebook.com/v2.5/2346898372193364/picture','EAAIVhEmfx6gBAIhjpMwz0TODQWK5upnM8SxS2ZB0Q9q8ROZBPSfb0X0pY0Y4ligVuljoI87lcFPHOCDUzMyeKCC7qqnOUJm1oPSowk9kXAzlkzUJCXPrR3OSLqc4maRrSBcKIb7Tp63zKRUkO3CoMzxmTsas4vGeQHPK8qjwZDZD',NULL,NULL,1562635906341);
/*!40000 ALTER TABLE `UserConnection` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `social_account`
--

DROP TABLE IF EXISTS `social_account`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `social_account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(45) NOT NULL,
  `password` varchar(45) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `social_account`
--

LOCK TABLES `social_account` WRITE;
/*!40000 ALTER TABLE `social_account` DISABLE KEYS */;
INSERT INTO `social_account` VALUES (1,'Trần Văn Cừ','XZfNMOeU');
/*!40000 ALTER TABLE `social_account` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Dumping routines for database 'login_via_social'
--
/*!40103 SET [email protected]_TIME_ZONE */;

/*!40101 SET [email protected]_SQL_MODE */;
/*!40014 SET [email protected]_FOREIGN_KEY_CHECKS */;
/*!40014 SET [email protected]_UNIQUE_CHECKS */;
/*!40101 SET [email protected]_CHARACTER_SET_CLIENT */;
/*!40101 SET [email protected]_CHARACTER_SET_RESULTS */;
/*!40101 SET [email protected]_COLLATION_CONNECTION */;
/*!40111 SET [email protected]_SQL_NOTES */;

-- Dump completed on 2019-05-10  9:11:48

Nếu bạn không muốn sử dụng db để lưu connection bạn có thể sử dụng InMemoryUsersConnectionRepository để thay thế nó sẽ lưu connection vào Memory của server ,nhưng tôi không khuyến khích việc này bởi vì connection sẽ bị mất khi mà server bị tắt và gây tốn Memory nên chỉ áp dụng khi bạn test thôi nhé.

4. The Sign-In Adapter

package login.facebook.security;

import java.util.Collections;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.web.SignInAdapter;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.NativeWebRequest;

@Service
public class FacebookSignInAdapter implements SignInAdapter {
    @Override
    public String signIn(String localUserId, Connection<?> connection, NativeWebRequest request) {
        System.out.println(" ======> Sign In adapter");
        SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(connection.getDisplayName(), null, Collections.singletonList(new SimpleGrantedAuthority("FACEBOOK_USER"))));
        return "/index";
    }
}

Chú ý :Đoạn code này nghĩa là người dùng đăng nhập qua facebook sẽ có role FACEBOOK_USER và người dùng login qua form sẽ có role USER

5. Connection Sign Up

package login.facebook.security;

import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;

import login.facebook.persistence.dao.UserRepository;
import login.facebook.persistence.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionSignUp;
import org.springframework.stereotype.Service;

@Service
public class FacebookConnectionSignup implements ConnectionSignUp {

    @Autowired
    private UserRepository userRepository;

    @Override
    public String execute(Connection<?> connection) {
        System.out.println("signup ===> ");
        final User user = new User();
        user.setUsername(connection.getDisplayName());
        user.setPassword(randomAlphabetic(8));
        userRepository.save(user);
        return user.getUsername();
    }

}

Bạn có thể thấy tôi tạo mới user và sử dụng DisplayName là username khi lần đầu authenticate với face book,Bạn có thể tạo hoặc không tùy theo yêu cầu hệ thống

6. The Facebook Properties

Bây giờ hãy cấu hình Facebook Properties trong application.properties

spring.social.facebook.appId=YOUR_APP_ID

spring.social.facebook.appSecret=YOUR_APP_SECRET

Chúng ta cần một tài khoản face book để có được appId và appSecret:Bạn hãy vào https://developers.facebook.com/apps để lấy và bật chế độ development

7.Front End

Form login (login.html) gồm login and Facebook và login qua form :

<html>
<body>
<div th:if="${param.logout}">You have been logged out</div>
<div th:if="${param.error}">There was an error, please try again</div>
 
<form th:action="@{/login}" method="POST" >
    <input type="text" name="username" />
    <input type="password" name="password" />
    <input type="submit" value="Login" />
</form>
     
<form action="/signin/facebook" method="POST">
    <input type="hidden" name="scope" value="public_profile" />
    <input type="submit" value="Login using Facebook"/>
</form>
</body>
</html>

Đây là trang index.html

<html>
<body>
<nav>
    <p sec:authentication="name">Username</p>      
    <a th:href="@{/logout}">Logout</a>                     
</nav>
 
<h1>Welcome, <span sec:authentication="name">Username</span></h1>
<p sec:authentication="authorities">User authorities</p>
</body>
</html>

Bây giờ thì chạy úng dụng lên và trải nghiệm nhé :

Soure Code tham khảo :https://gitlab.com/cutran/spring-social-login