当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《使用SpringSecurity时,获取bean中当前用户名(即SecurityContext)信息的正确方法是什么?》经验,为你挑选了9个好方法。

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

我不喜欢在这个控制器中调用静态方法 - 这违背了Spring的全部目的,恕我直言.有没有办法配置应用程序以注入当前的SecurityContext或当前的身份验证?

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

tsunade21.. 251

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

 @RequestMapping(method = RequestMethod.GET)   
 public ModelAndView showResults(final HttpServletRequest request, Principal principal) {

     final String currentUser = principal.getName();

 }


Scott Bale.. 64

自从回答这个问题后,Spring世界发生了很多变化.Spring简化了当前用户在控制器中的使用.对于其他bean,Spring采用了作者的建议并简化了"SecurityContextHolder"的注入.更多细节在评论中.


这是我最终选择的解决方案.而不是SecurityContextHolder在我的控制器中使用,我想注入一些SecurityContextHolder在引擎盖下使用的东西,但是从我的代码中抽象出类似单例的类.我发现除了滚动我自己的界面之外没办法做到这一点,如下所示:

public interface SecurityContextFacade {

  SecurityContext getContext();

  void setContext(SecurityContext securityContext);

}

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

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
  }

}

并且,由于接口是解耦点,因此单元测试非常简单.在这个例子中,我使用Mockito:

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();
  }

}

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

public class SecurityContextHolderFacade implements SecurityContextFacade {

  public SecurityContext getContext() {
    return SecurityContextHolder.getContext();
  }

  public void setContext(SecurityContext securityContext) {
    SecurityContextHolder.setContext(securityContext);
  }

}

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


     ...
  
    
  

Spring,一个所有东西的依赖注入容器,似乎没有提供注入类似东西的方法,这似乎有点愚蠢.我理解SecurityContextHolder是从acegi继承而来,但仍然.问题是,它们非常接近 - 如果只有SecurityContextHolder一个getter来获取底层SecurityContextHolderStrategy实例(这是一个接口),你可以注入它.事实上,我甚至打开了一个Jira问题.

最后一件事 - 我刚刚改变了我之前的答案.如果你很好奇,请查看历史记录但是,正如同事指出的那样,我之前的回答在多线程环境中不起作用.默认情况下,SecurityContextHolderStrategy使用的底层SecurityContextHolder是一个实例ThreadLocalSecurityContextHolderStrategy,它将SecurityContexts 存储在a中ThreadLocal.因此,SecurityContext在初始化时直接注入bean 并不一定是个好主意- 可能需要ThreadLocal在多线程环境中每次检索它,以便检索正确的bean .



1> tsunade21..:

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

 @RequestMapping(method = RequestMethod.GET)   
 public ModelAndView showResults(final HttpServletRequest request, Principal principal) {

     final String currentUser = principal.getName();

 }



2> Scott Bale..:

自从回答这个问题后,Spring世界发生了很多变化.Spring简化了当前用户在控制器中的使用.对于其他bean,Spring采用了作者的建议并简化了"SecurityContextHolder"的注入.更多细节在评论中.


这是我最终选择的解决方案.而不是SecurityContextHolder在我的控制器中使用,我想注入一些SecurityContextHolder在引擎盖下使用的东西,但是从我的代码中抽象出类似单例的类.我发现除了滚动我自己的界面之外没办法做到这一点,如下所示:

public interface SecurityContextFacade {

  SecurityContext getContext();

  void setContext(SecurityContext securityContext);

}

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

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
  }

}

并且,由于接口是解耦点,因此单元测试非常简单.在这个例子中,我使用Mockito:

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();
  }

}

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

public class SecurityContextHolderFacade implements SecurityContextFacade {

  public SecurityContext getContext() {
    return SecurityContextHolder.getContext();
  }

  public void setContext(SecurityContext securityContext) {
    SecurityContextHolder.setContext(securityContext);
  }

}

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


     ...
  
    
  

Spring,一个所有东西的依赖注入容器,似乎没有提供注入类似东西的方法,这似乎有点愚蠢.我理解SecurityContextHolder是从acegi继承而来,但仍然.问题是,它们非常接近 - 如果只有SecurityContextHolder一个getter来获取底层SecurityContextHolderStrategy实例(这是一个接口),你可以注入它.事实上,我甚至打开了一个Jira问题.

最后一件事 - 我刚刚改变了我之前的答案.如果你很好奇,请查看历史记录但是,正如同事指出的那样,我之前的回答在多线程环境中不起作用.默认情况下,SecurityContextHolderStrategy使用的底层SecurityContextHolder是一个实例ThreadLocalSecurityContextHolderStrategy,它将SecurityContexts 存储在a中ThreadLocal.因此,SecurityContext在初始化时直接注入bean 并不一定是个好主意- 可能需要ThreadLocal在多线程环境中每次检索它,以便检索正确的bean .


