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

@AuthenticationPrincipal等注解在方法中返回null的问题排查

问题描述

我正尝试通过Spring Security架构文档及相关教程实现端点用户安全,已确认JWT密钥配置正确(认证功能可正常工作),但在ConnectionController的方法中使用@AuthenticationPrincipal、Authentication、Principal时均返回null;而通过SecurityContextHolder.getContext()能获取到正确的Authentication及Jwt对象,想了解遗漏了哪些配置。

SecurityConfig.class

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig {

    private static final String[] AUTH_WHITELIST = {
            // -- Swagger UI v2
            "/v2/api-docs",
            "/swagger-resources",
            "/swagger-resources/**",
            "/configuration/ui",
            "/configuration/security",
            "/swagger-ui.html",
            "/webjars/**",
            // -- Swagger UI v3 (OpenAPI)
            "/v3/api-docs/**",
            "/swagger-ui/**"
            // other public endpoints of your API may be appended to this array
    };
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                // Our public endpoints
                .antMatchers("/**/openapi.json").permitAll()
                .antMatchers("/api/ims/auth/**").permitAll()
                .antMatchers(AUTH_WHITELIST).permitAll()
                .anyRequest().authenticated()
                .and()
                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
        return http.build();
    }

    // Used by JwtAuthenticationProvider to decode and validate JWT tokens
    @Bean
    public JwtDecoder jwtDecoder() {
        SecretKey secretKey = new SecretKeySpec("INSERTSECRETHERE".getBytes(), "HMACSHA256");
        return NimbusJwtDecoder.withSecretKey(secretKey).macAlgorithm(MacAlgorithm.HS256).build();
    }
}

ConnectionController.class

@Path("connection/v1")
@SecurityScheme(name = SWAGGER_AUTH_NAME, type = HTTP, scheme = SWAGGER_AUTH_SCHEME, in = HEADER)
@Tag(name = "Connection Controller", description = "Manage connection resources")
@RestController
public class ConnectionController {
    private final ConnectionService connectionService;
    @Inject
    public ConnectionController(final ConnectionService connectionService)
    {
        this.connectionService = connectionService;
    }

    @GET
    @Path("/user/{userId}")
    @Produces(MediaType.APPLICATION_JSON)
    @Operation(description = "Retrieve connections by userId")
    //@PreAuthorize("authentication.principal.claims.userId.equals(#userId)")
    public List<ConnectionDto> getConnectionsByUserId(@PathParam("userId") final String userId, @Parameter(hidden = true) @AuthenticationPrincipal Jwt authentication)
    {
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication1 = context.getAuthentication();
        Object jwt = context.getAuthentication().getPrincipal();
        Assert.hasText(userId, "User id must be provided");
        return this.connectionService.getConnectionsByUserId(userId);
    }
}
问题原因与解决方案

核心原因

你混用了JAX-RS注解(@Path、@GET、@PathParam等)和Spring MVC注解(@RestController、@AuthenticationPrincipal)。Spring MVC的参数解析器默认只会处理Spring MVC注解标记的方法,不会介入JAX-RS注解的方法处理流程,所以@AuthenticationPrincipal无法被解析注入;而SecurityContextHolder是全局线程绑定的,不受请求处理方式影响,因此能直接获取到认证信息。

解决方案

方案一:替换JAX-RS注解为Spring MVC注解(推荐)

把Controller中的JAX-RS注解换成Spring MVC对应的注解,让Spring MVC接管请求处理,这样@AuthenticationPrincipal就能正常生效:

修改后的ConnectionController关键代码:

// 替换类上的@Path为Spring MVC的@RequestMapping
@RequestMapping("/connection/v1")
@SecurityScheme(name = SWAGGER_AUTH_NAME, type = HTTP, scheme = SWAGGER_AUTH_SCHEME, in = HEADER)
@Tag(name = "Connection Controller", description = "Manage connection resources")
@RestController
public class ConnectionController {
    // ... 构造方法不变

    // 替换@GET+@Path为@GetMapping,@PathParam为@PathVariable
    @GetMapping("/user/{userId}")
    @Produces(MediaType.APPLICATION_JSON)
    @Operation(description = "Retrieve connections by userId")
    public List<ConnectionDto> getConnectionsByUserId(@PathVariable("userId") final String userId, @AuthenticationPrincipal Jwt authentication) {
        // ... 原有逻辑
    }
}

方案二:保留JAX-RS注解并配置Spring支持(繁琐,不推荐)

如果必须保留JAX-RS注解,需要引入spring-boot-starter-jersey依赖,配置Jersey资源注册,同时自定义Jersey的参数注入器来支持@AuthenticationPrincipal:

  1. 引入Jersey依赖到项目构建文件(pom.xml/build.gradle)
  2. 创建Jersey配置类注册你的ConnectionController
  3. 实现Jersey的ValueFactoryProvider,从SecurityContextHolder中获取Jwt对象并注入

这种方式配置复杂,且Spring Security与Jersey的整合容易出现兼容性问题,优先推荐方案一。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 20:23:14