source

Spring Security 및 jQuery를 사용하여 만료된 세션을 처리하는 방법은 무엇입니까?

factcode 2023. 7. 28. 22:56
반응형

Spring Security 및 jQuery를 사용하여 만료된 세션을 처리하는 방법은 무엇입니까?

저는 애플리케이션에서 스프링 보안과 jQuery를 사용하고 있습니다.메인 페이지는 AJAX를 통해 동적으로 탭에 컨텐츠를 로드하는 것을 사용합니다.모든 것이 정상이지만 가끔 탭 안에 로그인 페이지가 표시되고 자격 증명을 입력하면 탭이 없는 콘텐츠 페이지로 리디렉션됩니다.

그래서 저는 이 상황을 처리하고 싶습니다.일부 사람들이 AJAX 인증을 사용하는 것은 알고 있지만, 그것이 저에게 꽤 복잡해 보이고 제 애플리케이션이 이전에 로그인하지 않고는 어떤 접근도 허용하지 않기 때문에 저에게 적합한지 확신할 수 없습니다.저는 모든 AJAX 응답에 대해 글로벌 핸들러를 작성하고 싶습니다.window.location.reload()인증이 필요한 경우.이 경우에는 더 나은 방법을 찾는 것이 좋을 것 같습니다.401처리하기 쉽기 때문에 표준 로그인 양식 대신 오류가 발생했습니다.

그렇게,

모든 jQuery AJAX 요청에 대해 글로벌 오류 처리기를 작성할 수 있습니까?

AJAX 요청에 대해 401 오류를 보내지만 정기적인 요청에 대해 표준 로그인 페이지를 평소와 같이 표시하도록 Spring Security 동작을 사용자 지정하려면 어떻게 해야 합니까?

좀 더 우아한 해결책이 있을까요?공유 부탁드립니다.

감사해요.

여기 제가 생각하기에 아주 간단한 접근법이 있습니다.이것은 제가 이 사이트에서 관찰한 접근 방식의 조합입니다.저는 그것에 대해 블로그 포스트를 썼습니다: http://yoyar.com/blog/2012/06/dealing-with-the-spring-security-ajax-session-timeout-problem/

기본 아이디어는 위에서 제안한 대로 인증 진입점과 함께 api url 접두사(즉, /api/secured)를 사용하는 것입니다.간단하고 효과적입니다.

인증 진입점은 다음과 같습니다.

package com.yoyar.yaya.config;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;

import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;

public class AjaxAwareAuthenticationEntryPoint 
             extends LoginUrlAuthenticationEntryPoint {

    public AjaxAwareAuthenticationEntryPoint(String loginUrl) {
        super(loginUrl);
    }

    @Override
    public void commence(
        HttpServletRequest request, 
        HttpServletResponse response, 
        AuthenticationException authException) 
            throws IOException, ServletException {

        boolean isAjax 
            = request.getRequestURI().startsWith("/api/secured");

        if (isAjax) {
            response.sendError(403, "Forbidden");
        } else {
            super.commence(request, response, authException);
        }
    }
}

다음은 스프링 컨텍스트 xml에 포함된 내용입니다.

<bean id="authenticationEntryPoint"
  class="com.yoyar.yaya.config.AjaxAwareAuthenticationEntryPoint">
    <constructor-arg name="loginUrl" value="/login"/>
</bean>

<security:http auto-config="true"
  use-expressions="true"
  entry-point-ref="authenticationEntryPoint">
    <security:intercept-url pattern="/api/secured/**" access="hasRole('ROLE_USER')"/>
    <security:intercept-url pattern="/login" access="permitAll"/>
    <security:intercept-url pattern="/logout" access="permitAll"/>
    <security:intercept-url pattern="/denied" access="hasRole('ROLE_USER')"/>
    <security:intercept-url pattern="/" access="permitAll"/>
    <security:form-login login-page="/login"
                         authentication-failure-url="/loginfailed"
                         default-target-url="/login/success"/>
    <security:access-denied-handler error-page="/denied"/>
    <security:logout invalidate-session="true"
                     logout-success-url="/logout/success"
                     logout-url="/logout"/>
