提交 25c1a668 编写于 作者: K kohsuke

implemented BASIC authentication support.


git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@3460 71c3de6d-444a-0410-be80-ed276b4c234a
上级 f8a2c9d9
package hudson;
import hudson.model.Hudson;
import hudson.util.Scrambler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
/**
* Implements the dual authentcation mechanism.
*
* <p>
* Hudson supports both the HTTP basic authentication and the form-based authentication.
* The former is for scripted clients, and the latter is for humans. Unfortunately,
* becase the servlet spec does not allow us to programatically authenticate users,
* we need to rely on some hack to make it work, and this is the class that implements
* that hack.
*
* <p>
* When an HTTP request arrives with an HTTP basic auth header, this filter detects
* that and emulate an invocation of <tt>/j_security_check</tt>
* (see <a href="http://mail-archives.apache.org/mod_mbox/tomcat-users/200105.mbox/%3C9005C0C9C85BD31181B20060085DAC8B10C8EF@tuvi.andmevara.ee%3E">this page</a> for the original technique.)
*
* <p>
* This causes the container to perform authentication, but there's no way
* to find out whether the user has been successfully authenticated or not.
* So to find this out, we then redirect the user to
* {@link Hudson#doSecured(StaplerRequest, StaplerResponse) <tt>/secured/...</tt> page}.
*
* <p>
* The handler of the above URL checks if the user is authenticated,
* and if not report an HTTP error code. Otherwise the user is
* redirected back to the original URL, where the request is served.
*
* <p>
* So all in all, the redirection works like <tt>/abc/def</tt> -> <tt>/secured/abc/def</tt>
* -> <tt>/abc/def</tt>.
*
* <h2>Notes</h2>
* <ul>
* <li>
* The technique of getting a request dispatcher for <tt>/j_security_check</tt> may not
* work for all containers, but so far that seems like the only way to make this work.
* <li>
* This A->B->A redirect is a cyclic redirection, so we need to watch out for clients
* that detect this as an error.
* </ul>
*
* @author Kohsuke Kawaguchi
*/
public class BasicAuthenticationFilter implements Filter {
private ServletContext servletContext;
public void init(FilterConfig filterConfig) throws ServletException {
servletContext = filterConfig.getServletContext();
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse rsp = (HttpServletResponse) response;
String authorization = req.getHeader("Authorization");
String path = req.getServletPath();
if(authorization==null || req.getUserPrincipal()!=null || path.startsWith("/secured/")) {
// normal requests
chain.doFilter(request,response);
return;
}
// authenticate the user
String username = null;
String password = null;
String uidpassword = Scrambler.descramble(authorization.substring(6));
int idx = uidpassword.indexOf(':');
if (idx >= 0) {
username = uidpassword.substring(0, idx);
password = uidpassword.substring(idx+1);
}
if(username==null) {
rsp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
rsp.setHeader("WWW-Authenticate","Basic realm=\"Hudson administrator\"");
return;
}
path = req.getContextPath()+"/secured"+path;
String q = req.getQueryString();
if(q!=null)
path += '?'+q;
// prepare a redirect
rsp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
rsp.setHeader("Location",path);
// ... but first let the container authenticate this request
RequestDispatcher d = servletContext.getRequestDispatcher("/j_security_check?j_username="+
URLEncoder.encode(username,"UTF-8")+"&j_password="+URLEncoder.encode(password,"UTF-8"));
d.include(req,rsp);
}
//public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// HttpServletRequest req = (HttpServletRequest) request;
// String authorization = req.getHeader("Authorization");
//
// String path = req.getServletPath();
// if(authorization==null || req.getUserPrincipal()!=null || path.startsWith("/secured/")) {
// chain.doFilter(request,response);
// } else {
// if(req.getQueryString()!=null)
// path += req.getQueryString();
// ((HttpServletResponse)response).sendRedirect(req.getContextPath()+"/secured"+path);
// }
//}
public void destroy() {
}
}
......@@ -2,6 +2,7 @@ package hudson.model;
import com.thoughtworks.xstream.XStream;
import groovy.lang.GroovyShell;
import hudson.BasicAuthenticationFilter;
import hudson.FeedAdapter;
import hudson.FilePath;
import hudson.Functions;
......@@ -42,7 +43,6 @@ import hudson.util.CopyOnWriteList;
import hudson.util.CopyOnWriteMap;
import hudson.util.FormFieldValidator;
import hudson.util.MultipartFormDataParser;
import hudson.util.Scrambler;
import hudson.util.XStream2;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
......@@ -52,7 +52,6 @@ import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
......@@ -65,7 +64,6 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URLEncoder;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
......@@ -1242,44 +1240,24 @@ public final class Hudson extends View implements ItemGroup<TopLevelItem>, Node
}
/**
* Authenticate the user by using the HTTP BASIC auth protocol.
* <p>
* This is useful for scripted clients, and complements the default
* form-based authentication.
* Checks if the user was successfully authenticated.
*
* @see BasicAuthenticationFilter
*/
public void doSecured( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
String authorization = req.getHeader("Authorization");
String username = null;
String password = null;
if(authorization!=null) {
String uidpassword = Scrambler.descramble(authorization.substring(6));
int idx = uidpassword.indexOf(':');
if (idx >= 0) {
username = uidpassword.substring(0, idx);
password = uidpassword.substring(idx+1);
}
}
if(authorization==null || username==null) {
if(req.getUserPrincipal()==null) {
// authentication must have failed
rsp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
rsp.setHeader("WWW-Authenticate","Basic realm=\"Hudson administrator\"");
return;
}
// the user is now authenticated, so send him back to the target
String path = req.getContextPath()+req.getRestOfPath();
String q = req.getQueryString();
if(q!=null)
path += q;
// prepare a redirect
rsp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
rsp.setHeader("Location",path);
path += '?'+q;
// ... but first let the container authenticate this request
RequestDispatcher d = req.getServletContext().getRequestDispatcher("/j_security_check?j_username="+
URLEncoder.encode(username,"UTF-8")+"&j_password="+URLEncoder.encode(password,"UTF-8"));
d.include(req,rsp);
rsp.sendRedirect2(path);
}
/**
......
......@@ -26,6 +26,15 @@
<url-pattern>*.jelly</url-pattern>
</servlet-mapping>
<filter>
<filter-name>authentication-filter</filter-name>
<filter-class>hudson.BasicAuthenticationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>authentication-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>hudson.WebAppMain</listener-class>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册