アプリケーションでSpring SecurityとjQueryを使用しています。メインページでは、AJAXを介してタブにコンテンツを動的にロードします。そして、大丈夫です、しかし時々私は私のタブの中にログインページを持っています、そして、私が資格証明をタイプするならば、私はタブなしでコンテンツページにリダイレクトされます。
ですから、この状況に対処したいと思います。一部の人々がAJAX認証を使用していることを知っていますが、それは私にとって非常に複雑に見え、私のアプリケーションは以前にログインせずにアクセスを許可しないため、私に適しているかどうかはわかりません。認証が必要な場合に、すべてのAJAX応答window.location.reload()
を実行するためのグローバルハンドラーを作成するだけです。この場合、401
処理が簡単なため、標準のログインフォームではなくエラー。
そう、
1)すべてのjQueryに対してグローバルエラーハンドラを記述することは可能ですかAJAXリクエスト?
2)AJAXリクエストに対して401エラーを送信しますが、通常のリクエストが通常の標準ログインページを表示するための通常のリクエストに対して)Spring Securityの動作をどのようにカスタマイズできますか?
3)より優雅な解決策があるかもしれませんか?共有してください。
ありがとう。
これは私が非常に簡単なアプローチだと思います。これは、このサイトで私が観察したアプローチの組み合わせです。私はそれについてブログ投稿を書きました: 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は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";
}
}
});
http://forum.springsource.org/showthread.php?t=95881 を見てください。提案されたソリューションは、他の回答よりもはるかに明確です:
私はこの問題の解決策を思いついたばかりですが、十分にテストしていません。 Spring、Spring Security、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エラーは任意の方法で処理できます。 1つのプロジェクトで、ログインフォームを含む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;
}
_
したがって、ここには2つの問題があります。 1)Springセキュリティーは機能していますが、応答はajax呼び出しでブラウザーに返されます。 2)Springセキュリティは、最初にリクエストされたページを追跡し、ログイン後にリダイレクトできるようにします(ログイン後に常に特定のページを使用するように指定した場合を除く)。この場合、リクエストはAjax文字列だったので、その文字列にリダイレクトされ、ブラウザに表示されます。
簡単な解決策は、Ajaxエラーを検出することです。返されたリクエストがログインページに固有のものである場合(Springはログインページのhtmlを返送し、リクエストの「responseText」プロパティになります)、それを検出します。次に、現在のページをリロードするだけで、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);
}
}
}
}