您的位置:首页 > 编程语言 > Java开发

spring-security 3.0.X, 让ajax login和普通login共存

2013-05-31 17:39 393 查看
使用spring security时遇到一个问题,有大量的ajax post是需要登录控制的,但是默认的spring-security机制导致post结果返回的是登录页。

现在要解决几个问题:

1,ajax post如果需要登录的话,返回需要登录的json消息,前端可以继续处理

2,新建一套ajax login的页面流转,但是不能和原有的login过程冲突,因为其他的非ajax请求还是需要用正常的login。

spring security配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">

<!-- Configure Spring Security -->
<!--
<security:http auto-config="true">
<security:form-login login-page="/login" login-processing-url="/loginProcess"
default-target-url="/" authentication-failure-url="/login?login_error=1" />
<security:logout logout-url="/logout" logout-success-url="/logoutSuccess" />
<security:remember-me key="bookingtest" />
</security:http>
-->
<security:http auto-config="false" entry-point-ref="jilujiaAuthenticationEntryPoint">
<!-- 登录过滤器 -->
<security:custom-filter before="FORM_LOGIN_FILTER" ref="loginFilter"/>
<!-- ajax登录过滤器 -->
<security:custom-filter position="FORM_LOGIN_FILTER" ref="ajaxLoginFilter"/>
<!-- 只cache get,避免ajax post 被cache -->
<security:request-cache ref="httpSessionRequestCache"/>
<!-- 注销过滤器 -->
<security:logout logout-url="/logout" logout-success-url="/logoutSuccess" />
<!-- remember me -->
<security:remember-me key="bookingtest" />
</security:http>

<bean id="jilujiaAuthenticationEntryPoint" class="com.jilujia.framework.security.JilujiaAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login" />
</bean>

<bean id="httpSessionRequestCache" class="org.springframework.security.web.savedrequest.HttpSessionRequestCache">
<property name="justUseSavedRequestOnGet" value="true" />
</bean>

<!-- 验证普通用户 -->
<bean id="loginFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureHandler" ref="failureHandler"/>
<property name="authenticationSuccessHandler" ref="successHandler"/>
<property name="filterProcessesUrl" value="/loginProcess"/>
</bean>

<bean id="failureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/login?login_error=1" />
</bean>

<bean id="successHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="alwaysUseDefaultTargetUrl" value="false"/>
<property name="defaultTargetUrl" value="/"/>
</bean>
<!-- 验证ajax请求-->
<bean id="ajaxLoginFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureHandler" ref="ajaxFailureHandler"/>
<property name="authenticationSuccessHandler" ref="ajaxSuccessHandler"/>
<property name="filterProcessesUrl" value="/ajaxLoginProcess"/>
</bean>

<bean id="ajaxFailureHandler" class="com.jilujia.framework.security.AjaxAuthenticationFailureHandler">
</bean>

<bean id="ajaxSuccessHandler" class="com.jilujia.framework.security.AjaxAuthenticationSuccessHandler">
</bean>

<security:global-method-security  jsr250-annotations="enabled" secured-annotations="enabled" />

<security:authentication-manager alias="authenticationManager">
<security:authentication-provider user-service-ref="customUserDetailsService">
<security:password-encoder ref="passwordEncoder" />
</security:authentication-provider>
</security:authentication-manager>

<bean id="customUserDetailsService" class="com.jilujia.framework.security.JilujiaUserDetailsService">
<property name="dataSource" ref="dataSource" />
</bean>

<bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"/>
</beans>

重点有几个:jilujiaAuthenticationEntryPoint,解决问题1, 这里区分ajax请求和非ajax请求的方式是uri中包含不包含ajax字符串,可以按需调整。

public class JilujiaAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {

private static final Log logger = LogFactory.getLog(JilujiaAuthenticationEntryPoint.class);

private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

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

HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;

String redirectUrl = null;

String url = request.getRequestURI();

if (logger.isDebugEnabled()) {
logger.debug("url:" + url);
}

// 非ajax请求
if (url.indexOf("ajax") == -1) {

if (this.isUseForward()) {

if (this.isForceHttps() && "http".equals(request.getScheme())) {
// First redirect the current request to HTTPS.
// When that request is received, the forward to the login page will be used.
redirectUrl = buildHttpsRedirectUrlForRequest(httpRequest);
}

if (redirectUrl == null) {
String loginForm = determineUrlToUseForThisRequest(httpRequest, httpResponse, authException);

if (logger.isDebugEnabled()) {
logger.debug("Server side forward to: " + loginForm);
}

RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(loginForm);

dispatcher.forward(request, response);

return;
}
} else {
// redirect to login page. Use https if forceHttps true

redirectUrl = buildRedirectUrlToLoginPage(httpRequest, httpResponse, authException);

}

redirectStrategy.sendRedirect(httpRequest, httpResponse, redirectUrl);
} else {
// ajax请求,返回json,替代redirect到login page
if (logger.isDebugEnabled()) {
logger.debug("ajax request or post");
}

ObjectMapper objectMapper = new ObjectMapper();
response.setHeader("Content-Type", "application/json;charset=UTF-8");
JsonGenerator jsonGenerator = objectMapper.getJsonFactory().createJsonGenerator(response.getOutputStream(),
JsonEncoding.UTF8);
try {
JsonData jsonData = new JsonData(2, null);
objectMapper.writeValue(jsonGenerator, jsonData);
} catch (JsonProcessingException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
}
}

}


