一、领域(Realm):

1、Principal接口代表角色信息,包含了三个成员:用户名、密码、role列表(以逗号分隔),对应了tomcat-users.xml文件中一行user信息:

GenericPrincipal作为Principal接口的默认实现类,提供了hasRole函数,通过这个函数可以判断该角色是否支持指定的role;

2、Realm接口代表领域对象,是一个用来对用户进行身份验证的组件;可以认为Realm是Principal的管理器,包含了用户角色的集合;每个Realm对象都会与一个context容器对象相关联;hasRole方法判断指定的用户角色是否支持指定的role,authenticate用来判断进行用户身份验证,该方法重载了四个实现方法,其中最重要和最常用的是用户名和密码验证

3、Realm接口的基本实现形式是RealmBase类,这是一个抽象类,提供了具体领域实现类的相同操作部分,不同部分由具体子类自己去实现,默认情况下会使用MemoryRealm类的实例作为验证用的领域对象;

4、当第一次调用MemoryRealm实例时,它会读取tomcat-users.xml文件的内容,创建Digester对象读取:

可以看到读取配置文件时使用了MemoryRuleset规则对象,在MemoryRuleset对象读取时:

可以看到该规则会读取tomcat-users/下的username, password, roles到一个GenericPrincipal对象中并添加到领域对象Realm中;

二、安全验证机制:

  • loginConfig是登录配置类,loginConfig实例封装了领域对象的名字和身份验证方法,身份验证方法有:BASIC, FORM, DIGEST和CLIENT-CERT;如果身份验证方法是FORM,还需要指定loginpage和errorPage属性;
  • loginConfig实例会读取web项目的WEB-INF/web.xml文件里面的login-config配置,比如在web.xml项目里添加如下配置,则在浏览器中访问该项目时会弹出身份验证的对话框:
    <security-constraint>
<web-resource-collection>
<web-resource-name>MySecurityTest</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin-gui</role-name>
<role-name>manager-gui</role-name>
</auth-constraint>
</security-constraint> <login-config>
<auth-method>BASIC</auth-method>
<realm-name>MySecurityTest</realm-name>
</login-config>

  

  1)  web-resource-collection:

  此元素确定应该保护的资源,所有security-constraint元素都必须包含至少一个web-resource-collection项.此元素由一个给出任意标识名称的web-resource-name元素、一个确定应该保护URL    的url-pattern元素、一个指出此保护所适用的HTTP命令(GET、POST等,缺省为所有方法)的http-method元素和一个提供资料的可选description元素组成。

  2)   auth-constraint:

  指出哪些用户应该具有受保护资源的访问权。此元素应该包含一个或多个标识具有访问权限的用户类别role-name元素,以及包含(可选)一个描述角色的description元素。

  3)   user-data-constraint:

  这是个可选的元素,指出在访问相关资源时使用任何传输层保护。它必须包含一个transport-guarantee子元素(合法值为NONE、INTEGRAL或CONFIDENTIAL),并且可选地包含一个description元素。transport-guarantee为NONE值将对所用的通讯协议不加限制。INTEGRAL值表示数据必须以一种防止截取它的人阅读它的方式传送。虽然原理上(并且在未来的HTTP版本中),在INTEGRAL和CONFIDENTIAL之间可能会有差别,但在当前实践中,他们都只是简单地要求用SSL。

  4)   login-config:

  auth-method指出了认证的方法,一共有四种:BASIC,FORM,DIGEST,CLIENT-CERT,realm-name表示域名,对于FORM的认证方式还需要指定loginpage和errorpage,对于下面的form表单:

<form name="loginform" method="post" action="j_security_check">   

    <INPUT name="j_username" type="text">   

    <INPUT name="j_password" TYPE="password">   

    <input type="submit" value="登 录" >   

</form> 

  form 的action 必须是j_security_check, method="post", 用户名 name="j_username" , 密码name="j_password"  这些都是固定的元素;

  • loginConfig类对应上面xml配置文件里面的login-config项,SecurityCollection类对应配置文件里面的web-resource-collection,SecurityConstraint对应配置文件里面的security-constraint(web-resource-collection配置是作为security-constraint的子配置项存在的),将配置文件解析到对应的对象里面是通过Digester对象解析,解析时的规则类是WebRuleSet类;
  • Authenticator是代表验证的接口,这个只是一个空的接口,起到一个标识的作用,AuthenticatorBase实现了这个接口,实现了不同验证方式的公共代码,不同的验证方式均是从这个类派生出子类来实现的;这个类同时也是一个阀(valve),他会安装到context容器的pipeline列表中,这样在访问web项目时会调用pipeline.invoke,也就是会调用验证阀进行身份验证;
  • 安全认证访问流程:

    1)   standardcontext.start():调用lifecycle.fireLifecycleEvent(START_EVENT, null);发送START_EVENT事件;

    2)   ContextConfig接收到start事件后调用start方法;

    3)   Start方法中调用ContextConfig.authenticatorConfig方法,加载BasicAuthenticator对象并添加到context.pipeline.valves[]中;

    4)   当有连接请求到达时,会调用HttpProcessor.parseHeaders方法,里面header里面有authorization字段,则设置request.authorization标记为true;

    5)Context.invoke方法会调用pipeline.invoke,然后会调用StandardPipelineValveContext.invokeNext方法,在这里面会调用到验证阀AuthenticatorBase.invoke方法,其中代码段如下:

      

      这里的authenticate方法会调用不同验证子类的authenticate方法来验证;

  • BASIC验证:

    BASIC的验证方式相对不是很安全,验证字符串以“Authorization: Basic username:password”的形式发送,其中username:password以base64编码的形式存在,如果被截获后将这个字符串进行base64解码,即可查看到用户名和密码明文(如果请求是在安全的SSL层上,则BASIC验证方式依然是安全的);

    认证规则如下:

      1)   客户端访问受保护的资源;

      2)   服务器返回401 Unauthorized状态,响应头信息如下图所示,其中WWW-Authenticate:Basic realm="MyRealm"表示该资源的受保护信息。

      3)   浏览器根据响应弹出窗口,提示用户输入用户名和密码。

      4)   浏览器将客户端将输入的用户名、密码用Base64算法进行加密后发送给服务器。例如,使用用户名、密码都是“java”进行登录,浏览器则发送的请求头中包含“Authorization: Basic amF2YTpqYXZh”,其中“amF2YTpqYXZh”是用户名、密码组成的字符串“java:java”进行Base64加密得到的结果。

      5)   如果认证成功,则返回相应的受保护资源。如果认证失败,则仍返回401 Unauthorized状态,要求重新进行认证。

  • DIGEST验证:

    Digest认证提供了一种不使用明文发送用户名密码的方式。Digest认证的规则如下:    

      1)   客户端访问受保护的资源。

      2)   服务器返回401 Unauthorized状态,响应头信息如下图所示,其中

        WWW-Authenticate:Digest realm="MyRealm", qop="auth", nonce="1454307975468:a0aefce3e84d69723e6f04fda5674ad0", opaque="23BB4CB60BFE2CD08B490A16B86C9661"

        表示相关的安全域信息、随机数信息(nonce)等。

      3)   浏览器根据响应弹出窗口,提示用户输入用户名和密码。

      4)   浏览器将客户端将输入的用户名以明文的方式、密码等其他信息以摘要的方式返回给服务端。

      5)   服务端将用户名、正确的密码等信息按规则进行摘要加密,与客户端提供的信息进行比对。如果认证成功,则返回相应的受保护资源。如果认证失败,则仍返回401 Unauthorized状态,要求重新进行认证。

  • FORM验证:    

    Basic和Digest认证由于其自身的设计,各浏览器的实现都是弹出一个无所谓美观的对话框,对用户体验有很大的影响。Form认证中定义了采集用户信息的登录页面、登录失败页面,通过用户自定义实现这两个页面,能够完成美观的登录操作。在web.xml中配置Form认证方式及登录页面示例如下:

<login-config>

    <auth-method>FORM</auth-method>

    <realm-name>file</realm-name>

    <form-login-config>

        <form-login-page>/login.xhtml</form-login-page>

        <form-error-page>/error.xhtml</form-error-page>

    </form-login-config>

</login-config>  

  在Servlet规范中规定,使用Form认证时,表单提交的action必须为j_security_check,而获取登录信息的字段必须为j_username和j_password。

  认证流程如下:      

    1)   查看是否已经对当前用户进行了认证,避免重复认证造成资源浪费:checkForCachedAuthentication(request, response, true)。

    2)   如果没有认证,则需要保存当前用户需要保存的页面:saveRequest(request, session),然后跳转到登录页面:forwardToLoginPage(request, response, config)。

    3)   用户提交了用户名和密码,进行认证工作:principal = realm.authenticate(username, password);如果认证失败,则跳转至失败页面:forwardToErrorPage(request, response, config);如果认证成功,则跳转至第二步保存的页面:response.sendRedirect(response.encodeRedirectURL(uri))。

    4)   浏览器接收到302重定向状态码后,将页面跳转至最初访问的页面。

    5)   再次走进Form认证器的认证流程,通过判断条件matchRequest(request)将认证主体(Principal)保存在Request和Session中,判断条件为:已经通过了认证;存在一个已保存的页面且与当前请求页面路径相同。然后将本次请求的所有信息都重置为最初的请求信息:restoreRequest(request, session)。此后的访问在第一步即直接返回了。

  • CLIENT-CERT验证:    

    Client认证依赖于HTTPS,因此是Java EE安全规范中安全性最高的一种认证方式。使用Client认证需要在web.xml中配置如下:

    <login-config>

        <auth-method>CLIENT-CERT</auth-method>

    </login-config>

