在 Spring Boot 项目中实现 OAuth2 支持可通过 Spring Security OAuth2 或 Spring Authorization Server 实现。以下是基于 Spring Security OAuth2 的完整实现方案:
1. 添加依赖
<!-- Spring Security OAuth2 客户端 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<!-- Spring Security OAuth2 资源服务器(可选,保护 API) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
2. 配置 OAuth2 客户端
在 application.properties
中配置第三方服务提供商(如 GitHub、Google):
# 启用 OAuth2 客户端
spring.security.oauth2.client.registration.github.client-id=your-client-id
spring.security.oauth2.client.registration.github.client-secret=your-client-secret
spring.security.oauth2.client.registration.github.scope=read:user,user:email
# Google 配置示例
spring.security.oauth2.client.registration.google.client-id=your-google-client-id
spring.security.oauth2.client.registration.google.client-secret=your-google-client-secret
spring.security.oauth2.client.registration.google.scope=openid,profile,email
3. 配置安全策略
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.antMatchers("/", "/login").permitAll() // 公开路径
.anyRequest().authenticated() // 其他路径需要认证
)
.oauth2Login(oauth2 -> oauth2
.loginPage("/login") // 自定义登录页
.defaultSuccessUrl("/dashboard") // 登录成功后跳转
.failureUrl("/login?error") // 登录失败后跳转
)
.logout(logout -> logout
.logoutSuccessUrl("/") // 登出后跳转
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
);
return http.build();
}
}
4. 获取用户信息
方式1:使用 OAuth2AuthenticationToken
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
public class UserController {
@GetMapping("/user")
public Map<String, Object> getUserInfo(OAuth2AuthenticationToken authentication) {
// 获取用户属性(不同提供商字段可能不同)
return authentication.getPrincipal().getAttributes();
}
}
方式2:使用 @AuthenticationPrincipal
注解
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@GetMapping("/user")
public OAuth2User getUser(@AuthenticationPrincipal OAuth2User oauth2User) {
return oauth2User;
}
}
5. 自定义用户信息映射
处理不同提供商的用户信息结构差异:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
@Configuration
public class OAuth2Config {
@Bean
public DefaultOAuth2UserService userService() {
DefaultOAuth2UserService service = new DefaultOAuth2UserService();
// 自定义用户信息映射
service.setUserInfoExtractors(extractors -> {
extractors.put("github", this::extractGithubUser);
extractors.put("google", this::extractGoogleUser);
// 可添加更多提供商
});
return service;
}
private OAuth2User extractGithubUser(OAuth2UserRequest userRequest) {
// 处理 GitHub 特定的用户信息
return service.loadUser(userRequest);
}
private OAuth2User extractGoogleUser(OAuth2UserRequest userRequest) {
// 处理 Google 特定的用户信息
return service.loadUser(userRequest);
}
}
6. 保护 API(资源服务器)
配置项目作为资源服务器:
# 配置 JWT 验证
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://your-auth-server.com
@Configuration
public class ResourceServerConfig {
@Bean
public SecurityFilterChain resourceServerFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.antMatchers("/public").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt() // 使用 JWT 验证
);
return http.build();
}
}
7. 自定义登录页面
创建 login.html
:
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<h2>Login with OAuth2</h2>
<a href="/oauth2/authorization/github">Login with GitHub</a>
<a href="/oauth2/authorization/google">Login with Google</a>
</body>
</html>
8. 多提供商支持
在配置文件中添加多个提供商:
# Facebook 配置
spring.security.oauth2.client.registration.facebook.client-id=your-facebook-id
spring.security.oauth2.client.registration.facebook.client-secret=your-facebook-secret
spring.security.oauth2.client.registration.facebook.scope=email,public_profile
9. 测试 OAuth2 集成
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(UserController.class)
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
@WithMockUser
public void testAuthenticatedUser() throws Exception {
mockMvc.perform(get("/user"))
.andExpect(status().isOk());
}
}
10. 注意事项
- 注册应用:需在各提供商(GitHub、Google 等)后台注册应用,获取 Client ID 和 Secret。
- 重定向 URI:确保回调 URL 与提供商配置一致(如
http://localhost:8080/login/oauth2/code/github
)。 - 状态验证:Spring Security 自动处理 CSRF 和状态验证,防止攻击。
- 刷新令牌:对于长期访问,需处理刷新令牌逻辑(可通过
OAuth2AuthorizedClientService
)。
总结
通过 Spring Security OAuth2 客户端,可轻松实现第三方认证和授权,主要步骤包括:配置提供商信息、定义安全策略、获取和处理用户信息。如需保护 API,可同时配置资源服务器。这种方式使应用能够无缝集成 GitHub、Google 等身份提供商,提供更好的用户体验。