หน้าเว็บ

วันศุกร์ที่ 2 พฤษภาคม พ.ศ. 2557

Spring security detect Primefaces ajax session expire


        จากบทความเดิมก่อนหน้านี้  (เมื่อนานมาแล้ว) "การทำ Login ด้วย Spring Security"  ไม่สามารถตรวจจับ session expire ของ jsf request ที่เป็น ajax ได้

        คือ เมื่อเกิด session expire แล้วเราทำการส่ง request ที่เป็น ajax กลับไปยัง web server  spring security จะปฏิเสธ request นั้นทันที  ทำให้ request ไม่เกิดการประมวลผลตาม logic ที่เราเขียนไว้ (request จะนิ่ง  ไม่เกิดปฏิกิริยาตอบสนองใดๆ ทั้งสิ้น) ทั้งๆ ที่ตามความเป็นจริงแล้ว  มันควรจะเด้งไปที่หน้า login แต่มันก็ไม่ยอมทำ

        บทความนี้จะเป็นการแก้ปัญหา  ว่าเราจะทำยังไง  ให้มันเด้งไปยังหน้า login เมื่อ session expire แล้วได้รับ request ที่เป็น ajax ครับ

แนวคิด
request ที่เป็น ajax ของ jsf นั้น  response ที่ถูกส่งกลับมาจะเป็น text/xml   รูปแบบประมาณนี้
<?xml version="1.0" encoding="UTF-8"?>
<partial-response>
    ....
    ....
    ....
</partial-response>
        ซึ่งข้างใน patial response ก็จะเป็น response ของ logic ที่เราเขียนเอาไว้ ใน managed bean (backing bean หรือ controller)

        การทำให้ ajax เกิดการ redirect page ไปยังหน้า login นั้น  เราจะต้องทำให้ response ถูกส่งกลับมา แล้วมีรูปแบบดังนี้
<?xml version="1.0" encoding="UTF-8"?>
<partial-response>
    <redirect url="http://your.url.here/"></redirect>
</partial-response>
คำถาม แล้วเราจะทำยังไงล่ะ ให้มันได้ response แบบนี้?

วิธีการ
1. ที่ applicationContext-security.xml  (อ้างอิงจากบทความ การทำ Login ด้วย Spring Security")  ให้เพิ่ม xml config ดังต่อไปนี้ลงไป (เราจะ manage session เอง ด้วย session management filter)
    ....
    ....
    ....

    <beans:bean id="sessionManagementFilter" class="org.springframework.security.web.session.SessionManagementFilter">
        <beans:constructor-arg name="securityContextRepository" ref="httpSessionSecurityContextRepository" />
        <beans:property name="invalidSessionStrategy" ref="redirectStrategy"/>
    </beans:bean>
    
    <beans:bean id="redirectStrategy" class="com.blogspot.na5cent.web.security.JsfRedirectStrategy">
        <beans:constructor-arg name="invalidSessionUrl" value="/authen/" />
    </beans:bean>
    
    <beans:bean id="httpSessionSecurityContextRepository" class="org.springframework.security.web.context.HttpSessionSecurityContextRepository"/>
    
    ....
    ....
    ....
invalidSessionUrl คือ url ที่เราต้องการให้มัน redirect ไป เมื่อ session expire

2. ที่ tag <http/>  ให้เพิ่ม custom filter <custom-filter ref="sessionManagementFilter" before="SESSION_MANAGEMENT_FILTER"/>  โดยให้ reference ไปยัง config ข้อ 1 ดังนี้
<http  use-expressions="true" auto-config="true">
    ....
    ....
    ....  
    
    <custom-filter ref="sessionManagementFilter" before="SESSION_MANAGEMENT_FILTER"/>        
</http>
3. จากข้อ 1 มีการประกาศ bean redirectStrategy --> com.blogspot.na5cent.web.security.JsfRedirectStrategy  ตัวนี้แหล่ะครับ  ที่เราจะต้องไปเขียน code เพื่อให้มัน generate xml redirect ให้เรา  ดังนี้
package com.blogspot.na5cent.web.security;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.util.StringUtils;

/**
 * Inspired by StackOverflow.com
 * and by Spring Security 3 and ICEfaces 3 Tutorial.
 *
 * @author banterCZ
 */
public class JsfRedirectStrategy implements InvalidSessionStrategy {

    private static final Logger LOG = LoggerFactory.getLogger(JsfRedirectStrategy.class);

    private static final String FACES_REQUEST_HEADER = "faces-request";

    private String invalidSessionUrl;
    
    public JsfRedirectStrategy(String invalidSessionUrl){
        this.invalidSessionUrl = invalidSessionUrl;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {

        boolean ajaxRedirect = "partial/ajax".equals(request.getHeader(FACES_REQUEST_HEADER));
        if (ajaxRedirect) {
            String contextPath = request.getContextPath();
            String redirectUrl = contextPath + invalidSessionUrl;
            LOG.debug("Session expired due to ajax request, redirecting to '{}'", redirectUrl);

            String ajaxRedirectXml = createAjaxRedirectXml(redirectUrl);
            LOG.debug("Ajax partial response to redirect: {}", ajaxRedirectXml);

            response.setContentType("text/xml");
            response.getWriter().write(ajaxRedirectXml);
        } else {
            String requestURI = getRequestUrl(request);
            LOG.debug("Session expired due to non-ajax request, starting a new session and redirect to requested url '{}'", requestURI);
            request.getSession(true);
            response.sendRedirect(requestURI);
        }

    }

    private String getRequestUrl(HttpServletRequest request) {
        StringBuffer requestURL = request.getRequestURL();

        String queryString = request.getQueryString();
        if (StringUtils.hasText(queryString)) {
            requestURL.append("?").append(queryString);
        }

        return requestURL.toString();
    }

    private String createAjaxRedirectXml(String redirectUrl) {
        return new StringBuilder()
                .append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
                .append("<partial-response><redirect url=\"")
                .append(redirectUrl)
                .append("\"></redirect></partial-response>")
                .toString();
    }

    public void setInvalidSessionUrl(String invalidSessionUrl) {
        this.invalidSessionUrl = invalidSessionUrl;
    }

}
แค่นี้ก็เป็นอันเสร็จเรียบร้อยครับ

thank you  : http://stackoverflow.com/questions/16858017/primefaces-jsf-2-redirect-when-session-expires

ไม่มีความคิดเห็น:

แสดงความคิดเห็น