第6章 Spring Security 的 Web 安全性(2024 最新版)(上)

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 第6章 Spring Security 的 Web 安全性(2024 最新版)

9526b1d6ced59dd11678343f8411959.png

6.1 CSRF 防护

跨站请求伪造(CSRF)是一种网络攻击方式,攻击者诱使用户在不知情的情况下,通过用户已登录的Web应用发送恶意请求。幸运的是,通过适当的防护措施,可以有效防止这类攻击。

6.1.1 基础知识详解

跨站请求伪造(CSRF)是一种常见的网络攻击,它利用了Web应用中用户会话的自动化完成机制。在不知情的情况下,攻击者可以诱导用户执行非预期的操作,如更改密码、转账等。了解CSRF的基本概念和防护机制对于开发安全的Web应用至关重要。

CSRF 攻击原理

  1. 用户登录:用户在某个Web应用中登录,并在其浏览器中保持了登录状态(通常是通过Cookies)。
  2. 恶意请求:用户访问了一个恶意网站,该网站包含了一个指向上述Web应用的恶意请求(例如,一个隐藏的表单或者一段JavaScript代码)。
  3. 自动提交:由于用户的浏览器仍然保持着对目标Web应用的登录状态,所以恶意请求会自动携带用户的会话Cookies并被提交。
  4. 非预期操作:如果目标Web应用没有适当的防护机制,恶意请求就可能以用户的身份执行非预期的操作。

CSRF 防护机制

  1. CSRF 令牌:最常见的防护机制是使用CSRF令牌(也称为anti-CSRF令牌)。每次用户发起请求时,服务器会生成一个唯一的、不可预测的令牌,并在响应中返回给用户(通常是作为表单的一部分或通过HTTP头发送)。然后,用户在后续请求中必须提交这个令牌,服务器将验证令牌的有效性。
  2. 双重提交Cookies:这种方法不需要在服务器端存储CSRF令牌。它假定攻击者无法读取或设置目标站点的Cookies。令牌存储在一个Cookie中,并且通过请求(如表单字段)再次提交。服务器验证Cookie中的令牌和请求中的令牌是否匹配。
  3. 自定义请求头:由于跨站请求通常无法设置自定义请求头,因此检查HTTP请求中是否存在自定义头(如X-Requested-With)也是一种简单有效的防护手段。
  4. Referer验证:检查HTTP请求的Referer头部可以帮助确定请求是否来自于信任的来源。然而,由于隐私考虑,一些用户或浏览器可能会禁用或篡改Referer头部,限制了这种方法的有效性。

最佳实践

  • 为所有表单和状态改变请求使用CSRF令牌:确保每个执行状态改变的请求(如POST、PUT、DELETE等)都需要一个有效的CSRF令牌。
  • 使用框架提供的CSRF防护:许多Web框架(如Spring Security)提供了内置的CSRF防护支持,开发者应该充分利用这些特性来保护应用。
  • 定期旋转CSRF令牌:定期更换CSRF令牌可以减少令牌被猜测或泄露的风险。
  • 为CSRF令牌设置适当的作用域:令牌应该是特定于用户会话的,并且应该限制其在应用中的作用域,以避免跨站点的泄露风险。

通过理解CSRF攻击的工作原理和实施有效的防护措施,开发者可以显著提高Web应用的安全性,保护用户免受这种攻击方式的影响。

6.1.2 重点案例:Spring Security 中的 CSRF 防护

Spring Security 提供了一套全面的 CSRF 防护机制,确保应用安全地处理每一个请求。通过实用的案例,我们将一步步探索如何在 Spring Boot 应用中利用 Spring Security 实现 CSRF 防护。

案例 Demo

假设我们正在开发一个简单的博客平台,用户需要登录后才能发布文章。以下是如何通过 Spring Security 添加 CSRF 防护的步骤。

步骤 1: 启用 Spring Security

首先,确保你的 Spring Boot 应用中已经添加了 Spring Security 依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

步骤 2: 配置 CSRF 保护

在 Spring Security 配置中,CSRF 保护默认是启用的。但是,为了展示如何显式启用 CSRF 保护,并演示如何配置,我们将在 WebSecurityConfig 类中添加相关配置。

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // 其他配置...
            .csrf().enable(); // 显式启用 CSRF 保护
    }
}

步骤 3: 在表单中包含 CSRF 令牌

对于每个需要 CSRF 保护的表单,Spring Security 需要你在表单中包含一个 CSRF 令牌。如果你使用的是 Thymeleaf,它会自动为你的表单添加 CSRF 令牌。