</security:http>

저는 다음과 같은 용액을 사용했습니다.

봄에 정의된 보안이 잘못된 세션 URL입니다.

<security:session-management invalid-session-url="/invalidate.do"/>

해당 페이지에 대해 다음 컨트롤러가 추가되었습니다.

@Controller
public class InvalidateSession
{
    /**
     * This url gets invoked when spring security invalidates session (ie timeout).
     * Specific content indicates ui layer that session has been invalidated and page should be redirected to logout. 
     */
    @RequestMapping(value = "invalidate.do", method = RequestMethod.GET)
    @ResponseBody
    public String invalidateSession() {
        return "invalidSession";
    }
}

또한 Ajax의 경우 agaxSetup을 사용하여 모든 Ajax 요청을 처리했습니다.

// Checks, if data indicates that session has been invalidated.
// If session is invalidated, page is redirected to logout
   $.ajaxSetup({
    complete: function(xhr, status) {
                if (xhr.responseText == 'invalidSession') {
                    if ($("#colorbox").count > 0) {
                        $("#colorbox").destroy();
                    }
                    window.location = "logout";
                }
            }
        });

http://forum.springsource.org/showthread.php?t=95881, 을 보십시오. 제안된 솔루션은 다른 답변보다 훨씬 명확하다고 생각합니다.

  1. jquery Ajax 호출에 사용자 지정 헤더를 추가합니다('보내기 전' 후크 사용).은 또한 있다니습수도를 할 수 .X-Requested-With보내는 jQuery에서 보내는 헤더입니다.
  2. 사용자를 로그인 페이지로 안내하는 대신 HTTP 401 오류 코드를 반환하도록 서버 측에서 해당 헤더를 찾도록 Spring Security를 구성합니다.

저는 이 문제에 대한 해결책을 생각해 냈을 뿐, 그것을 철저히 테스트하지는 않았습니다.저도 spring, spring security, jQuery를 사용하고 있습니다.먼저 로그인 컨트롤러에서 상태 코드를 401로 설정합니다.

LoginController {

public ModelAndView loginHandler(HttpServletRequest request, HttpServletResponse response) {

...
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
... 
return new ModelAndView("login", model);
}

onload() 메서드에서 내 모든 페이지는 내 글로벌 자바스크립트 파일의 함수를 호출합니다.

function initAjaxErrors() {

jQuery(window).ajaxError(function(event, xmlHttpRequest, ajaxOptions, thrownError) {
    if (403 == xmlHttpRequest.status)
        showMessage("Permission Denied");
    else
        showMessage("An error occurred: "+xmlHttpRequest.status+" "+xmlHttpRequest.statusText);
});

}

이 시점에서 401 오류를 원하는 대로 처리할 수 있습니다.한 프로젝트에서 로그인 양식이 포함된 iframe 주위에 jQuery 대화상자를 배치하여 jQuery 인증을 처리했습니다.

제가 일반적으로 하는 방법은 다음과 같습니다.모든 AJAX 호출에서 결과를 확인한 후 사용하십시오.

$.ajax({ type: 'GET',
    url: GetRootUrl() + '/services/dosomething.ashx',
    success: function (data) {
      if (HasErrors(data)) return;

      // process data returned...

    },
    error: function (xmlHttpRequest, textStatus) {
      ShowStatusFailed(xmlHttpRequest);
    }
  });

그리고 그다음에HasErrors()함수는 이렇게 생겼으며 모든 페이지에서 공유할 수 있습니다.

function HasErrors(data) {
  // check for redirect to login page
  if (data.search(/login\.aspx/i) != -1) {
    top.location.href = GetRootUrl() + '/login.aspx?lo=TimedOut';
    return true;
  }
  // check for IIS error page
  if (data.search(/Internal Server Error/) != -1) {
    ShowStatusFailed('Server Error.');
    return true;
  }
  // check for our custom error handling page
  if (data.search(/Error.aspx/) != -1) {
    ShowStatusFailed('An error occurred on the server. The Technical Support Team has been provided with the error details.');
    return true;
  }
  return false;
}

그래서 여기에 두 가지 문제가 있습니다. 1) Spring security가 작동하고 있지만 응답이 브라우저에 Ajax 호출로 다시 들어오고 있습니다. 2) Spring security는 사용자가 로그인한 후 해당 페이지로 리디렉션할 수 있도록 원래 요청된 페이지를 추적합니다(로그인 후 항상 특정 페이지를 사용하도록 지정하지 않는 한).이 경우 요청이 Ajax 문자열이었기 때문에 해당 문자열로 리디렉션되고 브라우저에 표시됩니다.

