26

アプリケーションで Spring Security と jQuery を使用しています。メイン ページは、AJAX を介してコンテンツを動的にタブにロードします。すべて問題ありませんが、タブ内にログイン ページが表示されることがあります。資格情報を入力すると、タブのないコンテンツ ページにリダイレクトされます。

だから、この状況を何とかしたい。一部の人々が AJAX 認証を使用していることは知っていますが、それが私に適しているかどうかはわかりません。なぜなら、それは私にとって非常に複雑に見え、私のアプリケーションは事前にログインしないとアクセスを許可しないからです。window.location.reload()認証が必要な場合に実行するすべての AJAX 応答のグローバル ハンドラーを書きたいと思います。401この場合、標準のログインフォームではなく、エラーを取得した方が扱いやすいのではないかと思います。

そう、

1)すべての jQuery AJAX リクエストに対してグローバル エラー ハンドラを作成することは可能ですか?

2) Spring Security の動作をカスタマイズして、AJAX リクエストに対して 401 エラーを送信し、通常のリクエストに対しては通常どおり標準のログイン ページを表示するにはどうすればよいですか?

3)もっと優雅な解決策があるかもしれませんか?共有してください。

ありがとう。

4

7 に答える 7

10

これは私が非常に簡単だと思うアプローチです。これは、私がこのサイトで観察したアプローチの組み合わせです。私はそれについてブログ記事を書きました: 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>
于 2012-06-19T03:23:27.487 に答える
9

次のソリューションを使用しました。

春のセキュリティで定義された無効なセッション 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 の場合は、ajaxSetup を使用してすべての 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";
                }
            }
        });
于 2012-09-03T12:15:31.667 に答える
4

http://forum.springsource.org/showthread.php?t=95881を見てください。提案された解決策は、ここにある他の回答よりもはるかに明確だと思います。

  1. jquery ajax 呼び出しにカスタム ヘッダーを追加します (「beforeSend」フックを使用)。jQuery が送信する「X-Requested-With」ヘッダーを使用することもできます。
  2. ユーザーをログインページに移動させる代わりに、サーバー側でそのヘッダーを検索して HTTP 401 エラーコードを返すように Spring Security を構成します。
于 2011-01-15T12:55:46.163 に答える
3

この問題の解決策を思いついたばかりですが、完全にテストしていません。スプリング、スプリング セキュリティ、jQuery も使用しています。まず、ログインのコントローラーから、ステータス コードを 401 に設定します。

LoginController {

public ModelAndView loginHandler(HttpServletRequest request, HttpServletResponse response) {

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

それらの onload() メソッドで、すべてのページがグローバル JavaScript ファイル内の関数を呼び出します。

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 認証を処理しました。

于 2010-11-30T23:01:07.557 に答える
2

これが私が通常行う方法です。すべての 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;
}
于 2010-10-06T17:24:27.090 に答える
0

タイムアウトが発生すると、セッションがすでにクリアされている間に 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);
        }
    }
}

}

于 2014-09-22T08:36:03.303 に答える