<form action="/postArticle" method="post">
    <input type="text" name="title" />
    <input type="text" name="content" />
    <!-- Thymeleaf 自动添加 CSRF 令牌 -->
    <button type="submit">发布文章</button>
</form>

步骤 4: 处理 AJAX 请求中的 CSRF 令牌

对于 AJAX 请求,你需要手动将 CSRF 令牌添加到请求的头部。

首先,从页面中获取 CSRF 令牌(假设你已经将其存储在了一个 meta 标签中)。

<meta name="_csrf" content="${_csrf.token}"/>
<meta name="_csrf_header" content="${_csrf.headerName}"/>

然后,在发送 AJAX 请求时,从 meta 标签中读取并添加 CSRF 令牌。

const csrfToken = document.querySelector('meta[name="_csrf"]').getAttribute('content');
const csrfHeader = document.querySelector('meta[name="_csrf_header"]').getAttribute('content');
fetch('/postArticle', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        [csrfHeader]: csrfToken // 添加 CSRF 令牌到请求头
    },
    body: JSON.stringify({title: '新文章', content: '文章内容'})
});

测试 CSRF 防护

启动应用并尝试发布文章。你会发现,只有当请求中包含有效的 CSRF 令牌时,文章才能成功发布。

通过这个案例,你可以看到 Spring Security 如何帮助我们简单有效地实现 CSRF 防护,从而保护应用免受跨站请求伪造攻击。无论是传统的表单提交还是现代的 AJAX 请求,Spring Security 都为我们提供了强大的工具和机制来确保每个请求的安全。

6.1.3 拓展案例 1:自定义 CSRF 令牌仓库

在某些场景下,应用可能需要更灵活地处理 CSRF 令牌,比如在分布式系统中共享 CSRF 令牌或在客户端和服务器之间以不同的方式传递令牌。这时,通过实现自定义 CSRF 令牌仓库(CsrfTokenRepository)来满足这些特定需求就显得尤为重要。

案例 Demo

假设我们的应用需要将 CSRF 令牌存储在 Redis 中,以支持在多个实例间共享令牌。以下是如何实现一个自定义的 CSRF 令牌仓库的步骤。

步骤 1: 实现 CsrfTokenRepository

首先,创建一个新的类 RedisCsrfTokenRepository 实现 CsrfTokenRepository 接口。这个类将负责从 Redis 获取和存储 CSRF 令牌。

public class RedisCsrfTokenRepository implements CsrfTokenRepository {
    private final RedisTemplate<String, CsrfToken> redisTemplate;
    public RedisCsrfTokenRepository(RedisTemplate<String, CsrfToken> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    @Override
    public CsrfToken generateToken(HttpServletRequest request) {
        String tokenId = UUID.randomUUID().toString();
        return new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", tokenId);
    }
    @Override
    public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) {
        String key = resolveSessionKey(request);
        if (token == null) {
            redisTemplate.delete(key);
        } else {
            redisTemplate.opsForValue().set(key, token, 30, TimeUnit.MINUTES);
        }
    }
    @Override
    public CsrfToken loadToken(HttpServletRequest request) {
        String key = resolveSessionKey(request);
        return redisTemplate.opsForValue().get(key);
    }
    private String resolveSessionKey(HttpServletRequest request) {
        return "csrf:" + request.getSession().getId();
    }
}

注意:在实际应用中,你需要配置 RedisTemplate 以正确序列化和反序列化 CsrfToken 对象。

步骤 2: 配置 Spring Security 使用自定义 CSRF 令牌仓库

接下来,在你的 Spring Security 配置中指定应用使用 RedisCsrfTokenRepository 作为 CSRF 令牌仓库。

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private RedisTemplate<String, CsrfToken> redisTemplate;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf()
            .csrfTokenRepository(new RedisCsrfTokenRepository(redisTemplate));
    }
}

通过这种方式,应用的 CSRF 令牌将被存储在 Redis 中,支持在多个应用实例间共享令牌,这对于构建可扩展的分布式应用尤为重要。

测试自定义 CSRF 令牌仓库

现在,当用户访问应用并生成 CSRF 令牌时,这些令牌将被存储在 Redis 中。无论用户是通过哪个实例发起请求,应用都能从 Redis 中检索到相应的 CSRF 令牌,并进行验证。

