文章标题

来源:互联网 发布:俄罗斯聊天软件下载 编辑:程序博客网 时间:2024/06/08 18:46

配置信息的加载

  从《how tomcat works》(深入剖析tomcat)第10章的例程Bootstrap1可以看到,有关安全认证的配置是作为listener载入到context中的:

LifecycleListener listener = new SimpleContextConfig();((Lifecycle) context).addLifecycleListener(listener);

  查看SimpleContextConfig的代码,可以看到其监听了START_EVENT事件:

public void lifecycleEvent(LifecycleEvent event) {    if (Lifecycle.START_EVENT.equals(event.getType())) {      context = (Context) event.getLifecycle();      authenticatorConfig();      context.setConfigured(true);    }  }

  当context启动时,会通知注册的监听器相关事件的发生:

lifecycle.fireLifecycleEvent(START_EVENT, null);

安全验证配置

  SimpleContextConfig的lifecycleEvent方法就会被调用,在之中主要的操作是调用了authenticatorConfig()方法,再来查看authenticatorConfig()的代码:

  private synchronized void authenticatorConfig() {    // Does this Context require an Authenticator?    SecurityConstraint constraints[] = context.findConstraints();    if ((constraints == null) || (constraints.length == 0))      return;    LoginConfig loginConfig = context.getLoginConfig();    if (loginConfig == null) {      loginConfig = new LoginConfig("NONE", null, null, null);      context.setLoginConfig(loginConfig);    }    // Has an authenticator been configured already?    Pipeline pipeline = ((StandardContext) context).getPipeline();    if (pipeline != null) {      Valve basic = pipeline.getBasic();      if ((basic != null) && (basic instanceof Authenticator))        return;      Valve valves[] = pipeline.getValves();      for (int i = 0; i < valves.length; i++) {        if (valves[i] instanceof Authenticator)        return;      }    }    else { // no Pipeline, cannot install authenticator valve      return;    }    // Has a Realm been configured for us to authenticate against?    if (context.getRealm() == null) {      return;    }    // Identify the class name of the Valve we should configure    String authenticatorName = "org.apache.catalina.authenticator.BasicAuthenticator";    // Instantiate and install an Authenticator of the requested class    Valve authenticator = null;    try {      Class authenticatorClass = Class.forName(authenticatorName);      authenticator = (Valve) authenticatorClass.newInstance();      ((StandardContext) context).addValve(authenticator);      System.out.println("Added authenticator valve to Context");    }    catch (Throwable t) {    }  }

  可以看到其主要操作是在context的管道中添加BasicAuthenticator阀,当请求到来时,context会从管道从逐级调用各个阀,入口是invoke方法,因此去看下BasicAuthenticator的invoke方法,查看BasicAuthenticator代码会发现其没有重写父类的invoke方法,因此去查看BasicAuthenticator父类AuthenticatorBase的invoke方法:

验证过程

  public void invoke(Request request, Response response,                       ValveContext context)        throws IOException, ServletException {        // If this is not an HTTP request, do nothing        if (!(request instanceof HttpRequest) ||            !(response instanceof HttpResponse)) {            context.invokeNext(request, response);            return;        }        if (!(request.getRequest() instanceof HttpServletRequest) ||            !(response.getResponse() instanceof HttpServletResponse)) {            context.invokeNext(request, response);            return;        }        HttpRequest hrequest = (HttpRequest) request;        HttpResponse hresponse = (HttpResponse) response;        if (debug >= 1)            log("Security checking request " +                ((HttpServletRequest) request.getRequest()).getMethod() + " " +                ((HttpServletRequest) request.getRequest()).getRequestURI());        LoginConfig config = this.context.getLoginConfig();        // Have we got a cached authenticated Principal to record?        if (cache) {            Principal principal =                ((HttpServletRequest) request.getRequest()).getUserPrincipal();            if (principal == null) {                Session session = getSession(hrequest);                if (session != null) {                    principal = session.getPrincipal();                    if (principal != null) {                        if (debug >= 1)                            log("We have cached auth type " +                                session.getAuthType() +                                " for principal " +                                session.getPrincipal());                        hrequest.setAuthType(session.getAuthType());                        hrequest.setUserPrincipal(principal);                    }                }            }        }        // Special handling for form-based logins to deal with the case        // where the login form (and therefore the "j_security_check" URI        // to which it submits) might be outside the secured area        String contextPath = this.context.getPath();        String requestURI = hrequest.getDecodedRequestURI();        if (requestURI.startsWith(contextPath) &&            requestURI.endsWith(Constants.FORM_ACTION)) {            if (!authenticate(hrequest, hresponse, config)) {                if (debug >= 1)                    log(" Failed authenticate() test");                return;            }        }        // Is this request URI subject to a security constraint?        SecurityConstraint constraint = findConstraint(hrequest);        if ((constraint == null) /* &&            (!Constants.FORM_METHOD.equals(config.getAuthMethod())) */ ) {            if (debug >= 1)                log(" Not subject to any constraint");            context.invokeNext(request, response);            return;        }        if ((debug >= 1) && (constraint != null))            log(" Subject to constraint " + constraint);        // Make sure that constrained resources are not cached by web proxies        // or browsers as caching can provide a security hole        if (!(((HttpServletRequest) hrequest.getRequest()).isSecure())) {            HttpServletResponse sresponse =                 (HttpServletResponse) response.getResponse();            sresponse.setHeader("Pragma", "No-cache");            sresponse.setHeader("Cache-Control", "no-cache");            sresponse.setDateHeader("Expires", 1);        }        // Enforce any user data constraint for this security constraint        if (debug >= 1)            log(" Calling checkUserData()");        if (!checkUserData(hrequest, hresponse, constraint)) {            if (debug >= 1)                log(" Failed checkUserData() test");            // ASSERT: Authenticator already set the appropriate            // HTTP status code, so we do not have to do anything special            return;        }        // Authenticate based upon the specified login configuration        if (constraint.getAuthConstraint()) {            if (debug >= 1)                log(" Calling authenticate()");            if (!authenticate(hrequest, hresponse, config)) {                if (debug >= 1)                    log(" Failed authenticate() test");                // ASSERT: Authenticator already set the appropriate                // HTTP status code, so we do not have to do anything special                return;            }        }        // Perform access control based on the specified role(s)        if (constraint.getAuthConstraint()) {            if (debug >= 1)                log(" Calling accessControl()");            if (!accessControl(hrequest, hresponse, constraint)) {                if (debug >= 1)                    log(" Failed accessControl() test");                // ASSERT: AccessControl method has already set the appropriate                // HTTP status code, so we do not have to do anything special                return;            }        }        // Any and all specified constraints have been satisfied        if (debug >= 1)            log(" Successfully passed all security constraints");        context.invokeNext(request, response);    }    {% endcodeblock %}&emsp;&emsp;代码略长,一开始过滤掉了非http请求的情况。如果开启cache的话会首先从session中查找是否已有验证。从context中获取LoginConfig,然后调用了authenticate(hrequest,hresponse, config)方法;然后会查找是否有constraint,没有的话就调用下一个管道,返回。有的话针对constraint做进一步的认证,包括access control,是根据constraint中的用户角色决定是否有权访问。&emsp;&emsp;上述提到的authenticate(hrequest,hresponse, config)方法在AuthenticatorBase是一个抽象方法,其实现在子类BasicAuthenticator中:{% codeblock lang:java %}public boolean authenticate(HttpRequest request,                                HttpResponse response,                                LoginConfig config)        throws IOException {        // Have we already authenticated someone?        Principal principal =            ((HttpServletRequest) request.getRequest()).getUserPrincipal();        if (principal != null) {            if (debug >= 1)                log("Already authenticated '" + principal.getName() + "'");            return (true);        }        // Validate any credentials already included with this request        HttpServletRequest hreq =            (HttpServletRequest) request.getRequest();        HttpServletResponse hres =            (HttpServletResponse) response.getResponse();        String authorization = request.getAuthorization();        String username = parseUsername(authorization);        String password = parsePassword(authorization);        principal = context.getRealm().authenticate(username, password);        if (principal != null) {            register(request, response, principal, Constants.BASIC_METHOD,                     username, password);            return (true);        }        // Send an "unauthorized" response and an appropriate challenge        String realmName = config.getRealmName();        if (realmName == null)            realmName = hreq.getServerName() + ":" + hreq.getServerPort();    //        if (debug >= 1)    //            log("Challenging for realm '" + realmName + "'");        hres.setHeader("WWW-Authenticate",                       "Basic realm=\"" + realmName + "\"");        hres.setStatus(HttpServletResponse.SC_UNAUTHORIZED);        //      hres.flushBuffer();        return (false);    }

  可以看到,验证成功则交给下一级阀处理,失败则返回UNAUTHORIZED(即HTTP 401状态码),浏览器在第一次接受到401状态码时会自动弹出验证窗口,用户填完验证信息并点击登录,浏览器会见携带验证信息再发送一次请求,如果再次接受到401状态码,则会显示验证失败。

0 0
原创粉丝点击