При использовании безопасности Spring, что надлежащий путь состоит в том, чтобы получить текущее имя пользователя (т.е. SecurityContext) информация в бобе?

279
задан dur 5 November 2015 в 02:53
поделиться

8 ответов

Единственная проблема состоит в том, что даже после аутентификации с безопасностью Spring, боб пользователя/принципала не существует в контейнере, таким образом введение зависимости, это будет трудно. Прежде чем мы использовали безопасность Spring, мы создадим ограниченный по объему сессией боб, который имел текущий Принципал, введите это в "AuthService" и затем введите тот Сервис на большинство других служб в Приложении. Таким образом, те Сервисы просто назвали бы authService.getCurrentUser () для получения объекта. Если у Вас есть место в Вашем коде, где Вы получаете ссылку на тот же Принципал на сессии, можно просто установить его как свойство на ограниченном по объему сессией бобе.

2
ответ дан cliff.meyers 23 November 2019 в 02:02
поделиться

Для последнего Spring приложение MVC я записал, я не вводил держателя SecurityContext, но у меня действительно был основной контроллер, что у меня было два служебных метода, связанные с этим... isAuthenticated () & getUsername (). Внутренне они делают вызов статического метода, который Вы описали.

, По крайней мере, тогда это находится только в однажды месте, если необходимо позже осуществить рефакторинг.

4
ответ дан RichH 23 November 2019 в 02:02
поделиться

Вы могли использовать подход AOP Spring. Например, если у Вас есть некоторый сервис, который должен знать текущий принципал. Вы могли представить пользовательскую аннотацию т.е. @Principal, которые указывают, что этот Сервис должен быть основным зависимым.

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

Тогда в Вашем совете, который я думаю потребности расширить MethodBeforeAdvice, проверка, что конкретный сервис имеет @Principal аннотацию и введи Основное имя, или установи его на 'АНОНИМНЫЙ' вместо этого.

3
ответ дан Pavel Rodionov 23 November 2019 в 02:02
поделиться

Я соглашаюсь, что необходимость запросить 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.
 * <p>
 * Equivalent of calling:
 * <p>
 * <code>getCurrentUser() != null</code>
 * 
 * @return if user is logged in
 */
public static boolean isLoggedIn() {
    return getCurrentUser() != null;
}
22
ответ дан matt b 23 November 2019 в 02:02
поделиться

Я бы просто сделал это:

request.getRemoteUser();
5
ответ дан 23 November 2019 в 02:02
поделиться

С тех пор, как был дан ответ на этот вопрос, в мире 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 выглядит так:

<bean id="myController" class="com.foo.FooController">
     ...
  <constructor-arg index="1">
    <bean class="com.foo.SecurityContextHolderFacade">
  </constructor-arg>
</bean>

Кажется более чем немного глупым, что Spring, зависимость контейнер для инъекций всего, не предоставил способ ввести что-то подобное. Насколько я понимаю, SecurityContextHolder унаследован от acegi, но все же. Дело в том, что они так близки - если бы только SecurityContextHolder имел геттер для получения базового экземпляра SecurityContextHolderStrategy (который является интерфейсом), вы могли бы внедрить его. Фактически, я даже открыл проблему Jira по этому поводу.

И последнее - я только что существенно изменил ответ, который у меня был здесь раньше. Если вам интересно, проверьте историю, но, как мне указал коллега, мой предыдущий ответ не будет работать в многопоточной среде. Базовая SecurityContextHolderStrategy , используемая SecurityContextHolder , по умолчанию является экземпляром ThreadLocalSecurityContextHolderStrategy , который сохраняет SecurityContext18 s . Следовательно, не обязательно вводить SecurityContext непосредственно в компонент во время инициализации - его может потребоваться каждый раз извлекать из ThreadLocal в многопоточном среды, поэтому будет получена правильная.

Базовая SecurityContextHolderStrategy , используемая SecurityContextHolder , по умолчанию является экземпляром ThreadLocalSecurityContextHolderStrategy , который сохраняет SecurityContext18 s . Следовательно, не обязательно вводить SecurityContext непосредственно в компонент во время инициализации - его может потребоваться каждый раз извлекать из ThreadLocal в многопоточном среды, поэтому будет получена правильная.

Базовая SecurityContextHolderStrategy , используемая SecurityContextHolder , по умолчанию является экземпляром ThreadLocalSecurityContextHolderStrategy , который сохраняет SecurityContext18 s . Следовательно, не обязательно вводить SecurityContext непосредственно в компонент во время инициализации - его может потребоваться каждый раз извлекать из ThreadLocal в многопоточном среды, поэтому будет получена правильная.

64
ответ дан 23 November 2019 в 02:02
поделиться

Да, статика в целом плохая, но в данном случае статика - это самый безопасный код, который можно написать. Поскольку контекст безопасности ассоциирует Принципала с текущим потоком, наиболее безопасный код будет обращаться к статическому из потока как можно более непосредственно. Скрытие доступа за классом обертки, который вводится, дает злоумышленнику больше точек для атаки. Им не нужен будет доступ к коду (который им будет сложно изменить, если банку подписать), им просто нужен способ переопределить конфигурацию, что можно сделать во время выполнения или просунуть немного XML в classpath. Даже использование вставки аннотаций в подписанный код было бы переопределено с помощью внешнего XML. Такой XML мог бы впрыскивать в работающую систему неавторизованный принцип. Вероятно, поэтому весна делает что-то настолько не-Spring, как в этом случае.

5
ответ дан 23 November 2019 в 02:02
поделиться

Я получаю аутентифицированного пользователя от 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";
    }
}
13
ответ дан 23 November 2019 в 02:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: