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

我不喜欢在这个控制器中调用静态方法——恕我直言,这违背了Spring的全部目的。有没有一种方法来配置应用程序有当前的SecurityContext,或当前的认证,注入代替?

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

当前回答

我使用@Controller类中的@AuthenticationPrincipal注释以及@ controlleradvisor注释。例:

@ControllerAdvice
public class ControllerAdvicer
{
    private static final Logger LOGGER = LoggerFactory.getLogger(ControllerAdvicer.class);


    @ModelAttribute("userActive")
    public UserActive currentUser(@AuthenticationPrincipal UserActive currentUser)
    {
        return currentUser;
    }
}

其中UserActive是我用于已登录用户服务的类,并且扩展自org.springframework.security.core.userdetails.User。喜欢的东西:

public class UserActive extends org.springframework.security.core.userdetails.User
{

    private final User user;

    public UserActive(User user)
    {
        super(user.getUsername(), user.getPasswordHash(), user.getGrantedAuthorities());
        this.user = user;
    }

     //More functions
}

真的很容易。

其他回答

将Principal定义为控制器方法中的依赖项,spring将在调用时将当前已验证的用户注入到方法中。

我喜欢在freemarker页面上分享我支持用户详细信息的方法。 一切都很简单,工作完美!

你只需要在default-target-url上放置身份验证重新请求(表单登录后的页面) 这是我的那个页面的controller方法:

@RequestMapping(value = "/monitoring", method = RequestMethod.GET)
public ModelAndView getMonitoringPage(Model model, final HttpServletRequest request) {
    showRequestLog("monitoring");


    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    String userName = authentication.getName();
    //create a new session
    HttpSession session = request.getSession(true);
    session.setAttribute("username", userName);

    return new ModelAndView(catalogPath + "monitoring");
}

这是我的超光速代码:

<@security.authorize ifAnyGranted="ROLE_ADMIN, ROLE_USER">
<p style="padding-right: 20px;">Logged in as ${username!"Anonymous" }</p>
</@security.authorize> 

就是这样,用户名将出现在授权后的每一页。

对于我写的上一个Spring MVC应用程序,我没有注入SecurityContext holder,但我确实有一个基本控制器,我有两个实用程序方法与此相关…isAuthenticated() & getUsername()。在内部,它们执行您所描述的静态方法调用。

至少在以后需要重构时,它只在一个地方。

您可以使用Spring AOP方法。 例如,如果你有一些服务,它需要知道当前的本金。您可以引入自定义注释,例如@Principal,它表明该服务应该是依赖于主体的。

public class SomeService {
    private String principal;
    @Principal
    public setPrincipal(String principal){
        this.principal=principal;
    }
}

然后在您的通知中,我认为需要扩展MethodBeforeAdvice,检查特定的服务有@Principal注释并注入Principal名称,或者将其设置为'ANONYMOUS'。

The only problem is that even after authenticating with Spring Security, the user/principal bean doesn't exist in the container, so dependency-injecting it will be difficult. Before we used Spring Security we would create a session-scoped bean that had the current Principal, inject that into an "AuthService" and then inject that Service into most of the other services in the Application. So those Services would simply call authService.getCurrentUser() to get the object. If you have a place in your code where you get a reference to the same Principal in the session, you can simply set it as a property on your session-scoped bean.