文档章节

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

烀饼
 烀饼
发布于 2012/07/13 10:44
字数 1134
阅读 11393
收藏 17

使用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
 

© 著作权归作者所有

烀饼
粉丝 5
博文 4
码字总数 2598
作品 0
宝山
部门经理
私信 提问
加载中

评论(1)

CrazyCoding
CrazyCoding
您好,我现在需要普通请求和所有Ajax请求(不仅只是login)共存,因为我的前段基本是ExtJS,普通请求跳转的是页面,Ajax我只要返回JSON数据 然后再决定我是否去跳转登录页面?
使用vue集成spring security进行安全登陆

在前后端分离的状态下,传统的spring security认证模式也需要做一点改造,以适应ajax的前端访问模式 现在前后端分离的开发模式已经成为主流,好处不多说了,说说碰到的问题和坑。首先要解决的...

暴走的初号机
05/17
0
0
spring security ajax登录及返回

序 本文讲述一下如何自定义spring security的登录页,网上给的资料大多过时,而且是基于后端模板技术的,讲的不是太清晰,本文给出一个采用ajax的登录及返回的前后端分离方式。 ajax返回 总共...

xixicat
2017/11/24
0
0
Spring Security框架怎么设置响应为json格式

前端使用mui框架发送ajax,设置请求类型为json格式,后端的service使用@PreAuthorize("isAuthenticated()")注解进行是否登录的校验,当用户匿名时(未登录),Spring security框架会返回登录...

譬如北辰
2018/02/18
1K
2
采用annotation对spring-mvc进行登录权限控制

在web系统中,判断用户是否登录是一个常用功能. 本文提出一种采用annotation对spring-mvc进行用户登录判断的方法. [程序源代码][1] 方法简介 建立一个annotation, 在需要登录判断的spring-mv...

taojinhuo
2013/01/05
11.3K
17
Secures your applications with Spring Security 5 and Keycloak

Spring Security 5 brought new OAuth2/OIDC client instead of the legacy client support in the old Spring Security OAuth sub project. The new OAuth2 umbrella modules in the core p......

hantsy
2018/04/24
174
0

没有更多内容

加载失败,请刷新页面

加载更多

怎样在磁盘上查找MySQL表的大小?这里有答案

导读 我想知道 MySQL 表在磁盘上占用多少空间,但看起来很琐碎。不应该在 INFORMATION_SCHEMA.TABLES 中提供这些信息吗?没那么简单! 我想知道 MySQL 表在磁盘上占用多少空间,但看起来很琐碎...

问题终结者
18分钟前
5
0
jQuery load() 方法实现加载远程数据

jQuery load() 方法是简单但强大的 AJAX 方法。load() 方法从服务器加载数据,并把返回的数据放入被选元素中。 语法: $(selector).load(URL,data,callback);必需的 URL 参数规定您希望加载的...

前端老手
19分钟前
3
0
Spring Boot缓存实战 Redis 设置有效时间和自动刷新缓存-2

问题 上一篇Spring Boot Cache + redis 设置有效时间和自动刷新缓存,时间支持在配置文件中配置,说了一种时间方式,直接扩展注解的Value值,如: @Override@Cacheable(value = "people#${s...

xiaolyuh
27分钟前
9
0
怎样在磁盘上查找MySQL表的大小?这里有答案

我想知道 MySQL 表在磁盘上占用多少空间,但看起来很琐碎。不应该在 INFORMATION_SCHEMA.TABLES 中提供这些信息吗?没那么简单! 我想知道 MySQL 表在磁盘上占用多少空间,但看起来很琐碎。不应...

Linux就该这么学
52分钟前
5
0
Redis

一、Redis支持的几种数据类型:字符串、List、SET、HASH、ZSET 二、Redis的缓存技术主要是为了降低关系数据库的负载并减少网站成本 三、在Redis里面,被MULTI命令和EXEC命令包围的所有命令会...

BobwithB
54分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部