本文目录导读:

- 项目结构
- 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生成验证等核心功能,可以根据需要扩展其他平台的支持。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播