通过实现自定义的 CSRF 令牌仓库,你可以根据应用的具体需求灵活地处理 CSRF 令牌,无论是在客户端和服务器之间自定义传递令牌的方式,还是在分布式系统中共享令牌,都能提供一个安全可靠的解决方案。

6.1.4 拓展案例 2:禁用特定请求的 CSRF 防护

虽然 CSRF 防护是保护 Web 应用安全的重要机制,但在某些情况下,你可能需要为特定的请求路径禁用 CSRF 防护。例如,对于某些第三方服务的回调端点或是性能敏感的 API,可能不适合执行 CSRF 检查。Spring Security 提供了灵活的配置选项,允许你根据需要调整 CSRF 防护的范围。

案例 Demo

假设我们有一个应用,其中包含一个第三方支付服务的回调端点 /payment/notify,由于这个端点是由支付服务提供商直接调用的,用户不会直接与之交互,因此我们可以安全地为这个特定路径禁用 CSRF 防护。

步骤 1: 配置 Spring Security

在 Spring Security 的配置类中,使用 ignoringAntMatchers() 方法指定需要禁用 CSRF 防护的路径。

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf()
            .ignoringAntMatchers("/payment/notify") // 禁用 /payment/notify 端点的 CSRF 防护
            .and()
            .authorizeRequests()
            .antMatchers("/payment/notify").permitAll() // 允许所有用户无需认证即可访问
            .anyRequest().authenticated(); // 其他所有请求都需要认证
    }
}

通过这种配置,/payment/notify 端点将不再要求请求中包含 CSRF 令牌,同时允许未经认证的用户访问,确保支付服务的回调请求能够成功到达并被处理。

步骤 2: 处理回调请求

接下来,实现一个控制器来处理 /payment/notify 端点的请求。

@RestController
public class PaymentController {
    @PostMapping("/payment/notify")
    public ResponseEntity<String> paymentNotification(@RequestBody PaymentNotification notification) {
        // 处理支付通知逻辑
        return ResponseEntity.ok("Payment processed");
    }
}

测试禁用 CSRF 防护

启动应用后,尝试向 /payment/notify 端点发送一个 POST 请求,即使请求中不包含 CSRF 令牌,请求也应该成功被接受和处理。

通过在特定场景下禁用 CSRF 防护,你可以为需要与外部系统交互的端点提供灵活性,同时仍然保持应用的安全性。重要的是,这种配置应谨慎使用,确保不会意外地为敏感操作禁用了 CSRF 防护,从而降低安全风险。

通过这些案例,你可以看到 CSRF 防护是网络安全中的重要组成部分,Spring Security 提供了强大而灵活的机制来保护应用免受 CSRF 攻击。正确实施 CSRF 防护措施,可以有效提升应用的安全性。

6.2 跨域请求处理(CORS)

在构建现代 Web 应用时,跨域资源共享(CORS)是一个常见的挑战。CORS 是一种机制,它允许限制的资源(如字体、JavaScript 等)在一个网页应用被另一个不同源(域名、协议或端口)的网页请求时,如何被请求。理解和合理配置 CORS 对于保护 Web 应用、提供灵活的服务消费体验至关重要。

6.2.1 基础知识详解

跨域资源共享(CORS)是一个 W3C 标准,允许网页脚本能够向不同源(域、协议或端口)的服务器发出请求,从而克服了 AJAX 直接由浏览器同源政策所施加的限制。在深入探讨如何在应用中实现和配置 CORS 之前,了解其基础知识和工作原理是非常重要的。

同源政策

  • 定义:同源政策是一种约定,它限制了一个源的文档或脚本如何与另一个源的资源进行交互。这是一个重要的安全机制,用于隔离潜在恶意文件。
  • 源的定义:源是由协议、域名和端口三部分组成的。只有当这三部分完全匹配时,两个 URL 才属于同一个源。

CORS 工作原理

  • 简单请求
  • 满足特定条件的请求被视为简单请求(例如使用 GET、HEAD 或 POST 方法,且 HTTP 头部限于一组特定集合)。
  • 浏览器直接发出简单请求,但会在请求头中添加 Origin 字段,服务器根据这个字段决定是否允许该跨域请求。
  • 预检请求(Preflight)
  • 不符合简单请求条件的请求,浏览器会先发送一个预检请求,使用 OPTIONS 方法,询问服务器是否允许该跨域请求。
  • 预检请求的响应中,服务器可以指定允许的方法、头部信息和是否允许携带凭证等信息。