这仍然是当前Spring版本的推荐解决方案吗?我无法相信只需要检索用户名就需要这么多代码.
如果您使用的是Spring Security 3.0.x,他们在我记录的JIRA问题中实现了我的建议https://jira.springsource.org/browse/SEC-1188,这样您现在可以直接将SecurityContextHolderStrategy实例(来自SecurityContextHolder)注入你的bean通过标准的Spring配置.
请看tsunade21的回答.Spring 3现在允许您使用java.security.Principal作为控制器中的方法参数
之前的两条评论指的是一个旧的,不正确的答案,我刚刚更换了.

3> matt b..:

我同意不得不为当前用户查询SecurityContext,这似乎是一种处理这个问题的非Spring方式.

我写了一个静态的"帮助器"类来处理这个问题; 它很脏,因为它是一个全局和静态的方法,但我想这样,如果我们改变任何与安全相关的东西,至少我只需要在一个地方改变细节:

/**
* Returns the domain User object for the currently logged in user, or null
* if no User is logged in.
* 
* @return User object for the currently logged in user, or null if no User
*         is logged in.
*/
public static User getCurrentUser() {

    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal()

    if (principal instanceof MyUserDetails) return ((MyUserDetails) principal).getUser();

    // principal object is either null or represents anonymous user -
    // neither of which our domain User object can represent - so return null
    return null;
}


/**
 * Utility method to determine if the current user is logged in /
 * authenticated.
 * 

* Equivalent of calling: *

* getCurrentUser() != null * * @return if user is logged in */ public static boolean isLoggedIn() { return getCurrentUser() != null; }


只要SecurityContextHolder.getContext()是,而后者是线程安全的,因为它将安全细节保存在threadLocal中.此代码不维护任何状态.

4> Brad Parks..:

要使它只显示在JSP页面中,您可以使用Spring Security Tag Lib:

http://static.springsource.org/spring-security/site/docs/3.0.x/reference/taglibs.html

要使用任何标记,必须在JSP中声明安全性标记库:

<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>

然后在jsp页面中执行以下操作:


    logged in as  



    not logged in

注意:如@ SBerg413的评论中所述,您需要添加

使用表达式="真"

到security.xml配置中的"http"标记,以使其工作.


要使此方法起作用,您需要将use-expressions ="true"添加到security.xml配置中的http标记.

5> matsev..:

如果您使用的是Spring Security ver> = 3.2,则可以使用@AuthenticationPrincipal注释:

@RequestMapping(method = RequestMethod.GET)
public ModelAndView showResults(@AuthenticationPrincipal CustomUser currentUser, HttpServletRequest request) {
    String currentUsername = currentUser.getUsername();
    // ...
}

CustomUser是一个自定义对象,它实现UserDetails了自定义返回的对象UserDetailsService.

可以在Spring Security参考文档的@AuthenticationPrincipal章节中找到更多信息.



6> digz6666..:

我通过HttpServletRequest.getUserPrincipal()获得经过身份验证的用户;

例:

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.support.RequestContext;

import foo.Form;

@Controller
@RequestMapping(value="/welcome")
public class IndexController {

    @RequestMapping(method=RequestMethod.GET)
    public String getCreateForm(Model model, HttpServletRequest request) {

        if(request.getUserPrincipal() != null) {
            String loginName = request.getUserPrincipal().getName();
            System.out.println("loginName : " + loginName );
        }

        model.addAttribute("form", new Form());
        return "welcome";
    }
}



7> Farm..:

在Spring 3+中,您有以下选项.

选项1 :

@RequestMapping(method = RequestMethod.GET)    
public String currentUserNameByPrincipal(Principal principal) {
    return principal.getName();
}

选项2:

@RequestMapping(method = RequestMethod.GET)
public String currentUserNameByAuthentication(Authentication authentication) {
    return authentication.getName();
}

选项3:

@RequestMapping(method = RequestMethod.GET)    
public String currentUserByHTTPRequest(HttpServletRequest request) {
    return request.getUserPrincipal().getName();

}

选项4:花哨的一个:查看更多细节

public ModelAndView someRequestHandler(@ActiveUser User activeUser) {
  ...
}



8> Michael Bush..:

是的,静态通常很糟糕 - 通常,但在这种情况下,静态是您可以编写的最安全的代码.由于安全上下文将Principal与当前运行的线程相关联,因此最安全的代码将尽可能直接地从线程访问静态.隐藏注入的包装类后面的访问权限会为攻击者提供更多攻击点.他们不需要访问代码(如果jar被签名,他们将很难改变它们),他们只需要一种覆盖配置的方法,这可以在运行时完成或将一些XML滑入类路径.即使在签名代码中使用注释注入也可以使用外部XML进行覆盖.这样的XML可能会为正在运行的系统注入一个流氓主体.这可能就是为什么Spring在这种情况下做了类似Spring的事情.



9> 小智..:

我会这样做:

request.getRemoteUser();


这实际上是一种在Spring Security Web应用程序中获取远程用户名的有效且非常简单的方法.标准过滤器链包括一个`SecurityContextHolderAwareRequestFilter`,它包装请求并通过访问`SecurityContextHolder`来实现这个调用.
推荐阅读
和谐啄木鸟
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有