本文目录导读:

- 项目结构
- Maven依赖配置 (pom.xml)
- 配置文件 (application.yml)
- 配置类
- 实体类
- JWT工具类
- OAuth服务层
- 用户服务层
- 控制器
- 安全配置
- 主应用类
- 前端示例 (HTML)
- SQL脚本
- GitHub OAuth应用配置
- 使用说明
- 扩展其他平台
我将为您提供一个完整的Java第三方登录实现案例,以GitHub登录为例进行说明。
项目结构
third-party-login-demo/
├── pom.xml
├── src/main/java/com/example/oauth/
│ ├── controller/
│ │ └── OAuthController.java
│ ├── config/
│ │ └── OAuthConfig.java
│ ├── service/
│ │ ├── GitHubOAuthService.java
│ │ └── UserService.java
│ ├── model/
│ │ ├── GitHubUser.java
│ │ └── AuthToken.java
│ └── util/
│ └── JwtUtil.java
├── src/main/resources/
│ └── application.yml
Maven依赖配置 (pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>third-party-login</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- HTTP Client -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
</dependencies>
</project>
配置文件 (application.yml)
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/oauth_demo?useSSL=false&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
oauth:
github:
client-id: your-github-client-id
client-secret: your-github-client-secret
redirect-uri: http://localhost:8080/oauth/github/callback
authorize-url: https://github.com/login/oauth/authorize
token-url: https://github.com/login/oauth/access_token
user-url: https://api.github.com/user
jwt:
secret: your-secret-key-at-least-256-bits-long-for-hs256
expiration: 86400000 # 24小时
app:
frontend-callback: http://localhost:3000/oauth/callback
配置类
package com.example.oauth.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "oauth.github")
public class GitHubOAuthConfig {
private String clientId;
private String clientSecret;
private String redirectUri;
private String authorizeUrl;
private String tokenUrl;
private String userUrl;
}
package com.example.oauth.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "jwt")
public class JwtConfig {
private String secret;
private long expiration;
}
实体类
package com.example.oauth.model;
import lombok.Data;
import javax.persistence.*;
import java.time.LocalDateTime;
@Data
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String email;
private String username;
private String avatarUrl;
@Column(name = "provider")
private String provider; // github, google, etc
@Column(name = "provider_id", unique = true)
private String providerId;
@Column(name = "access_token")
private String accessToken;
@Column(name = "created_at")
private LocalDateTime createdAt;
@Column(name = "last_login")
private LocalDateTime lastLogin;
@PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
}
}
JWT工具类
package com.example.oauth.util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component
public class JwtUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private long expiration;
private SecretKey getSigningKey() {
byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
return Keys.hmacShaKeyFor(keyBytes);
}
public String generateToken(Long userId, String username, String provider) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + expiration);
Map<String, Object> claims = new HashMap<>();
claims.put("userId", userId);
claims.put("username", username);
claims.put("provider", provider);
return Jwts.builder()
.setClaims(claims)
.setSubject(username)
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
public Claims parseToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
}
public boolean validateToken(String token) {
try {
Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
}
OAuth服务层
package com.example.oauth.service;
import com.example.oauth.config.GitHubOAuthConfig;
import com.example.oauth.model.GitHubUser;
import com.example.oauth.model.User;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
@Service
public class GitHubOAuthService {
@Autowired
private GitHubOAuthConfig config;
@Autowired
private UserService userService;
private final RestTemplate restTemplate = new RestTemplate();
private final ObjectMapper objectMapper = new ObjectMapper();
/**
* 获取授权URL
*/
public String getAuthorizationUrl() {
return config.getAuthorizeUrl() + "?client_id=" + config.getClientId()
+ "&redirect_uri=" + config.getRedirectUri()
+ "&scope=user:email";
}
/**
* 获取访问令牌
*/
public String getAccessToken(String code) {
String tokenUrl = config.getTokenUrl();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.setAccept(java.util.Collections.singletonList(MediaType.APPLICATION_JSON));
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("client_id", config.getClientId());
body.add("client_secret", config.getClientSecret());
body.add("code", code);
body.add("redirect_uri", config.getRedirectUri());
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, headers);
ResponseEntity<String> response = restTemplate.postForEntity(tokenUrl, request, String.class);
try {
JsonNode jsonNode = objectMapper.readTree(response.getBody());
return jsonNode.get("access_token").asText();
} catch (Exception e) {
throw new RuntimeException("Failed to get access token", e);
}
}
/**
* 获取GitHub用户信息
*/
public GitHubUser getGitHubUser(String accessToken) {
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(accessToken);
headers.setAccept(java.util.Collections.singletonList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<GitHubUser> response = restTemplate.exchange(
config.getUserUrl(),
HttpMethod.GET,
entity,
GitHubUser.class
);
return response.getBody();
}
/**
* 处理GitHub OAuth回调
*/
public User handleOAuthCallback(String code) {
// 获取访问令牌
String accessToken = getAccessToken(code);
// 获取GitHub用户信息
GitHubUser githubUser = getGitHubUser(accessToken);
// 查找或创建用户
User user = userService.findOrCreateUser(githubUser, accessToken);
return user;
}
}
package com.example.oauth.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class GitHubUser {
private Long id;
private String login;
private String name;
private String email;
private String avatarUrl;
private String bio;
}
用户服务层
package com.example.oauth.service;
import com.example.oauth.model.GitHubUser;
import com.example.oauth.model.User;
import com.example.oauth.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Optional;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
/**
* 查找或创建GitHub用户
*/
public User findOrCreateUser(GitHubUser githubUser, String accessToken) {
Optional<User> existingUser = userRepository.findByProviderId(String.valueOf(githubUser.getId()));
User user;
if (existingUser.isPresent()) {
user = existingUser.get();
// 更新用户信息
user.setUsername(githubUser.getLogin() != null ? githubUser.getLogin() : githubUser.getName());
user.setAvatarUrl(githubUser.getAvatarUrl());
user.setAccessToken(accessToken);
user.setLastLogin(LocalDateTime.now());
} else {
// 创建新用户
user = new User();
user.setProvider("github");
user.setProviderId(String.valueOf(githubUser.getId()));
user.setUsername(githubUser.getLogin() != null ? githubUser.getLogin() : githubUser.getName());
user.setEmail(githubUser.getEmail());
user.setAvatarUrl(githubUser.getAvatarUrl());
user.setAccessToken(accessToken);
user.setLastLogin(LocalDateTime.now());
}
return userRepository.save(user);
}
/**
* 通过ID查找用户
*/
public Optional<User> findById(Long id) {
return userRepository.findById(id);
}
}
package com.example.oauth.repository;
import com.example.oauth.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByProviderId(String providerId);
Optional<User> findByEmail(String email);
}
控制器
package com.example.oauth.controller;
import com.example.oauth.model.User;
import com.example.oauth.service.GitHubOAuthService;
import com.example.oauth.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/oauth")
public class OAuthController {
@Autowired
private GitHubOAuthService gitHubOAuthService;
@Autowired
private JwtUtil jwtUtil;
@Value("${app.frontend-callback}")
private String frontendCallback;
/**
* 发起GitHub登录
*/
@GetMapping("/github/login")
public void githubLogin(HttpServletResponse response) throws IOException {
String authorizationUrl = gitHubOAuthService.getAuthorizationUrl();
response.sendRedirect(authorizationUrl);
}
/**
* GitHub回调
*/
@GetMapping("/github/callback")
public ResponseEntity<?> githubCallback(
@RequestParam(value = "code") String code,
@RequestParam(value = "state", required = false) String state,
HttpServletResponse response) throws IOException {
try {
// 处理OAuth回调
User user = gitHubOAuthService.handleOAuthCallback(code);
// 生成JWT令牌
String token = jwtUtil.generateToken(user.getId(), user.getUsername(), user.getProvider());
// 构建响应数据
Map<String, Object> authResponse = new HashMap<>();
authResponse.put("token", token);
authResponse.put("userId", user.getId());
authResponse.put("username", user.getUsername());
authResponse.put("avatarUrl", user.getAvatarUrl());
authResponse.put("provider", user.getProvider());
// 重定向到前端并携带token
String redirectUrl = frontendCallback + "?token=" + token
+ "&userId=" + user.getId()
+ "&username=" + user.getUsername();
response.sendRedirect(redirectUrl);
return null; // 已经重定向
} catch (Exception e) {
e.printStackTrace();
response.sendRedirect(frontendCallback + "?error=authentication_failed");
return null;
}
}
/**
* 获取当前用户信息
*/
@GetMapping("/user/info")
public ResponseEntity<?> getUserInfo(@RequestHeader("Authorization") String authorization) {
try {
String token = authorization.replace("Bearer ", "");
if (jwtUtil.validateToken(token)) {
org.springframework.security.core.userdetails.User user =
org.springframework.security.core.userdetails.User
.withUsername("placeholder")
.password("")
.roles("USER")
.build();
Map<String, Object> response = new HashMap<>();
response.put("valid", true);
response.put("tokenInfo", jwtUtil.parseToken(token));
return ResponseEntity.ok(response);
} else {
return ResponseEntity.status(401).body("Invalid token");
}
} catch (Exception e) {
return ResponseEntity.status(401).body("Invalid token");
}
}
}
安全配置
package com.example.oauth.config;
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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.cors().and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/oauth/github/login", "/oauth/github/callback").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.formLogin().disable();
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
主应用类
package com.example.oauth;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties
public class OAuthApplication {
public static void main(String[] args) {
SpringApplication.run(OAuthApplication.class, args);
}
}
前端示例 (HTML)
<!DOCTYPE html>
<html>
<head>OAuth Login Demo</title>
</head>
<body>
<h1>第三方登录示例</h1>
<button onclick="loginWithGitHub()">使用GitHub登录</button>
<div id="userInfo" style="display: none;">
<h2>用户信息</h2>
<p>用户名: <span id="username"></span></p>
<img id="avatar" width="100" height="100"/>
</div>
<script>
// 获取URL参数
function getUrlParam(name) {
const url = new URL(window.location.href);
return url.searchParams.get(name);
}
// 登录函数
function loginWithGitHub() {
window.location.href = 'http://localhost:8080/oauth/github/login';
}
// 处理登录回调
window.onload = function() {
const token = getUrlParam('token');
const username = getUrlParam('username');
if (token && username) {
document.getElementById('username').textContent = username;
document.getElementById('userInfo').style.display = 'block';
// 存储token
localStorage.setItem('auth_token', token);
// 获取完整用户信息
fetchUserInfo(token);
// 清理URL参数
window.history.replaceState({}, document.title, window.location.pathname);
}
};
// 获取用户详细信息
async function fetchUserInfo(token) {
try {
const response = await fetch('http://localhost:8080/oauth/user/info', {
headers: {
'Authorization': `Bearer ${token}`
}
});
const userData = await response.json();
console.log('User data:', userData);
} catch (error) {
console.error('Error fetching user info:', error);
}
}
</script>
</body>
</html>
SQL脚本
-- 创建数据库
CREATE DATABASE IF NOT EXISTS oauth_demo DEFAULT CHARACTER SET utf8mb4;
USE oauth_demo;
-- 用户表
CREATE TABLE IF NOT EXISTS users (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255),
username VARCHAR(255),
avatar_url VARCHAR(500),
provider VARCHAR(50),
provider_id VARCHAR(100),
access_token TEXT,
created_at DATETIME,
last_login DATETIME,
UNIQUE KEY uk_provider_id (provider_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
GitHub OAuth应用配置
- 登录GitHub
- 进入 Settings → Developer settings → OAuth Apps
- 点击 "New OAuth App"
- 填写配置:
- Application name: 你的应用名称
- Homepage URL: http://localhost:8080
- Authorization callback URL: http://localhost:8080/oauth/github/callback
- 获取 Client ID 和 Client Secret
使用说明
-
获取GitHub OAuth凭据:在GitHub开发者设置中创建OAuth应用,获取Client ID和Client Secret。
-
配置应用:更新
application.yml中的GitHub凭据。 -
运行数据库:启动MySQL,执行SQL脚本。
-
启动应用:运行
OAuthApplication主类。 -
测试流程:
- 访问前端页面
- 点击"使用GitHub登录"
- 跳转到GitHub授权页面
- 授权后回调到我们的应用
- 应用创建用户并返回JWT令牌
扩展其他平台
按类似方式可以扩展Google、微信等平台的登录:
public interface OAuthService {
String getAuthorizationUrl();
String getAccessToken(String code);
Object getUserInfo(String accessToken);
User handleOAuthCallback(String code);
}
// Google OAuth Service 实现示例
@Service
public class GoogleOAuthService implements OAuthService {
// 实现gooogle OAuth逻辑
}
这个案例提供了完整的GitHub第三方登录实现,包括OAuth授权流程、用户管理、JWT生成验证等核心功能,可以根据需要扩展其他平台的支持。