在 Spring Boot 中禁用 Keycloak Security

1、概览 Keycloak 是一个开源的身份和访问管理解决方案。在测试阶段,禁用 Keycloak 可能有助于专注于业务测试。而且在测试环境中可能没有 Keycloak 服务器。 本文将带你了解如何禁用 Keycloak starter 的配置,以及如何在项目中启用 Spring Security 后如何对其进行修改。 2、在非 Spring Security 环境下禁用 Keycloak 我们首先来看看如何在不使用 Spring Security 的应用中禁用 Keycloak。 2.1、应用设置 首先,添加 spring-boot-starter-oauth2-client 依赖。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> </dependency> 此外,还需要添加 spring-boot-starter-oauth2-resource-server 依赖。它将允许我们使用 Keycloak 服务器验证 JWT Token: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency> 接下来,在 application.properties 中添加 Keycloak 服务器的配置: spring.security.oauth2.client.registration.keycloak.client-id=login-app spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code spring.security.oauth2.client.registration.keycloak.scope=openid spring.security.oauth2.client.provider.keycloak.issuer-uri= http://localhost:8080/realms/SpringBootKeycloak spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/SpringBootKeycloak 最后,添加 UserController 来检索用户: @RestController @RequestMapping("/users") public class UserController { @GetMapping("/{userId}") public User getCustomer(@PathVariable Long userId) { return new User(userId, "John", "Doe"); } } 2.

在 Spring Boot 中使用 Spring Security + JWT + MySQL 实现基于 Token 的身份认证

本文将会带你了解在 Spring Boot 中如何使用 Spring Security、JWT 和 MySQL 数据库实现基于 Token 的身份认证。 JWT (JSON Web Token)概览 JWT 是 JSON Web Token 的缩写,是一种安全地在各方之间传输信息的开放标准。它是一种紧凑、自包含的数据传输方法,通常用于客户端和服务器之间的数据传输。 JWT 通常用于认证和授权,服务器通过验证 JWT 中包含的数字签名来验证用户。 JWT 由三部分组成:Header、Payload 和 Signature(签名)。 Header 包含 Token 类型和 Token 签名算法的元数据。 Payload 包含关于被验证用户或实体的声明(Claim)或陈述。这些声明可包括用户 ID、用户名或电子邮件地址等信息。 Signature(签名)使用秘钥和 Header 及 Payload 生成,以确保 JWT 的完整性。 使用 JWT 的一个好处是它们是无状态的,这意味着服务器无需跟踪用户的身份认证状态。这可以提高可扩展性和性能。此外,JWT 可以在不同的域和服务中使用,只要它们共享相同的秘钥来验证签名即可。 Spring Security 概览 Spring Security 是一个提供身份认证、授权和防护常见攻击的框架。它为确保 Web 和响应式应用程序的安全提供一流的支持,是保护基于 Spring 的应用程序的事实标准。 Spring Security 用于保护 Web 应用程序、REST API 和微服务的安全,为身份认证和授权提供内置支持。 数据库表结构 添加 Maven 依赖 添加如下依赖到 Spring Boot 项目:

使用 @ExceptionHandler 处理 Spring Security 异常

