Как вручную установить аутентифицированного пользователя в Spring Security / SpringMVC - PullRequest
100 голосов
/ 12 января 2011

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

Обычная страница входа в систему, проходящая через перехватчик Spring, прекрасно работает.

В контроллере формы новой учетной записи я создаю UsernamePasswordAuthenticationToken и устанавливаю его в SecurityContext вручную:

SecurityContextHolder.getContext().setAuthentication(authentication);

На той же странице я позже проверяю, что пользователь вошел в систему с:

SecurityContextHolder.getContext().getAuthentication().getAuthorities();

Возвращает права доступа, которые я установил ранее при аутентификации. Все хорошо.

Но когда этот же код вызывается на следующей странице, которую я загружаю, токен аутентификации просто UserAnonymous.

Мне не понятно, почему он не сохранил аутентификацию, которую я установил в предыдущем запросе. Есть мысли?

  • Может ли это быть связано с неправильной настройкой идентификатора сеанса?
  • Есть ли что-то, что может как-то перезаписать мою аутентификацию?
  • Возможно, мне просто нужен еще один шаг для сохранения аутентификации?
  • Или мне нужно что-то сделать, чтобы объявить аутентификацию во всем сеансе, а не как-то один запрос?

Просто ищу некоторые мысли, которые могут помочь мне увидеть, что здесь происходит.

Ответы [ 6 ]

61 голосов
/ 12 января 2011

У меня была такая же проблема, как и у вас некоторое время назад. Я не могу вспомнить детали, но следующий код помог мне. Этот код используется в потоке Spring Webflow, следовательно, классы RequestContext и ExternalContext. Но наиболее важной для вас является метод doAutoLogin.

public String registerUser(UserRegistrationFormBean userRegistrationFormBean,
                           RequestContext requestContext,
                           ExternalContext externalContext) {

    try {
        Locale userLocale = requestContext.getExternalContext().getLocale();
        this.userService.createNewUser(userRegistrationFormBean, userLocale, Constants.SYSTEM_USER_ID);
        String emailAddress = userRegistrationFormBean.getChooseEmailAddressFormBean().getEmailAddress();
        String password = userRegistrationFormBean.getChoosePasswordFormBean().getPassword();
        doAutoLogin(emailAddress, password, (HttpServletRequest) externalContext.getNativeRequest());
        return "success";

    } catch (EmailAddressNotUniqueException e) {
        MessageResolver messageResolvable 
                = new MessageBuilder().error()
                                      .source(UserRegistrationFormBean.PROPERTYNAME_EMAIL_ADDRESS)
                                      .code("userRegistration.emailAddress.not.unique")
                                      .build();
        requestContext.getMessageContext().addMessage(messageResolvable);
        return "error";
    }

}


private void doAutoLogin(String username, String password, HttpServletRequest request) {

    try {
        // Must be called from request filtered by Spring Security, otherwise SecurityContextHolder is not updated
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
        token.setDetails(new WebAuthenticationDetails(request));
        Authentication authentication = this.authenticationProvider.authenticate(token);
        logger.debug("Logging in with [{}]", authentication.getPrincipal());
        SecurityContextHolder.getContext().setAuthentication(authentication);
    } catch (Exception e) {
        SecurityContextHolder.getContext().setAuthentication(null);
        logger.error("Failure in autoLogin", e);
    }

}
59 голосов
/ 01 декабря 2011

Я не смог найти других полных решений, поэтому подумал, что опубликую свои. Это может быть немного взломано, но это решило проблему с вышеупомянутой проблемой:

public void login(HttpServletRequest request, String userName, String password)
{

    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(userName, password);

    // Authenticate the user
    Authentication authentication = authenticationManager.authenticate(authRequest);
    SecurityContext securityContext = SecurityContextHolder.getContext();
    securityContext.setAuthentication(authentication);

    // Create a new session and add the security context.
    HttpSession session = request.getSession(true);
    session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
}
15 голосов
/ 13 января 2011

В конце концов выяснил корень проблемы.

Когда я создаю контекст безопасности вручную, объект сеанса не создается. Только когда запрос завершает обработку, механизм Spring Security понимает, что объект сеанса является нулевым (когда он пытается сохранить контекст безопасности в сеансе после обработки запроса).