CORS 响应头

  • Access-Control-Allow-Origin:指定哪些网站可以参与跨域资源共享。
  • Access-Control-Allow-Methods:指明在实际请求中允许使用的 HTTP 方法。
  • Access-Control-Allow-Headers:指明实际请求中允许携带的首部字段。
  • Access-Control-Allow-Credentials:指明是否允许发送 Cookie。
  • Access-Control-Max-Age:指明预检请求的结果能够被缓存多久。

实现 CORS 支持

  • 服务器端配置:服务器需要正确处理和响应跨域请求,包括处理预检请求并在响应中设置适当的 CORS 相关头部。
  • 客户端支持:在发起跨域 AJAX 请求时,客户端(如 Web 浏览器)将自动处理 CORS 流程,开发者需要确保请求符合 CORS 要求,可能需要设置特定的请求头。

安全考虑

  • 精确配置Access-Control-Allow-Origin 不应该设置为 *(表示接受任意域的请求),除非资源完全公开。对于需要凭证的请求,这个值必须是请求页面的完整源,不可以使用 *
  • 限制方法和头部:仅允许应用所需的方法和头部,减少潜在的攻击面。

CORS 的正确实现和配置对于保护 Web 应用的安全、提供跨域服务至关重要。理解其基础知识有助于开发者在保障安全的同时,实现应用的灵活访问和资源共享。

6.2.2 重点案例:在 Spring Boot 应用中配置 CORS

在这个案例中,我们将通过一个 Spring Boot 应用来展示如何灵活地配置 CORS,使得前端应用能够安全地跨域请求后端资源。我们假设后端API托管在 https://api.example.com,而前端应用运行在 https://app.example.com

案例 Demo

步骤 1: 全局 CORS 配置

全局配置是一种简单且通用的方式来设置 CORS,适用于整个 Spring Boot 应用。在 WebSecurityConfig 类中通过重写 configure(HttpSecurity http) 方法实现。

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // 其他安全配置...
            .cors(cors -> cors
                .configurationSource(request -> {
                    CorsConfiguration config = new CorsConfiguration();
                    config.setAllowedOrigins(List.of("https://app.example.com"));
                    config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
                    config.setAllowedHeaders(List.of("Content-Type", "Authorization"));
                    config.setAllowCredentials(true);
                    return config;
                })
            );
    }
}

这段代码中,我们为来自 https://app.example.com 的请求配置了 CORS,允许了常用的 HTTP 方法,并允许携带认证信息(如 Cookies 和 HTTP 认证)。

步骤 2: 控制器级别的 CORS 配置

如果你想对特定的控制器或请求映射进行更细粒度的 CORS 配置,可以使用 @CrossOrigin 注解。

@RestController
@RequestMapping("/api")
public class DataController {
    @CrossOrigin(origins = "https://app.example.com", methods = {RequestMethod.GET, RequestMethod.POST}, maxAge = 3600)
    @GetMapping("/data")
    public ResponseEntity<String> getData() {
        return ResponseEntity.ok("Data from backend");
    }
}

在这个例子中,/api/data 端点专门为 https://app.example.com 配置了 CORS,允许 GET 和 POST 请求,并设置了预检请求的缓存时间为 3600 秒。

步骤 3: 使用 WebMvcConfigurer 自定义 CORS 配置

对于需要更复杂的 CORS 配置,例如根据请求动态决定允许的源,可以实现 WebMvcConfigurer 接口并重写 addCorsMappings 方法。

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("https://app.example.com")
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                .allowedHeaders("Content-Type", "Authorization")
                .allowCredentials(true)
                .maxAge(3600);
    }
}

这种方法提供了对 CORS 配置全面的控制,允许你针对不同的路径模式定义不同的策略。

测试 CORS 配置

启动你的 Spring Boot 应用后,可以使用前端 JavaScript 代码或者 Postman 发起跨域请求测试 CORS 配置是否生效。例如,从 https://app.example.com 页面上发起对 https://api.example.com/api/data 的 AJAX 请求,应该能够成功获取数据而不会遇到 CORS 错误。

通过上述案例,你可以看到在 Spring Boot 应用中配置 CORS 的多种方式。这些配置确保了后端服务能够安全地响应来自不同源的前端请求,同时保持了应用的灵活性和安全性。