간단한 해결책은 Ajax 오류를 감지하는 것이며, 다시 전송된 요청이 로그인 페이지에 특정된 경우(Spring이 로그인 페이지 html을 다시 전송할 것입니다), '응답'입니다.요청의 Text' 속성)이 탐지합니다.그런 다음 현재 페이지를 다시 로드하면 사용자가 Ajax 호출 컨텍스트에서 제거됩니다.그러면 Spring이 자동으로 로그인 페이지로 전송합니다.(기본 j_username(로그인 페이지에 고유한 문자열 값)을 사용하고 있습니다.

$(document).ajaxError( function(event, request, settings, exception) {
    if(String.prototype.indexOf.call(request.responseText, "j_username") != -1) {
        window.location.reload(document.URL);
    }
});

시간 초과가 발생하면 세션이 이미 지워진 상태에서 Ajax 작업이 트리거된 후 사용자가 로그인 페이지로 리디렉션됩니다.

보안 컨텍스트:

<http use-expressions="true" entry-point-ref="authenticationEntryPoint">
    <logout invalidate-session="true" success-handler-ref="logoutSuccessBean" delete-cookies="JSESSIONID" />
    <custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
    <custom-filter position="FORM_LOGIN_FILTER" ref="authFilter" />
    <session-management invalid-session-url="/logout.xhtml" session-authentication-strategy-ref="sas"/>
</http>

<beans:bean id="concurrencyFilter"
  class="org.springframework.security.web.session.ConcurrentSessionFilter">
    <beans:property name="sessionRegistry" ref="sessionRegistry" />
    <beans:property name="expiredUrl" value="/logout.xhtml" />
</beans:bean>

<beans:bean id="authenticationEntryPoint"  class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
    <beans:property name="loginFormUrl" value="/login.xhtml" />
</beans:bean>

<beans:bean id="authFilter"
  class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
    <beans:property name="sessionAuthenticationStrategy" ref="sas" />
    <beans:property name="authenticationManager" ref="authenticationManager" />
    <beans:property name="authenticationSuccessHandler" ref="authenticationSuccessBean" />
    <beans:property name="authenticationFailureHandler" ref="authenticationFailureBean" />
</beans:bean>

<beans:bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
    <beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
    <beans:property name="maximumSessions" value="1" />
    <beans:property name="exceptionIfMaximumExceeded" value="1" />
</beans:bean>

로그인 수신기:

public class LoginListener implements PhaseListener {

@Override
public PhaseId getPhaseId() {
    return PhaseId.RESTORE_VIEW;
}

@Override
public void beforePhase(PhaseEvent event) {
    // do nothing
}

@Override
public void afterPhase(PhaseEvent event) {
    FacesContext context = event.getFacesContext();
    HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
    String logoutURL = request.getContextPath() + "/logout.xhtml";
    String loginURL = request.getContextPath() + "/login.xhtml";

    if (logoutURL.equals(request.getRequestURI())) {
        try {
            context.getExternalContext().redirect(loginURL);
        } catch (IOException e) {
            throw new FacesException(e);
        }
    }
}

}

언급URL : https://stackoverflow.com/questions/3339431/how-to-handle-expired-session-using-spring-security-and-jquery

반응형