本文目录导读:

- 项目结构
- 创建Maven项目并配置pom.xml
- 主启动类
- 配置类 - 基础版本
- 配置类 - 高级版本(支持数据库认证)
- 自定义UserDetailsService
- 控制器类
- 模板文件
- 扩展功能 - 用户注册
- 测试类
- 使用说明
- 常用功能说明
我来为你创建一个完整的Spring Security入门案例,包括基础配置、用户认证和权限控制。
项目结构
spring-security-demo
├── pom.xml
└── src/main/java/com/example/demo
├── DemoApplication.java
├── config/
│ └── SecurityConfig.java
├── controller/
│ └── HelloController.java
└── service/
└── CustomUserDetailsService.java
创建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
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.14</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>spring-security-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-security-demo</name>
<description>Spring Security入门案例</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Thymeleaf模板引擎(可选) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Spring Security Thymeleaf 整合(可选) -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<!-- Spring Boot 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Security 测试 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
主启动类
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
配置类 - 基础版本
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 配置密码编码器
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 配置认证信息(内存用户)
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin")
.password(passwordEncoder().encode("admin123"))
.roles("ADMIN")
.and()
.withUser("user")
.password(passwordEncoder().encode("user123"))
.roles("USER");
}
/**
* 配置请求授权规则
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// 配置授权规则
.authorizeRequests()
// 不需要认证的路径
.antMatchers("/", "/public/**", "/login").permitAll()
// 需要ADMIN角色的路径
.antMatchers("/admin/**").hasRole("ADMIN")
// 需要USER或ADMIN角色的路径
.antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
// 其他所有请求都需要认证
.anyRequest().authenticated()
.and()
// 配置表单登录
.formLogin()
.loginPage("/login") // 自定义登录页
.loginProcessingUrl("/doLogin") // 登录提交路径
.defaultSuccessUrl("/home") // 登录成功跳转
.permitAll()
.and()
// 配置登出
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
.permitAll()
.and()
// 配置记住我
.rememberMe()
.rememberMeParameter("remember-me")
.tokenValiditySeconds(86400); // 24小时
}
}
配置类 - 高级版本(支持数据库认证)
package com.example.demo.config;
import com.example.demo.service.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService userDetailsService;
/**
* 配置密码编码器
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 配置认证信息(数据库认证)
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
/**
* 配置请求授权规则
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// 关闭CSRF(开发环境可关闭,生产环境建议开启)
.csrf().disable()
// 配置授权规则
.authorizeRequests()
.antMatchers("/", "/public/**", "/register", "/login", "/css/**", "/js/**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
.and()
// 配置表单登录
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/doLogin")
.defaultSuccessUrl("/home", true)
.failureUrl("/login?error")
.usernameParameter("username")
.passwordParameter("password")
.permitAll()
.and()
// 配置登出
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.permitAll()
.and()
// 配置记住我(需要数据库支持)
.rememberMe()
.rememberMeParameter("remember-me")
.tokenValiditySeconds(86400)
.userDetailsService(userDetailsService)
// 配置会话管理
.and()
.sessionManagement()
.maximumSessions(1) // 同一用户最多一个会话
.maxSessionsPreventsLogin(false); // 是否阻止新登录
}
}
自定义UserDetailsService
package com.example.demo.service;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
@Service
public class CustomUserDetailsService implements UserDetailsService {
/**
* 模拟数据库用户数据
*/
private static final List<UserAccount> USERS = Arrays.asList(
new UserAccount("admin", "$2a$10$8K1p/a0dL1LXMIgoEDFrwOfMQjzXhEhfCkJ5mJJUzLXGbYhQMoYQO", "ADMIN"), // admin123
new UserAccount("user", "$2a$10$4Wi5qVNBuFOMaNbTFoI0bucKpXgZiUcHwH0YJf5eNxS4AJkQ5D5qi", "USER") // user123
);
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return USERS.stream()
.filter(user -> user.getUsername().equals(username))
.findFirst()
.map(user -> {
return User.withUsername(user.getUsername())
.password(user.getPassword())
.roles(user.getRole())
.build();
})
.orElseThrow(() -> new UsernameNotFoundException("用户不存在: " + username));
}
/**
* 内部用户账号类
*/
private static class UserAccount {
private String username;
private String password;
private String role;
public UserAccount(String username, String password, String role) {
this.username = username;
this.password = password;
this.role = role;
}
public String getUsername() { return username; }
public String getPassword() { return password; }
public String getRole() { return role; }
}
}
控制器类
package com.example.demo.controller;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
@Controller
public class HelloController {
/**
* 首页 - 所有用户可访问
*/
@GetMapping({"/", "/home"})
public String home(Model model, Authentication authentication) {
if (authentication != null) {
model.addAttribute("username", authentication.getName());
model.addAttribute("roles", authentication.getAuthorities());
}
return "home";
}
/**
* 公开页面
*/
@GetMapping("/public")
@ResponseBody
public String publicPage() {
return "这是公开页面,不需要登录就能访问!";
}
/**
* 登录页
*/
@GetMapping("/login")
public String login() {
return "login";
}
/**
* 用户页面 - USER和ADMIN角色可访问
*/
@GetMapping("/user")
public String userPage(Model model) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
model.addAttribute("username", auth.getName());
return "user";
}
/**
* 管理员页面 - 仅ADMIN角色可访问
*/
@GetMapping("/admin")
public String adminPage(Model model) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
model.addAttribute("username", auth.getName());
return "admin";
}
/**
* 获取当前用户信息 - REST接口
*/
@GetMapping("/api/current-user")
@ResponseBody
public String currentUser(Authentication authentication) {
if (authentication != null) {
return "当前登录用户: " + authentication.getName() +
", 角色: " + authentication.getAuthorities();
}
return "未登录";
}
/**
* 访问拒绝页面
*/
@GetMapping("/403")
public String accessDenied() {
return "error/403";
}
}
模板文件
login.html
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">登录页面</title>
<style>
body { font-family: Arial, sans-serif; background: #f5f5f5; display: flex; justify-content: center; align-items: center; height: 100vh; }
.login-container { background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); width: 300px; }
h2 { text-align: center; color: #333; margin-bottom: 20px; }
.form-group { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; color: #555; }
input[type="text"], input[type="password"] { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box; }
.remember-me { display: flex; align-items: center; margin-bottom: 15px; }
.remember-me label { margin: 0 0 0 5px; }
button { width: 100%; padding: 10px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }
button:hover { background: #0056b3; }
.error { color: red; margin-bottom: 10px; text-align: center; }
.logout { color: green; margin-bottom: 10px; text-align: center; }
</style>
</head>
<body>
<div class="login-container">
<h2>用户登录</h2>
<!-- 登录失败信息 -->
<div class="error" th:if="${param.error}">
用户名或密码错误!
</div>
<!-- 登出信息 -->
<div class="logout" th:if="${param.logout}">
您已成功登出!
</div>
<form th:action="@{/doLogin}" method="post">
<div class="form-group">
<label>用户名</label>
<input type="text" name="username" required autofocus>
</div>
<div class="form-group">
<label>密码</label>
<input type="password" name="password" required>
</div>
<div class="remember-me">
<input type="checkbox" name="remember-me" id="rememberMe">
<label for="rememberMe">记住我</label>
</div>
<button type="submit">登录</button>
</form>
</div>
</body>
</html>
home.html
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">首页</title>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
.navbar { background: #333; padding: 10px; color: white; display: flex; justify-content: space-between; }
.navbar a { color: white; margin-left: 15px; text-decoration: none; }
.content { margin-top: 20px; }
.btn { display: inline-block; padding: 10px 20px; background: #007bff; color: white; text-decoration: none; border-radius: 4px; }
</style>
</head>
<body>
<div class="navbar">
<div>
<strong>Spring Security 入门案例</strong>
</div>
<div sec:authorize="isAuthenticated()">
<span>欢迎: <span sec:authentication="name">用户名</span></span>
<a th:href="@{/logout}">退出</a>
</div>
</div>
<div class="content">
<h1>欢迎访问Spring Security入门案例</h1>
<p>当前登录状态:</p>
<ul>
<li>用户名:<span th:text="${username}">未登录</span></li>
<li>角色:<span th:text="${roles}">无</span></li>
</ul>
<h2>可访问的页面:</h2>
<p>
<a class="btn" th:href="@{/public}">公共页面(所有人)</a>
<a class="btn" sec:authorize="isAuthenticated()" th:href="@{/user}">用户页面(USER)</a>
<a class="btn" sec:authorize="hasRole('ADMIN')" th:href="@{/admin}">管理员页面(ADMIN)</a>
</p>
<p>
<a class="btn" th:href="@{/api/current-user}">查看当前用户信息</a>
</p>
</div>
</body>
</html>
user.html
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">用户页面</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.btn { display: inline-block; padding: 10px 20px; background: #007bff; color: white; text-decoration: none; border-radius: 4px; margin-right: 10px; }
</style>
</head>
<body>
<h1>用户页面</h1>
<p>欢迎,<span th:text="${username}"></span>!这是用户专属页面。</p>
<p>只有USER和ADMIN角色才能访问此页面。</p>
<a class="btn" th:href="@{/home}">返回首页</a>
</body>
</html>
admin.html
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">管理员页面</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.btn { display: inline-block; padding: 10px 20px; background: #007bff; color: white; text-decoration: none; border-radius: 4px; margin-right: 10px; }
.admin-tools { border: 2px solid #ffc107; padding: 15px; margin: 20px 0; border-radius: 5px; background: #fff3cd; }
</style>
</head>
<body>
<h1>管理员页面</h1>
<p>欢迎,<span th:text="${username}"></span>!这是管理员专属页面。</p>
<p>只有ADMIN角色才能访问此页面。</p>
<div class="admin-tools">
<h2>系统管理工具(仅管理员可见)</h2>
<ul>
<li>用户管理</li>
<li>权限配置</li>
<li>系统日志</li>
<li>数据库管理</li>
</ul>
</div>
<a class="btn" th:href="@{/home}">返回首页</a>
<a class="btn" th:href="@{/user}">用户页面</a>
</body>
</html>
error/403.html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">访问被拒绝</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }
h1 { color: #dc3545; }
.btn { display: inline-block; padding: 10px 20px; background: #007bff; color: white; text-decoration: none; border-radius: 4px; margin-top: 20px; }
</style>
</head>
<body>
<h1>403 - 访问被拒绝</h1>
<p>您没有权限访问此页面。</p>
<a class="btn" th:href="@{/home}">返回首页</a>
<a class="btn" th:href="@{/logout}">重新登录</a>
</body>
</html>
扩展功能 - 用户注册
package com.example.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class RegisterController {
@Autowired
private PasswordEncoder passwordEncoder;
/**
* 注册页面
*/
@GetMapping("/register")
public String registerPage() {
return "register";
}
/**
* 处理注册请求
*/
@PostMapping("/register")
public String register(@RequestParam String username,
@RequestParam String password,
@RequestParam String confirmPassword) {
if (!password.equals(confirmPassword)) {
return "redirect:/register?error";
}
// 这里应该有保存用户到数据库的逻辑
System.out.println("注册用户: " + username);
System.out.println("加密密码: " + passwordEncoder.encode(password));
return "redirect:/login?registered";
}
}
测试类
package com.example.demo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
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;
@SpringBootTest
@AutoConfigureMockMvc
public class SecurityTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testPublicPage() throws Exception {
mockMvc.perform(get("/public"))
.andExpect(status().isOk());
}
@Test
public void testUnauthorizedAccess() throws Exception {
mockMvc.perform(get("/user"))
.andExpect(status().is3xxRedirection());
}
@Test
@WithMockUser(roles = "USER")
public void testUserAccess() throws Exception {
mockMvc.perform(get("/user"))
.andExpect(status().isOk());
}
@Test
@WithMockUser(roles = "ADMIN")
public void testAdminAccess() throws Exception {
mockMvc.perform(get("/admin"))
.andExpect(status().isOk());
}
@Test
@WithMockUser(roles = "USER")
public void testDeniedAdminAccess() throws Exception {
mockMvc.perform(get("/admin"))
.andExpect(status().isForbidden());
}
}
使用说明
启动项目
mvn spring-boot:run # 或 mvn clean package java -jar target/spring-security-demo-0.0.1-SNAPSHOT.jar
测试账号
| 用户名 | 密码 | 角色 |
|---|---|---|
| admin | admin123 | ADMIN |
| user | user123 | USER |
访问地址
- 首页:http://localhost:8080/
- 登录页:http://localhost:8080/login
- 注册页:http://localhost:8080/register
- 公开页面:http://localhost:8080/public
- 用户页面:http://localhost:8080/user
- 管理员页面:http://localhost:8080/admin
常用功能说明
-
认证流程:用户访问受保护资源 → 未认证跳转登录页 → 输入用户名密码 → 验证通过 → 访问目标资源
-
授权机制:基于RBAC角色控制,不同角色访问不同URL
-
加密存储:使用BCrypt加密算法存储密码
-
记住我功能:勾选"记住我"后,7天内自动登录
-
REST API支持:可以通过REST接口获取当前用户信息
这个案例涵盖了Spring Security的主要功能,你可以在此基础上继续扩展:
- 数据库集成(MySQL/JPA)
- JWT令牌认证
- OAuth2.0单点登录
- 自定义登录页面
- 权限粒度控制等
版权声明: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直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播