第6章 Spring Security 的 Web 安全性(2024 最新版)(下)+https://developer.aliyun.com/article/1487153

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
42 4
|
4月前
|
安全 Java 数据安全/隐私保护
使用Spring Security实现细粒度的权限控制
使用Spring Security实现细粒度的权限控制
|
2月前
|
JSON 算法 安全
Web安全-JWT认证机制安全性浅析
Web安全-JWT认证机制安全性浅析
30 2
|
2月前
|
前端开发 安全 Java
技术进阶:使用Spring MVC构建适应未来的响应式Web应用
【9月更文挑战第2天】随着移动设备的普及,响应式设计至关重要。Spring MVC作为强大的Java Web框架,助力开发者创建适应多屏的应用。本文推荐使用Thymeleaf整合视图,通过简洁的HTML代码提高前端灵活性;采用`@ResponseBody`与`Callable`实现异步处理,优化应用响应速度;运用`@ControllerAdvice`统一异常管理,保持代码整洁;借助Jackson简化JSON处理;利用Spring Security增强安全性;并强调测试的重要性。遵循这些实践,将大幅提升开发效率和应用质量。
63 7
|
3月前
|
Java API 数据库
详细介绍如何使用Spring Boot简化Java Web开发过程。
Spring Boot简化Java Web开发,以轻量级、易用及高度可定制著称。通过预设模板和默认配置,开发者可迅速搭建Spring应用。本文通过创建RESTful API示例介绍其快速开发流程:从环境准备、代码编写到项目运行及集成数据库等技术,展现Spring Boot如何使Java Web开发变得更高效、简洁。
67 1
|
3月前
|
Java API 数据库
【神操作!】Spring Boot打造RESTful API:从零到英雄,只需这几步,让你的Web应用瞬间飞起来!
【8月更文挑战第12天】构建RESTful API是现代Web开发的关键技术之一。Spring Boot因其实现简便且功能强大而深受开发者喜爱。本文以在线图书管理系统为例,展示了如何利用Spring Boot快速构建RESTful API。从项目初始化、实体定义到业务逻辑处理和服务接口实现,一步步引导读者完成API的搭建。通过集成JPA进行数据库操作,以及使用控制器类暴露HTTP端点,最终实现了书籍信息的增删查改功能。此过程不仅高效直观,而且易于维护和扩展。
56 1
|
3月前
|
Java Spring Maven
Struts 2遇见Spring:这个组合如何颠覆你的Web开发?
【8月更文挑战第31天】在现代Web开发中,Struts 2与Spring的结合使用能显著增强应用的模块化和可维护性。本文将介绍如何整合这两个框架,并提供代码示例。首先,在`pom.xml`中添加Struts 2和Spring的依赖,然后在`struts.xml`中配置Struts 2以识别Spring插件。接着,在Spring配置文件中定义bean,并在Struts 2的Action类中使用`@Autowired`注解进行自动注入。
47 0
|
3月前
|
Java 开发者 前端开发
Struts 2、Spring MVC、Play Framework 上演巅峰之战,Web 开发的未来何去何从?
【8月更文挑战第31天】在Web应用开发中,Struts 2框架因强大功能和灵活配置备受青睐,但开发者常遇配置错误、类型转换失败、标签属性设置不当及异常处理等问题。本文通过实例解析常见难题与解决方案,如配置文件中遗漏`result`元素致页面跳转失败、日期格式不匹配需自定义转换器、`&lt;s:checkbox&gt;`标签缺少`label`属性致显示不全及Action中未捕获异常影响用户体验等,助您有效应对挑战。
88 0
|
3月前
|
Java Spring
🔥JSF 与 Spring 强强联手:打造高效、灵活的 Web 应用新标杆!💪 你还不知道吗?
【8月更文挑战第31天】JavaServer Faces(JSF)与 Spring 框架是常用的 Java Web 技术。本文介绍如何整合两者,发挥各自优势,构建高效灵活的 Web 应用。首先通过 `web.xml` 和 `ContextLoaderListener` 配置 Spring 上下文,在 `applicationContext.xml` 定义 Bean。接着使用 `@Autowired` 将 Spring 管理的 Bean 注入到 JSF 管理的 Bean 中。
64 0
|
3月前
|
Java Spring Apache
Spring Boot邂逅Apache Wicket:一次意想不到的完美邂逅,竟让Web开发变得如此简单?
【8月更文挑战第31天】Apache Wicket与Spring Boot的集成提供了近乎无缝的开发体验。Wicket以其简洁的API和强大的组件化设计著称,而Spring Boot则以开箱即用的便捷性赢得开发者青睐。本文将指导你如何在Spring Boot项目中引入Wicket,通过简单的步骤完成集成配置。首先,创建一个新的Spring Boot项目并在`pom.xml`中添加Wicket相关依赖。
95 0