第二个问题,注意配置一个新的过滤器专门处理ajax 请求,这个filter是通过filterProcessesUrl=ajaxLoginProcess来区分ajax login动作和普通login动作的。

<!-- ajax登录过滤器 -->
<security:custom-filter position="FORM_LOGIN_FILTER" ref="ajaxLoginFilter"/>

<bean id="ajaxLoginFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureHandler" ref="ajaxFailureHandler"/>
<property name="authenticationSuccessHandler" ref="ajaxSuccessHandler"/>
<property name="filterProcessesUrl" value="/ajaxLoginProcess"/>
</bean>

同时对应了两个handler,专门处理ajax登录的成功和失败,都返回json消息。
<bean id="ajaxFailureHandler" class="com.jilujia.framework.security.AjaxAuthenticationFailureHandler">
</bean>

<bean id="ajaxSuccessHandler" class="com.jilujia.framework.security.AjaxAuthenticationSuccessHandler">
</bean>

public class AjaxAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

public AjaxAuthenticationSuccessHandler() {
}

public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {

ObjectMapper objectMapper = new ObjectMapper();
response.setHeader("Content-Type", "application/json;charset=UTF-8");
JsonGenerator jsonGenerator = objectMapper.getJsonFactory().createJsonGenerator(response.getOutputStream(),
JsonEncoding.UTF8);
try {
//成功为0
JsonData jsonData = new JsonData(0, null);
objectMapper.writeValue(jsonGenerator, jsonData);
} catch (JsonProcessingException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
}
}

public class AjaxAuthenticationFailureHandler implements AuthenticationFailureHandler {
protected final Log logger = LogFactory.getLog(getClass());

public AjaxAuthenticationFailureHandler() {
}

public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
ObjectMapper objectMapper = new ObjectMapper();
response.setHeader("Content-Type", "application/json;charset=UTF-8");
JsonGenerator jsonGenerator = objectMapper.getJsonFactory().createJsonGenerator(response.getOutputStream(),
JsonEncoding.UTF8);
try {
//失败为1
JsonData jsonData = new JsonData(1, null);
objectMapper.writeValue(jsonGenerator, jsonData);
} catch (JsonProcessingException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
}

}


ajax login page差不多是这样:

<div id="inlineLogin" style="width:500px;display: none;">
<form id="LoginForm" action="<c:url value="/ajaxLoginProcess" />" method="post">
<fieldset>
<legend>Login Information</legend>
<p>
<label for="j_username">User:</label>
<br />
<input type="text" name="j_username" id="j_username" <c:if test="${not empty param.login_error}">value="<%= session.getAttribute(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY) %>"</c:if> />
</p>
<p>
<label for="j_password">Password:</label>
<br />
<input type="password" name="j_password" id="j_password" />
</p>
<p>
<input type="checkbox" name="_spring_security_remember_me" id="remember_me" />
<label for="remember_me">Don't ask for my password for two weeks:</label>
</p>
<p>
<a href="javascript:loginSubmit()" id='btn_login' class='rndbutton'><span>Login</span></a>
</p>
</fieldset>
</form>
</div>


function loginSubmit(){
var form = $('#LoginForm').serialize();
$.post('<c:url value="/ajaxLoginProcess" />',form,function(data){
if(data.error == 1)
alert(data.messages);
else if (data.error == 0){
alert("success")
}
});
}


特别注意的是配置了一个

<!-- 只cache get,避免ajax post 被cache -->
<security:request-cache ref="httpSessionRequestCache"/>
因为我的环境中所有的post都是ajax,这些都不需要cache。

参考:

http://www.360doc.com/content/12/0712/13/7656232_223767530.shtml

http://blog.csdn.net/zjh527/article/details/6158706
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息