В конце запроса Spring Security создает новый объект сеанса и идентификатор сеанса. Однако этот новый идентификатор сеанса никогда не попадает в браузер, потому что это происходит в конце запроса, после того как был сделан ответ браузеру. Это приводит к потере нового идентификатора сеанса (и, следовательно, контекста безопасности, содержащего моего пользователя, вошедшего в систему вручную), когда следующий запрос содержит идентификатор предыдущего сеанса.

5 голосов
/ 02 ноября 2011

Новая функция фильтрации в Servlet 2.4 в основном снимает ограничение, заключающееся в том, что фильтры могут работать только в потоке запросов до и после фактической обработки запроса сервером приложений.Вместо этого фильтры Servlet 2.4 теперь могут взаимодействовать с диспетчером запросов в каждой точке отправки.Это означает, что когда веб-ресурс направляет запрос другому ресурсу (например, сервлету, перенаправляющему запрос на страницу JSP в том же приложении), фильтр может работать до того, как запрос будет обработан целевым ресурсом.Это также означает, что если веб-ресурс включает выходные данные или функцию из других веб-ресурсов (например, страницы JSP, включающей в себя выходные данные из нескольких других страниц JSP), фильтры Servlet 2.4 могут работать до и после каждого из включенных ресурсов.,

Чтобы включить эту функцию, вам необходимо:

web.xml

<filter>   
    <filter-name>springSecurityFilterChain</filter-name>   
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 
</filter>  
<filter-mapping>   
    <filter-name>springSecurityFilterChain</filter-name>   
    <url-pattern>/<strike>*</strike></url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

RegistrationController

return "forward:/login?j_username=" + registrationModel.getUserEmail()
        + "&j_password=" + registrationModel.getPassword();
5 голосов
/ 12 января 2011

Включите ведение журнала отладки, чтобы получить более полное представление о происходящем.

Вы можете узнать, устанавливаются ли куки-файлы сеанса, используя отладчик на стороне браузера для просмотра заголовков, возвращаемых в ответах HTTP. (Есть и другие способы.)

Одна из возможностей заключается в том, что SpringSecurity устанавливает файлы cookie для безопасного сеанса, и на следующей запрашиваемой странице вместо URL-адреса https указывается URL-адрес http. (Браузер не отправляет безопасный cookie для URL-адреса «http».)

1 голос
/ 19 июня 2014

Я пытался протестировать приложение extjs, и после того, как он успешно установил параметр testAuthenticationToken, он внезапно перестал работать без видимой причины.

Я не мог заставить вышеуказанные ответы работать, поэтому мое решение состояло в том, чтобы пропустить этот кусочек весны в тестовой среде. Я ввел шов вокруг весны так:

public class SpringUserAccessor implements UserAccessor
{
    @Override
    public User getUser()
    {
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication = context.getAuthentication();
        return (User) authentication.getPrincipal();
    }
}

Пользователь - здесь пользовательский тип.

Затем я обертываю его в классе, в котором только есть опция для тестового кода для переключения пружины.

public class CurrentUserAccessor
{
    private static UserAccessor _accessor;

    public CurrentUserAccessor()
    {
        _accessor = new SpringUserAccessor();
    }

    public User getUser()
    {
        return _accessor.getUser();
    }

    public static void UseTestingAccessor(User user)
    {
        _accessor = new TestUserAccessor(user);
    }
}

Тестовая версия выглядит так:

public class TestUserAccessor implements UserAccessor
{
    private static User _user;

    public TestUserAccessor(User user)
    {
        _user = user;
    }

    @Override
    public User getUser()
    {
        return _user;
    }
}

В коде вызова я все еще использую правильного пользователя, загруженного из базы данных:

    User user = (User) _userService.loadUserByUsername(username);
    CurrentUserAccessor.UseTestingAccessor(user);

Очевидно, что это не подойдет, если вам действительно нужно использовать защиту, но я использую установку без защиты для тестового развертывания. Я думал, что кто-то еще может столкнуться с подобной ситуацией. Это шаблон, который я использовал для макетирования статических зависимостей ранее. Другой альтернативой является то, что вы можете поддерживать статичность класса-оболочки, но я предпочитаю этот, так как зависимости кода более явные, так как вы должны передавать CurrentUserAccessor в классы там, где это требуется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...