You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot OAuth2服务层JUnit测试遇AuthenticationCredentialsNotFoundException解决咨询

解决JUnit测试Spring Security OAuth2服务层时的AuthenticationCredentialsNotFoundException问题

遇到这个问题太常见了——你的服务层代码肯定依赖了Spring Security的SecurityContext来获取当前用户的认证信息,但测试环境默认不会自动初始化这个上下文,自然就抛出找不到Authentication对象的错误了。下面给你几个实用的解决办法,按需选择就行:

方法1:手动在测试方法里初始化SecurityContext

这是最直接的方式,适合简单的测试场景。手动创建一个认证对象塞到上下文里就行:

import org.junit.jupiter.api.Test;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;

public class YourServiceTest {

    @Test
    void testTargetServiceMethod() {
        // 1. 构造测试用的用户信息
        UserDetails testUser = User.withUsername("test_user")
                .password("dummy_pwd")
                .roles("USER")
                .build();
        
        // 2. 创建Authentication认证对象
        Authentication auth = new UsernamePasswordAuthenticationToken(testUser, null, testUser.getAuthorities());
        
        // 3. 把认证对象设置到SecurityContext中
        SecurityContextHolder.getContext().setAuthentication(auth);
        
        // 4. 执行你的服务方法测试
        yourService.targetMethod();
        
        // 可选:测试结束后清理上下文,避免影响其他测试用例
        SecurityContextHolder.clearContext();
    }
}

方法2:用Spring Security的测试注解简化操作

如果你用的是Spring Boot Test,直接用框架提供的注解就能自动帮你处理认证上下文,省不少代码:

用@WithMockUser快速模拟通用用户

适合不需要真实用户数据的场景,注解会自动生成一个模拟的认证对象:

import org.junit.jupiter.api.Test;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class YourServiceTest {

    @Test
    @WithMockUser(username = "mock_user", roles = {"ADMIN", "USER"})
    void testTargetServiceMethod() {
        // 直接执行服务方法就行,SecurityContext已经被初始化
        yourService.targetMethod();
    }
}

用@WithUserDetails加载真实用户数据

如果测试需要用到数据库里的真实用户信息,这个注解会调用你的UserDetailsService来加载用户:

import org.junit.jupiter.api.Test;
import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class YourServiceTest {

    @Test
    @WithUserDetails("real_user_in_db") // 填数据库中存在的用户名
    void testTargetServiceMethod() {
        yourService.targetMethod();
    }
}

方法3:写基类统一处理认证上下文

如果多个测试类都需要相同的认证环境,可以写一个基类,用@BeforeEach和@AfterEach来统一初始化和清理:

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterEach;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;

public class BaseServiceTest {

    @BeforeEach
    void initSecurityContext() {
        UserDetails defaultTestUser = User.withUsername("default_test_user")
                .password("dummy_pwd")
                .roles("USER")
                .build();
        Authentication auth = new UsernamePasswordAuthenticationToken(defaultTestUser, null, defaultTestUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(auth);
    }
    
    @AfterEach
    void clearSecurityContext() {
        SecurityContextHolder.clearContext();
    }
}

然后让你的测试类继承这个基类:

public class YourServiceTest extends BaseServiceTest {

    @Test
    void testTargetServiceMethod() {
        yourService.targetMethod();
    }
}

方法4:针对JWT场景模拟OAuth2认证

如果你的服务是基于JWT的OAuth2,可以直接模拟JwtAuthenticationToken:

import org.junit.jupiter.api.Test;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;

public class YourServiceTest {

    @Test
    void testJwtBasedServiceMethod() {
        // 构造模拟的JWT对象
        Jwt mockJwt = Jwt.withTokenValue("dummy_jwt_token")
                .header("alg", "HS256")
                .claim("sub", "test_user_id")
                .claim("roles", "USER")
                .build();
        
        // 创建JWT认证对象
        JwtAuthenticationToken jwtAuth = new JwtAuthenticationToken(mockJwt);
        
        SecurityContextHolder.getContext().setAuthentication(jwtAuth);
        
        yourService.targetMethod();
    }
}

最后提醒一句:如果你的服务方法用了@PreAuthorize这类方法级权限注解,测试类要加上@EnableMethodSecurity(Spring Security 6+版本)或者@EnableGlobalMethodSecurity(prePostEnabled = true)(旧版本),不然注解不会生效,也可能触发类似的错误。

内容的提问来源于stack exchange,提问作者Denis Stephanov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:36:11