1. 概述
在本教程中,我們將重點介紹如何向現有表單登錄應用添加新的 Facebook 登錄功能。
我們將使用 Spring Social 支持與 Facebook 交互,並保持簡潔明瞭。
注意: Spring Social 庫已棄用;我們可以查看 Spring Security 5 – OAuth2 Login,它提供基於 OAuth2 的社交登錄功能。
2. Maven 配置
首先,我們需要將 spring-social-facebook 依賴添加到我們的 pom.xml 中:
<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 應用程序以獲取 appId 和 appSecret。
- 從 Facebook 應用程序設置中,請確保將“網站”平台和 http://localhost:8080/ 添加為“站點 URL”。
5. Security Config – Adding Facebook
Now, let’s add a new way to authenticate into the system – driven by 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);
}
}
Let’s carefully look at the new config:
- we’re using a ProviderSignInController to enable the Facebook authentication, which needs two things:
- by sending a POST to “/signin/facebook” – this controller will initiate a user sign-in using the Facebook service provider
- we’re setting up a SignInAdapter to handle the login logic in our application
- and we also setting up a ConnectionSignUp to handle signing up users implicitly when they first authenticate with Facebook
first, a ConnectionFactoryLocator registered as a FacebookConnectionFactory with the Facebook properties we defined earlier.
second, an InMemoryUsersConnectionRepository.
6. Facebook 登錄適配器
簡單來説,這個適配器是連接上述控制器——驅動 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();
}
}
正如你所看到的,我們為新用户創建了一個賬户 – 使用他們的 DisplayName 作為用户名。
8. 前端
最後,讓我們來查看我們的前端。
現在,我們將為這些兩個身份驗證流程 – 表單登錄和 Facebook – 在我們的登錄頁面提供支持:
<html>
<body>
<div th:if="${param.logout}">您已註銷</div>
<div th:if="${param.error}">發生錯誤,請重試</div>
<form th:action="@{/login}" method="POST" >
<input type="text" name="username" />
<input type="password" name="password" />
<input type="submit" value="登錄" />
</form>
<form action="/signin/facebook" method="POST">
<input type="hidden" name="scope" value="public_profile" />
<input type="submit" value="使用 Facebook 登錄"/>
</form>
</body>
</html>
最後 – 這裏是 index.html:
<html>
<body>
<nav>
<p sec:authentication="name">用户名</p>
<a th:href="@{/logout}">登出</a>
</nav>
<h1>歡迎, <span sec:authentication="name">用户名</span></h1>
<p sec:authentication="authorities">用户權限</p>
</body>
</html>
注意,這個 index 頁面顯示用户名和權限。
而且,這就是全部 – 我們現在有兩方便驗證應用程序的方法。
9. 結論
在本文中,我們學習瞭如何使用 spring-social-facebook 來為我們的應用程序實現輔助身份驗證流程。