【问题标题】:SecurityContextHolder.getContext().getAuthentication().getPrincipal() is null on ControllerSecurityContextHolder.getContext().getAuthentication().getPrincipal() 在控制器上为空
【发布时间】:2025-12-11 16:35:01
【问题描述】:

我有一个 Spring MVC 应用程序,我在其中公开一个端点,还有一个小型库,我在其中编写了一些常用功能。

我有一个这样的实用程序类:

class SecurityUtil {
    public static Principal getPrincipal(){
       return SecurityContextHolder.getContext().getAuthentication()
              .getPrincipal();
    } 
}

从控制器我正在做类似的事情:

 class MyController {

      public ResponseEntity<Void> myEndpoint(){
           // do something
           Principal principal = SecurityUtil.getPrincipal();
           // use the principal information for some audit processes
      }
 }

在这种情况下,Principal 为空,但如果像这样替换我的代码:

 class MyController {

      public ResponseEntity<Void> myEndpoint(){
           // do something
           Principal principal = SecurityContextHolder.getContext()
                                                      .getAuthentication()
                                                      .getPrincipal();
           // use the principal information for some audit processes
      }
 }

在这种情况下,Principal 不为空,它包含我需要的信息。
你知道会发生什么吗?

【问题讨论】:

  • 我刚刚测试过,没关系。我仍然得到一个空值
  • 您是否以 Spring bean 的身份访问 SecurityUtil

标签: spring-mvc spring-security


【解决方案1】:

我遇到了同样的问题,然后我通过以下方式解决了它。

创建UserService接口

public interface UserService {
    String getLoggedInUserName();
    User getLoggedInUser();
}

UserService 提供一个实现,但是,您也可以不创建接口,只需将 UserService 创建为一个类即可。

@Service
public class UserServiceImpl implements UserService { 

    private static Log log = LogFactory.getLog(UserServiceImpl.class);

    @Override
    public String getLoggedInUserName() {
        try {
            return getLoggedInUser().getUsername();
        } catch (Exception ex) {
            throw new UsernameNotFoundException("Please Log in", ex);
        }
    }

    @Override
    public User getLoggedInUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if (authentication.getPrincipal() instanceof User) {
            return (User) authentication.getPrincipal();
        } else {
            throw new UsernameNotFoundException("User is not authenticated; Found " + authentication.getPrincipal() + " of type " + authentication.getPrincipal().getClass() + "; Expected type User");
        }
    }

}

通过自动接线调用userService.getLoggedInUserName()UserService

@Autowired UserService userService

更新: 如果您只在控制器中获取它们,那么您可以简单地将 Principal 主体作为方法参数传递给您的控制器方法,而不是从安全上下文中获取它。它将自动连接到控制器,稍后您可以将其传递给您的服务方法。这种方式也被认为是一种很好的做法Spring MVC, getting principal from security context in service layer

@RequestMapping(value = "/myEndpoint", method = GET)
public ResponseEntity<Void> myEndpoint(Principal principal){
   // do something
   // use the principal information for some audit processes
}

【讨论】:

  • 嗯,这对我也不起作用。 UserService 和 UserServiceImpl 在 jar 中吗?或者直接在MVC项目上
  • 它直接在我的项目中,但我认为将它们放在jar中也不会引起任何问题。我已经测试过。
  • 如果您只在控制器中获取它们,那么您可以简单地将 Principal principal 作为方法参数传递给您的控制器方法,而不是从安全上下文中获取它。
  • 谢谢 Naresh Joshi,我的应用程序上下文中出现错误,现在可以正常工作了