1. 概述

在这个教程中,我们将专注于在现有表单登录应用中添加Facebook登录功能。我们将利用Spring Social库与Facebook交互,以保持代码简洁且易于理解。

2. Maven配置

首先,我们需要在pom.xml文件中添加spring-social-facebook依赖:

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

3. 安全配置 - 只有表单登录

让我们从简单的安全配置开始,这里只使用基于表单的身份验证:

@Configuration
@EnableWebSecurity
@ComponentScan(basePackages = { "com.baeldung.security" })
public class SecurityConfig {
    
    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public AuthenticationManager authManager(HttpSecurity http) throws Exception {
        return http.getSharedObject(AuthenticationManagerBuilder.class)
            .userDetailsService(userDetailsService)
            .and()
            .build();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf()
            .disable()
            .authorizeRequests()
            .antMatchers("/login*", "/signin/**", "/signup/**")
            .permitAll()
            .anyRequest()
            .authenticated()
            .and()
            .formLogin()
            .loginPage("/login")
            .permitAll()
            .and()
            .logout();
        return http.build();
    }
}

我们不会在这部分花太多时间,如果你想深入了解,可以参考表单登录文章

4. Facebook属性设置

接下来,我们在application.properties中配置Facebook属性:

spring.social.facebook.appId=YOUR_APP_ID
spring.social.facebook.appSecret=YOUR_APP_SECRET

注意:

  • 我们需要创建一个Facebook应用以获取appIdappSecret
  • 在Facebook应用的设置中,确保添加“网站”平台,并将http://localhost:8080作为“站点URL”

5. 安全配置 - 添加Facebook

现在,我们将添加一种新的身份验证方式 - 通过Facebook:

public class SecurityConfig {

    @Autowired
    private FacebookConnectionSignup facebookConnectionSignup;

    @Value("${spring.social.facebook.appSecret}")
    String appSecret;
    
    @Value("${spring.social.facebook.appId}")
    String appId;
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
        .authorizeRequests()
        .antMatchers("/login*","/signin/**","/signup/**").permitAll()
        ...
        return http.build();
    } 

    @Bean
    public ProviderSignInController providerSignInController() {
        ConnectionFactoryLocator connectionFactoryLocator = 
            connectionFactoryLocator();
        UsersConnectionRepository usersConnectionRepository = 
            getUsersConnectionRepository(connectionFactoryLocator);
        ((InMemoryUsersConnectionRepository) usersConnectionRepository)
            .setConnectionSignUp(facebookConnectionSignup);
        return new ProviderSignInController(connectionFactoryLocator, 
            usersConnectionRepository, new FacebookSignInAdapter());
    }
    
    private ConnectionFactoryLocator connectionFactoryLocator() {
        ConnectionFactoryRegistry registry = new ConnectionFactoryRegistry();
        registry.addConnectionFactory(new FacebookConnectionFactory(appId, appSecret));
        return registry;
    }
    
    private UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator 
        connectionFactoryLocator) {
        return new InMemoryUsersConnectionRepository(connectionFactoryLocator);
    }
}

仔细看看新配置:

  • 我们使用ProviderSignInController启用Facebook身份验证,它需要两样东西:
    首先,注册一个ConnectionFactoryLocator,并将其命名为FacebookConnectionFactory,使用我们之前定义的Facebook属性。
    其次,使用一个InMemoryUsersConnectionRepository
  • 发送一个POST"/signin/facebook",这个控制器会通过Facebook服务提供商启动用户登录流程。
  • 我们设置了一个SignInAdapter来处理应用程序中的登录逻辑。
  • 同时,我们还设置了ConnectionSignUp,用于处理用户首次使用Facebook进行身份验证时的自动注册。

6. 登录适配器

简单来说,这个适配器是连接控制器(驱动Facebook用户登录流程)与我们特定本地应用的桥梁:

public class FacebookSignInAdapter implements SignInAdapter {
    @Override
    public String signIn(
      String localUserId, 
      Connection<?> connection, 
      NativeWebRequest request) {
        
        SecurityContextHolder.getContext().setAuthentication(
          new UsernamePasswordAuthenticationToken(
          connection.getDisplayName(), null, 
          Arrays.asList(new SimpleGrantedAuthority("FACEBOOK_USER"))));
        
        return null;
    }
}

请注意,使用Facebook登录的用户将拥有角色FACEBOOK_USER,而使用表单登录的用户将拥有角色USER

7. 连接注册

当用户首次使用Facebook进行身份验证时,他们在这个应用中没有现有账户。这时我们需要为他们自动创建一个账户;我们将使用ConnectionSignUp来驱动这个用户创建逻辑:

@Service
public class FacebookConnectionSignup implements ConnectionSignUp {

    @Autowired
    private UserRepository userRepository;

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

如你所见,我们为新用户创建了账户,使用他们的显示名称作为用户名。

8. 前端

最后,让我们看看前端。现在我们的登录页面将支持这两种身份验证流程:表单登录和Facebook登录:

<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>

最后,这是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>

注意,这个主页如何显示用户名和权限。

就这样,我们有了两种应用登录方式。

9. 总结

在这篇简短的文章中,我们学习了如何使用spring-social-facebook为应用实现额外的身份验证流程。当然,源代码可以在GitHub上找到完整版本


» 下一篇: Java URL 简明指南