1、概览 本文将会带你了解如何使用 @ExceptionHandler 和 @ControllerAdvice 全局处理 Spring Security 异常。 Controller Advice 是一种拦截器,常用于处理全局异常。 2、Spring Security 异常 Spring Security 核心异常(如 AuthenticationException 和 AccessDeniedException)属于运行时异常。由于这些异常是由 DispatcherServlet 后面的 Authentication Filter 在调用 Controller 方法之前抛出的,因此 @ControllerAdvice 无法捕获这些异常。 通过添加自定义 Filter 和构建响应体,可以直接处理 Spring Security 异常。要通过@ExceptionHandler 和 @ControllerAdvice 在全局级别处理这些异常,需要自定义 AuthenticationEntryPoint 的实现。AuthenticationEntryPoint 用于发送 HTTP 响应,要求客户端提供凭证。虽然已经有多个内置实现,但是我们仍然需要自己实现,以发送自定义响应。 首先,让我们看看如何在不使用 @ExceptionHandler 的情况下全局处理 Security 异常。 3、不使用 @ExceptionHandler Spring Security 异常是从 AuthenticationEntryPoint 开始的。让我们编写一个 AuthenticationEntryPoint 的实现,用于拦截 Security 异常。 3.1、配置 AuthenticationEntryPoint 实现 AuthenticationEntryPoint 并覆写 commence() 方法: @Component("customAuthenticationEntryPoint") public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { RestError re = new RestError(HttpStatus.

在 Spring Boot 中校验用户上传的图片文件

图片上传是现代应用中非常常见的一种功能,也是风险比较高的一个地方。恶意用户可能会上传一些病毒、木马。这些东西不仅严重威胁服务器的安全还浪费了带宽,磁盘等资源。所以,在图片上传的接口中,一定要对用户上传的文件进行严格的校验。 本文介绍了 2 种对图片文件进行验证的方法可供你参考。 文件后缀校验 通过文件后缀(也就是文件扩展名,通常用于表示文件的类型),进行文件类型校验这是最常见的做法。 图片文件的后缀类型有很多,常见的只有:jpg、jpeg、gif、png、webp。我们可以在配置或者代码中定义一个“允许上传的图片后缀”集合,用于校验用户上传的图片文件。 package cn.springdoc.demo.web.controller; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Set; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping("/upload") public class UploadController { // 允许上传的图片类型的后缀集合 static final Set<String> imageSuffix = Set.of("jpg", "jpeg", "gif", "png", "webp"); @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity<String> upload (@RequestParam("file") MultipartFile file ) throws IllegalStateException, IOException{ // 文件的原始名称 String fileName = file.getOriginalFilename(); if (fileName == null) { return ResponseEntity.

Spring 的 beanName 设置可能会导致代理失败

一些使用小细节就是在不断的源码探索中逐步发现的,今天就来和大家研究一下通过 beanName 的设置,可以让一个 bean 拒绝被代理的问题! 1. 代码实践 假设我有如下一个切面: @Aspect @EnableAspectJAutoProxy @Component public class LogAspect { @Pointcut("execution(* org.javaboy.demo.service.*.*(..))") public void pc() { } @Before("pc()") public void before(JoinPoint jp) { String name = jp.getSignature().getName(); System.out.println(name + " 方法开始执行了..."); } } 这个切面要拦截的方法是 org.javaboy.demo.service 包下的所有类的所有方法,现在,这个包下有一个 BookService 类,内容如下: @Service("org.javaboy.demo.service.BookService.ORIGINAL") public class BookService { public void hello() { System.out.println("hello bs"); } } 这个 BookService 的 beanName 我没有使用默认的 beanName,而是自己配置了一个 beanName,这个 beanName 的配置方式是 类名的完整路径 + .ORIGINAL。 当我们按照这样的规则给 bean 取名之后,那么即使当前 bean 已经包含在切点所定义的范围内,这个 bean 也不会被代理了。

在 Spring Cloud Gateway 中修改响应体

1、简介 本文将带你了解如何在 Spring Cloud Gateway 中读取、修改响应体,然后再响应给客户端。 2、Spring Cloud Gateway 快速回顾 Spring Cloud Gateway(简称 SCG)是 Spring Cloud 系列中的一个子项目,它提供了一个构建在响应式 Web 栈之上的 API 网关。关于它的更多详细信息和用法,你可以参考 官方文档。 设计 API Gateway 解决方案时经常出现的一种特殊使用场景:如何在将后端响应的 Body 发送回客户端之前对其进行处理? 下面列出了一些可能会用到这种功能的场景: 保持与现有客户端的兼容性,同时允许后台不断迭代 需要屏蔽响应中的某些敏感字段(脱敏) 实现这个需求,只需要实现一个 Filter 来处理后台响应即可。Filter 是 SCG 的核心概念。 Filter 组件创建后就可以将其应用于任何已声明的路由(Route)。 3、实现数据过滤 Filter 创建一个简单的 Filter 来屏蔽 JSON 响应中的某些值。 例如,给定的 JSON 有一个名为 “ssn” 的字段: { "name" : "John Doe", "ssn" : "123-45-9999", "account" : "9999888877770000" } 我们希望用一个固定的值进行替换,从而防止数据泄漏: { "name" : "John Doe", "ssn" : "****", "account" : "9999888877770000" } 3.

使用 Spring 注解实例化同一个类的多个 Bean

1、概览 Spring IoC 容器创建和管理 Spring Bean,这些 Bean 是应用的核心。创建一个 Bean 实例与从普通的 Java 类创建对象相同。然而,生成多个相同类的 Bean 可能会比较麻烦一点。 本文将带你了解如何在 Spring 中使用注解创建同一个类的多个 Bean。 2、使用 Java 配置 这是使用注解创建多个同类 Bean 的最简单易行的方法。 举一个简单的例子。我们有一个 Person 类,它有两个字段:firstName 和 lastName: public class Person { private String firstName; private String lastName; public Person(String firstName, String secondName) { super(); this.firstName = firstName; this.lastName = secondName; } @Override public String toString() { return "Person [firstName=" + firstName + ", secondName=" + lastName + "]"; } } 接下来,构建一个名为 PersonConfig 的配置类,并在其中定义 Person 类的多个 Bean:

使用 Testcontainers 测试 Redis

1、概览 Testcontainers 是一个用于创建临时 Docker 容器进行单元测试的 Java 库。当我们想要避免使用实际服务器进行测试时,它非常有用。 本文将会带你了解如何在 Spring Boot 中使用 Testcontainers 测试 Redis。 2、项目设置 使用任何测试容器的首要前提是在运行测试的机器上安装 Docker。 安装好 Docker 后,就可以开始设置 Spring Boot 应用了。 在此应用中,我们将设置一个 Redis Hash、一个 Repository 和一个使用 Repository 与 Redis 交互的 Service。 2.1、依赖 添加所需的 spring-boot-starter-test 和 spring-boot-starter-data-redis 依赖。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 接着,还要添加 Testcontainers 依赖: <dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers</artifactId> <version>1.17.2</version> <scope>test</scope> </dependency> 2.2、配置 在 application.properties 文件中添加 Redis 连接的详细信息: spring.redis.host=127.0.0.1 spring.redis.port=6379 3、应用设置 我们要创建一个小型应用,向 Redis 数据库读写 Product(产品)。 3.1、Entity 创建 Product 类。

使用 Testcontainers 对 Keycloak 进行集成测试

1、简介 通常我们会通过集成测试来验证应用功能是否正常。集成测试至关重要,特别是对于认证这种敏感且重要的功能。Testcontainers 允许在测试阶段启动 Docker 容器,以便对实际的技术栈运行测试。 本文将带你了解如何使用 Testcontainers 针对实际的 Keycloak 实例设置集成测试。 2、配置 Spring Security 和 Keycloak 我们需要配置 Spring Security、Keycloak 和 Testcontainers。 2.1、整合 Spring Boot 和 Spring Security 在 pom.xml 中添加 spring-boot-starter-security 依赖。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> 接着,创建一个示例 Controller,它返回一个 User。 @RestController @RequestMapping("/users") public class UserController { @GetMapping("me") public UserDto getMe() { return new UserDto(1L, "janedoe", "Doe", "Jane", "jane.doe@baeldung.com"); } } 至此,我们有了一个受保护的 Controller,用于处理对 /users/me 端点的请求。启动应用时,Spring Security 会为用户 user 生成一个密码,该密码在控制台输出的日志中。 2.配置 Keycloak 启动本地 Keycloak 的最简单方法是使用 Docker。

使用 @NotNull 注解进行非空校验

1、概览 空指针异常 NullPointerException 是一个常见问题,避免这种问题的方法之一是在方法参数上添加 @NotNull 等校验注解。 给方法参数添加了 @NotNull 注解后,还需要其他的一些设置才能自动对参数进行非空校验。 2、给方法参数添加 @NotNull 注解 创建一个类,其中包含一个返回 String 长度的方法。 在 String 参数上添加 @NotNull 注解: public class NotNullMethodParameter { public int validateNotNull(@NotNull String data) { return data.length(); } } 注意,有多个包下都有 @NotNull 注解,我们使用的应该是 jakarta.validation.constraints 包。 创建 NotNullMethodParameter 实例,然后使用 null 参数调用方法。 NotNullMethodParameter notNullMethodParameter = new NotNullMethodParameter(); notNullMethodParameter.doesNotValidate(null); 尽管在参数上使用了 @NotNull,但还是出现了空指针异常:NullPointerException。 注解未生效,因为没有 Validator 来执行它。 3、添加 Validator 添加 Hibernate Validator(jakarta.validation 的实现)来识别 @NotNull。 <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>8.0.0.Final</version> </dependency> 使用默认的 ValidatorFactory 创建 validator。