001: /* AcegiSecurityContextListener.java
002:
003: {{IS_NOTE
004: Purpose:
005:
006: Description:
007:
008: History:
009: Tue Sep 11 12:55:11 2006, Created by henrichen
010: }}IS_NOTE
011:
012: Copyright (C) 2006 Potix Corporation. All Rights Reserved.
013:
014: {{IS_RIGHT
015: This program is distributed under GPL Version 2.0 in the hope that
016: it will be useful, but WITHOUT ANY WARRANTY.
017: }}IS_RIGHT
018: */
019: package org.zkoss.zkplus.acegi;
020:
021: import org.zkoss.zkplus.spring.SpringUtil;
022:
023: import org.zkoss.zk.ui.Component;
024: import org.zkoss.zk.ui.Execution;
025: import org.zkoss.zk.ui.Executions;
026: import org.zkoss.zk.ui.UiException;
027: import org.zkoss.zk.ui.event.Event;
028: import org.zkoss.zk.ui.event.EventThreadInit;
029: import org.zkoss.zk.ui.event.EventThreadCleanup;
030: import org.zkoss.zk.ui.event.EventThreadResume;
031: import org.zkoss.zk.au.Command;
032:
033: import org.zkoss.util.logging.Log;
034: import org.zkoss.web.servlet.BufferedResponse;
035: import org.zkoss.io.NullWriter;
036: import org.zkoss.lang.Exceptions;
037:
038: import org.acegisecurity.context.SecurityContext;
039: import org.acegisecurity.context.SecurityContextHolder;
040: import org.acegisecurity.AcegiSecurityException;
041: import org.acegisecurity.AuthenticationException;
042:
043: import java.util.List;
044:
045: import javax.servlet.Filter;
046: import javax.servlet.FilterChain;
047: import javax.servlet.ServletException;
048: import javax.servlet.ServletRequest;
049: import javax.servlet.ServletResponse;
050:
051: /**
052: * <p>Listener to copy servlet thread ThreadLocal, securityContext, over to
053: * event thread ThreadLocal and handle Acegi Authentication Exception occured in
054: * Event handling (e.g. Acegi's MethodInterceptor).
055: * </p>
056: * <p>
057: * Whenever you use Acegi as your security provider you have to add following
058: * lines in WEB-INF/zk.xml:
059: * <pre><code>
060: * <listener>
061: * <description>Acegi SecurityContext Handler</description>
062: * <listener-class>org.zkoss.zkplus.acegi.AcegiSecurityContextListener</listener-class>
063: * </listener>
064: * </code></pre>
065: * </p>
066: *
067: * @author henrichen
068: */
069: public class AcegiSecurityContextListener implements EventThreadInit,
070: EventThreadCleanup, EventThreadResume {
071: private static final Log log = Log
072: .lookup(AcegiSecurityContextListener.class);
073: private SecurityContext _context;
074:
075: //-- EventThreadInit --//
076: public void prepare(Component comp, Event evt) {
077: _context = SecurityContextHolder.getContext(); //get threadLocal from servlet thread
078: }
079:
080: public boolean init(Component comp, Event evt) {
081: SecurityContextHolder.setContext(_context); //store into event thread
082: _context = null;
083: return true;
084: }
085:
086: //-- EventThreadCleanup --//
087: public void cleanup(Component comp, Event evt, List errs) {
088: _context = SecurityContextHolder.getContext(); //get threadLocal from event thread
089:
090: //handle Acegi Exception occured within Event handling
091: final Execution exec = Executions.getCurrent();
092: if (errs != null && !errs.isEmpty() && errs.size() == 1) {
093: Throwable ex = (Throwable) errs.get(0);
094: if (ex != null) {
095: ex = Exceptions.findCause(ex,
096: AcegiSecurityException.class);
097: if (ex instanceof AcegiSecurityException) {
098: //ZK massage the exception to visual message (not an exception), so
099: //we remember the exception in request attribute and let ZkEventExceptionFilter
100: //to rethrow the exception so Acegi's ExcepitonTranslationFilter can
101: //catch that and show login window.
102:
103: //to avoid show the massaged visula message
104: errs.clear();
105:
106: exec.setAttribute(ZkEventExceptionFilter.EXCEPTION,
107: ex);
108: exec.setAttribute(ZkEventExceptionFilter.COMPONENT,
109: comp);
110: exec
111: .setAttribute(ZkEventExceptionFilter.EVENT,
112: evt);
113: }
114: }
115: }
116:
117: //there was other exception, no need to go thru acegi filter chain.
118: if (errs != null && !errs.isEmpty())
119: return;
120:
121: //carry the current event that would be used by the filter chain.
122: exec.setAttribute(
123: ZkAuthenticationProcessingFilter.CURRENT_EVENT, evt);
124:
125: Filter filter = (Filter) SpringUtil
126: .getBean("zkFilterChainProxy");
127: if (filter != null) {
128: ServletRequest request = (ServletRequest) exec
129: .getNativeRequest();
130: ServletResponse response = (ServletResponse) exec
131: .getNativeResponse();
132: ServletResponse resp = BufferedResponse.getInstance(
133: response, new NullWriter());
134: try {
135: filter.doFilter(request, resp, new NullFilterChain());
136: } catch (Exception ex1) {
137: throw UiException.Aide.wrap(ex1); //should never occur
138: }
139:
140: //after filter chain, SecurityContext could have changed
141: _context = SecurityContextHolder.getContext(); //get threadLocal from event thread
142: }
143: }
144:
145: public void complete(Component comp, Event evt) {
146: SecurityContextHolder.setContext(_context); //store into servlet thread
147: _context = null;
148: }
149:
150: //-- EventThreadResume --//
151: public void beforeResume(Component comp, Event evt) {
152: _context = SecurityContextHolder.getContext(); //get threadLocal from servlet thread
153: }
154:
155: public void afterResume(Component comp, Event evt) {
156: SecurityContextHolder.setContext(_context); //store into event thread
157: _context = null;
158: }
159:
160: public void abortResume(Component comp, Event evt) {
161: //do nothing
162: }
163:
164: private static class NullFilterChain implements FilterChain {
165: public void doFilter(ServletRequest request,
166: ServletResponse response) throws java.io.IOException,
167: ServletException {
168: //do nothing
169: }
170: }
171: }
|