在使用Spring Security时,在一个bean中获取当前用户名(即SecurityContext)信息的正确方式是什么?

12 浏览
0 Comments

在使用Spring Security时,在一个bean中获取当前用户名(即SecurityContext)信息的正确方式是什么?

我有一个使用Spring Security的Spring MVC Web应用程序。我想知道当前登录用户的用户名。我正在使用下面给出的代码片段。这是被接受的方法吗?

我不喜欢在这个控制器中调用静态方法-这样做完全违背了Spring的目的,以我看来。是否有一种方法可以配置应用程序以注入当前SecurityContext或当前Authentication?

  @RequestMapping(method = RequestMethod.GET)
  public ModelAndView showResults(final HttpServletRequest request...) {
    final String currentUser = SecurityContextHolder.getContext().getAuthentication().getName();
    ...
  }

admin 更改状态以发布 2023年5月21日
0
0 Comments

自从回答此问题以来,Spring世界发生了很多变化。Spring已经简化了在控制器中获取当前用户的方式。对于其他bean,Spring采纳了作者的建议,简化了“SecurityContextHolder”的注入。更多详细信息请参阅评论。\n\n这是我最终使用的解决方案。我不想在我的控制器中使用SecurityContextHolder,而是想注入使用SecurityContextHolder的东西,但是将这个单例类从我的代码中抽象出来。我还没有找到别的方式来做到这一点,除了像这样制定自己的接口:\n\n

public interface SecurityContextFacade {
  SecurityContext getContext();
  void setContext(SecurityContext securityContext);
}

\n\n现在,我的控制器(或任何POJO)看起来是这样的:\n\n

public class FooController {
  private final SecurityContextFacade securityContextFacade;
  public FooController(SecurityContextFacade securityContextFacade) {
    this.securityContextFacade = securityContextFacade;
  }
  public void doSomething(){
    SecurityContext context = securityContextFacade.getContext();
    // do something w/ context
  }
}

\n\n由于接口是解耦的一个点,因此单元测试变得很简单。在此示例中,我使用Mockito:\n\n

public class FooControllerTest {
  private FooController controller;
  private SecurityContextFacade mockSecurityContextFacade;
  private SecurityContext mockSecurityContext;
  @Before
  public void setUp() throws Exception {
    mockSecurityContextFacade = mock(SecurityContextFacade.class);
    mockSecurityContext = mock(SecurityContext.class);
    stub(mockSecurityContextFacade.getContext()).toReturn(mockSecurityContext);
    controller = new FooController(mockSecurityContextFacade);
  }
  @Test
  public void testDoSomething() {
    controller.doSomething();
    verify(mockSecurityContextFacade).getContext();
  }
}

\n\n接口的默认实现如下所示:\n\n

public class SecurityContextHolderFacade implements SecurityContextFacade {
  public SecurityContext getContext() {
    return SecurityContextHolder.getContext();
  }
  public void setContext(SecurityContext securityContext) {
    SecurityContextHolder.setContext(securityContext);
  }
}

\n\n最后,生产Spring配置如下所示:\n\n


     ...
  
    
  

\n\nSpring作为一个依赖注入容器,居然没有提供一种类似的注入方式,这似乎有点傻。我知道SecurityContextHolder是从acegi继承而来的,但是还是这样。事实上,它们非常接近--如果只有SecurityContextHolder有一个getter方法来获取基础的SecurityContextHolderStrategy实例(它是一个接口),那么您就可以注入它了。实际上,我甚至打开了一个关于这个问题的Jira问题。还有一件事-我刚刚大幅改变了之前的答案。如果你好奇,请查看历史记录,但是,正如我的同事指出的那样,我的先前答案在多线程环境下无法工作。由SecurityContextHolder使用的底层SecurityContextHolderStrategy默认情况下是ThreadLocalSecurityContextHolderStrategy的一个实例,它将SecurityContext存储在ThreadLocal中。因此,在初始化时直接注入SecurityContext可能不是一个好主意-在多线程环境中,每次可能需要从ThreadLocal中检索,以检索正确的一个。

0
0 Comments

如果您正在使用 Spring 3,最简单的方法是:

 @RequestMapping(method = RequestMethod.GET)   
 public ModelAndView showResults(final HttpServletRequest request, Principal principal) {
     final String currentUser = principal.getName();
 }

0