tomcat源码阅读之安全机制的更多相关文章

  1. Tomcat源码阅读(二)初始化

    近来,我开始阅读tomcat的源码,感觉还挺清晰易懂:为了方便理解,我参考了网上的一些文章,把tomcat的组成归纳一下:整个tomcat的组成如下图所示: Tomcat在接收到用户请求时,将会通过以 ...

  2. tomcat源码阅读

    1      工具准备 需要SVN.Maven.JDK.Eclipse.Eclipse M2插件 2      下载源码及发布包 源码在这里:http://svn.apache.org/repos/a ...

  3. tomcat源码阅读之过滤器

    一.Servlet过滤器: 1.介绍: Servlet过滤器本身并不生成请求和响应对象,它只提供过滤作用. Servlet过滤器能够在Servlet被调用之前检查Request对象,修改Request ...

  4. tomcat源码阅读之SingleThreadModel

    一.接口简介: 实现了SingleThreadModel接口的servlet类只能保证在同一时刻,只有一个线程执行该servlet实例的service方法,在tomcat实现中会创建多个servlet ...

  5. tomcat源码阅读之载入器(Loader)

    一.Java类的载入器: 双亲委派模型: 1.JVM提供了三种类型的类加载器:引导类载入器(bootstrap class loader).扩展类载入器(extension class loader) ...

  6. Python3 源码阅读 - 垃圾回收机制

    Python的垃圾回收机制包括了两大部分: 引用计数(大部分在 Include/object.h 中定义) 标记清除+隔代回收(大部分在 Modules/gcmodule.c 中定义) 1. 引用计数 ...

  7. tomcat源码阅读之BackupManager

    一. 配置: <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOpti ...

  8. tomcat源码阅读之集群

    一. 配置: 在tomcat目录下的conf/Server.xml配置文件中增加如下配置: <!-- Cluster(集群,族) 节点,如果你要配置tomcat集群,则需要使用此节点. clas ...

  9. tomcat源码阅读之StandardContext

    Context实例表示一个具体的web应用程序,其中包含一个或者多个Wrapper实例,每个Wrapper表示一个具体的servlet定义.StandardContext类是Context接口的标准实 ...

随机推荐

  1. hdu1238 kmp

    You are given a number of case-sensitive strings of alphabetic characters, find the largest string X ...

  2. hdu-1849-nim模板

    Rabbit and Grass Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  3. 10046 event 知多少

    10046 event 知多少 2017年5月10日 10:08 1.在当前session级打开trace 适用于SQL语句可以在新的session创建后再运行. 在session级收集10046 t ...

  4. 关于vue Unexpected identifier 问题

    vue对于es6虽然自带babel转换 但是在index.html文件中并不会发生转换 因此在index.html中使用新的语法会导致低版本浏览器不识别代码因此报出Unexpected identif ...

  5. idea破解更新

    idea破解教程: https://www.cnblogs.com/jpfss/p/8872358.html JetbrainsCrack-3.1-release-enc.jar下载:http://i ...

  6. anaconda环境变量+修改jupyter默认路径

    手贱在安装的时候没有点添加环境变量 安装好后,用anaconda prompt运行一些程序命令之类都是可以的,但是直接打开cmd就不行了,为了省事,所以决定手动添加环境变量, %\ProgramDat ...

  7. uart boot log

    CC开始 xmodem 传输. 按 Ctrl+C 取消. 100% 95 KB 4 KB/s 00:00:23 0 Errors U-Boot SPL 2018.01 (Jan 16 2019 - 1 ...

  8. [转载]oracle游标概念讲解

    原文URL:http://www.2cto.com/database/201203/122387.html ORACLE游标概念讲解 什么是游标?  ①从表中检索出结果集,从中每次指向一条记录进行交互 ...

  9. SpingBoot三——基础架构

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:https://www.cnblogs.com/by-dream/p/10492073.html 继续上一节,为了更好的开发,现将 ...

  10. String类型的注意事项

    1.string类型是可变长字符序列,而vector是集合,存放的是某种类型的可变长序列 2.string类型对象的初始化有多种方式:string str="Hello",是将字符 ...