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

转自: http://my.oschina.net/jilujia/blog/66795


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

现在要解决几个问题:

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

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

 spring security配置如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<? 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字符串,可以按需调整。

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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>

            

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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差不多是这样:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
< 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 >

 

?
1
2
3
4
5
6
7
8
9
10
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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值