文章标题
来源:互联网 发布:俄罗斯聊天软件下载 编辑:程序博客网 时间: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 %}  代码略长,一开始过滤掉了非http请求的情况。如果开启cache的话会首先从session中查找是否已有验证。从context中获取LoginConfig,然后调用了authenticate(hrequest,hresponse, config)方法;然后会查找是否有constraint,没有的话就调用下一个管道,返回。有的话针对constraint做进一步的认证,包括access control,是根据constraint中的用户角色决定是否有权访问。  上述提到的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
- 文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题 文章标题 文章标题 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- ubuntu安装GitLab
- CTFCrypto练习之RSA算法
- 使用canvas实现行走的小人动画
- 25. Reverse Nodes in k-Group
- git 创建服务端库Shell脚本
- 文章标题
- Android SharePreferences 源码分析
- 刘汝佳的算法竞赛入门经典(第2版) 习题解答
- #循环中的continue 和 break
- zoj2099
- java反射机制
- 简单的js验证码
- 关于maven本地仓库新建项目报错的解决
- 想要学习Linux技术,先好好的读一本Linux书籍吧