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

Spring授权服务器中LoginUser作为AuthenticationPrincipal为空的配置问题

问题解决方案:@AuthenticationPrincipal 获取LoginUser为null的处理

针对你遇到的OAuth2授权码模式下@AuthenticationPrincipal注解无法获取到LoginUser实例的问题,需要在**Spring Security配置类(SessionAuthSecurityConfig)和授权服务器配置类(AuthorizationServerConfig)**中同时调整配置,以下是具体步骤:

1. 确保CustomDaoAuthenticationProvider正确返回LoginUser

首先要保证自定义认证提供者在认证成功后,返回的Authentication对象中Principal是LoginUser实例:

  • 检查CustomDaoAuthenticationProvider的retrieveUser方法,确保调用CustomUserDetailsService.loadUserByUsername后直接返回LoginUser,不要进行类型转换或包装。
  • 示例代码(如果重写retrieveUser):
public class CustomDaoAuthenticationProvider extends DaoAuthenticationProvider {
    @Override
    protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        // 直接调用自定义UserDetailsService获取LoginUser
        UserDetails loadedUser = getUserDetailsService().loadUserByUsername(username);
        if (loadedUser == null) {
            throw new InternalAuthenticationServiceException("UserDetailsService returned null");
        }
        return loadedUser; // 这里返回的是LoginUser实例
    }

    // 其他自定义校验逻辑...
}

2. 调整Spring Security配置类(SessionAuthSecurityConfig)

核心配置点:

  • 注册自定义认证提供者,替换默认的DaoAuthenticationProvider
  • 开启方法级安全支持,确保@AuthenticationPrincipal注解生效
  • 示例配置:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity // 开启方法级安全,支持@AuthenticationPrincipal
public class SessionAuthSecurityConfig extends WebSecurityConfigurerAdapter {
    private final CustomUserDetailsService customUserDetailsService;
    private final PasswordEncoder passwordEncoder;

    public SessionAuthSecurityConfig(CustomUserDetailsService customUserDetailsService, PasswordEncoder passwordEncoder) {
        this.customUserDetailsService = customUserDetailsService;
        this.passwordEncoder = passwordEncoder;
    }

    // 注册自定义认证提供者
    @Bean
    public CustomDaoAuthenticationProvider customDaoAuthenticationProvider() {
        CustomDaoAuthenticationProvider provider = new CustomDaoAuthenticationProvider();
        provider.setUserDetailsService(customUserDetailsService);
        provider.setPasswordEncoder(passwordEncoder);
        return provider;
    }

    // 配置认证管理器使用自定义提供者
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(customDaoAuthenticationProvider());
    }

    // 其他配置:比如登录路径、授权规则等...
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .permitAll();
    }
}

3. 调整授权服务器配置类(AuthorizationServerConfig)

核心配置点:

  • 把自定义UserDetailsService注入到授权服务器端点,确保授权流程能正确加载用户信息
  • 可选:配置Token增强器,将LoginUser的自定义字段写入令牌(如果需要在资源服务器获取)
  • 示例配置:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    private final AuthenticationManager authenticationManager;
    private final CustomUserDetailsService customUserDetailsService;
    private final PasswordEncoder passwordEncoder;

    public AuthorizationServerConfig(AuthenticationManager authenticationManager, CustomUserDetailsService customUserDetailsService, PasswordEncoder passwordEncoder) {
        this.authenticationManager = authenticationManager;
        this.customUserDetailsService = customUserDetailsService;
        this.passwordEncoder = passwordEncoder;
    }

    // 客户端配置(根据你的业务需求调整)
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("your-client-id")
            .secret(passwordEncoder.encode("your-client-secret"))
            .authorizedGrantTypes("authorization_code")
            .scopes("read", "write")
            .redirectUris("http://localhost:8080/callback");
    }

    // 配置授权端点,关联自定义UserDetailsService
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
            .authenticationManager(authenticationManager)
            .userDetailsService(customUserDetailsService) // 关键:让授权服务器使用自定义用户服务
            .tokenEnhancer(tokenEnhancer()); // 可选:增强令牌携带用户自定义信息
    }

    // 令牌增强器示例:将LoginUser的字段写入令牌额外信息
    private TokenEnhancer tokenEnhancer() {
        return (accessToken, authentication) -> {
            if (authentication.getPrincipal() instanceof LoginUser loginUser) {
                Map<String, Object> additionalInfo = new HashMap<>();
                additionalInfo.put("userId", loginUser.getId());
                additionalInfo.put("nickname", loginUser.getNickname());
                ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
            }
            return accessToken;
        };
    }

    // 授权服务器安全配置
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
            .tokenKeyAccess("permitAll()")
            .checkTokenAccess("isAuthenticated()")
            .allowFormAuthenticationForClients();
    }
}

4. 正确使用@AuthenticationPrincipal注解

在接口方法中直接注入LoginUser,确保注解使用正确:

@GetMapping("/api/user/info")
public ResponseEntity<Map<String, Object>> getUserInfo(@AuthenticationPrincipal LoginUser loginUser) {
    // 此时loginUser应为非null
    Map<String, Object> userInfo = new HashMap<>();
    userInfo.put("username", loginUser.getUsername());
    userInfo.put("userId", loginUser.getId());
    userInfo.put("enabled", loginUser.isEnabled());
    return ResponseEntity.ok(userInfo);
}

额外排查点

  • 调试时打印SecurityContextHolder.getContext().getAuthentication().getPrincipal(),确认实际类型是否为LoginUser
  • 检查CustomDaoAuthenticationProvider的additionalAuthenticationChecks方法是否抛出异常,导致认证失败
  • 如果使用分布式会话,确保LoginUser实现了序列化接口,避免用户信息丢失

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 17:47:41