副问题[/!--empirenews.page--]
大量的 Web 应用都有安详相干的需求,正因云云,Servlet 类型提议容器要有满意这些需求的机制和基本办法,以是容器要对以下安详特征予以支持:
- 身份验证:验证授权用户的用户名和暗码
- 资源会见节制:限定某些资源只应承部门用户会见
- 数据完备性:可以或许证明数据在传输进程中未被第三方修改
- 机要性或数据隐私:传输加密(SSL),确保信息只能被信赖用户会见
本文就以上题目,对 Tomcat 容器提供的认证和鉴权的计划与实现,以及内部单点登录的道理举办说明。首发于微信公家号顿悟源码.
1. 授权
容器和 Web 应用回收的是基于脚色的权限会见节制方法,个中容器必要实现认证和鉴权的成果,而 Web 应用则要实现授权的成果。
在 Servlet 类型中描写了两种授权方法:声明式安详和编程式安详。声明式安详就是在陈设描写符中声明脚色、资源会见权限和认证方法。以下代码片断摘自 Tomcat 自带的 Manager 应用的 web.xml:
- <security-constraint> <!-- 安详束缚 -->
- <web-resource-collection> <!-- 限定会见的资源荟萃 -->
- <web-resource-name>HTML Manager commands</web-resource-name>
- <url-pattern>/html/*</url-pattern>
- </web-resource-collection>
- <auth-constraint><!-- 授权可会见此资源荟萃的脚色 -->
- <role-name>manager-gui</role-name>
- </auth-constraint>
- </security-constraint>
-
- <login-config><!-- 设置验证要领 -->
- <auth-method>BASIC</auth-method>
- <realm-name>Tomcat Manager Application</realm-name>
- </login-config>
-
- <security-role><!-- 界说一个安详脚色 -->
- <description>
- The role that is required to access the HTML Manager pages
- </description>
- <role-name>manager-gui</role-name>
- </security-role>
这些安详相干的设置,城市在应用陈设时,初始化和配置到 StandardContext 工具中。更多具体的内容可查察类型对陈设描写文件的表明,接下来看 Tomcat 怎么计划和实现认证及鉴权。
2. 认证和鉴权的计划
Servlet 类型固然描写了 Web 应用声明安详束缚的机制,但没有界说容器与关联用户和脚色信息之间的接口。因此,Tomcat 界说了一个 Realm 接口,用于适配身份验证的各类信息源。整体计划的类图如下:
上图中,包括了各个类的焦点要领,要害类或接口的浸染如下:
- Realm - 译为域,域有泛指某种范畴的意思,在这个范畴内存储着用户名、暗码、脚色和权限,而且提供身份和权限验证的成果,典范的这个范畴可所以某个设置文件或数据库
- CombinedRealm - 内部包括一个或多个 Realm,按设置次序执行身份验证,任一 Realm 验证乐成,则暗示乐成验证
- LockOutRealm - 提供用户锁定机制,防备在一按时刻段有过多身份验证失败的实行
- Authenticator - 差异身份验证要领的接口,首要有 BASIC、DIGEST、FORM、SSL 这几种尺度实现
- Principal - 对认证主体的抽象,它包括用户身份和权限信息
- SingleSignOn - 用于支持容器内多应用的单点登录成果
2.1 初始化
Realm 是容器的一个可嵌套组件,可以嵌套在 Engine、Host 和 Context 中,而且子容器可以包围父容器设置的 Realm。默认的 server.xml 在 Engine 中设置了一个 LockOutRealm 组合域,内部包括一个 UserDatabaseRealm,它从设置的全局资源 conf/tomcat-users.xml 中提取用户信息。
web.xml 中声明的安详束缚会初始化成对应的 SecurityConstraint、SecurityCollection 和 LoginConfig 工具,并关联到一个 StandardContext 工具。
在上图可以看到,AuthenticatorBase 还实现了 Valve 接口,StandardContext 工具在设置的进程中,假如发明声明白尺度的验证要领,那么就会把它插手到本身的 Pipeline 中。
3. 一次哀求认证和鉴权进程
Context 在 Tomcat 内部就代表着一个 Web 应用,假设设置行使 BASIC 验证要领,那么 Context 内部的 Pipeline 就有 BasicAuthenticator 和 StandardContextValve 两个阀门,当哀求进入 Context 管道时,就起首举办认证和鉴权,要领挪用如下:
整个进程的焦点代码就在 AuthenticatorBase 的 invoke 要领中:
- public void invoke(Request request, Response response) throws IOException, ServletException {
- LoginConfig config = this.context.getLoginConfig();
- // 0. Session 工具中是否缓存着一个已经举办身份验证的 Principal
- if (cache) {
- Principal principal = request.getUserPrincipal();
- if (principal == null) {
- Session session = request.getSessionInternal(false);
- if (session != null) {
- principal = session.getPrincipal();
- if (principal != null) {
- request.setAuthType(session.getAuthType());
- request.setUserPrincipal(principal);
- }
- }
- }
- }
- // 对付基于表单登录,也许位于安详域之外的非凡环境举办处理赏罚
- String contextPath = this.context.getPath();
- String requestURI = request.getDecodedRequestURI();
- if (requestURI.startsWith(contextPath) && requestURI.endsWith(Constants.FORM_ACTION)) {
- return;
- }
- }
- // 获取安详域工具,默认设置是 LockOutRealm
- Realm realm = this.context.getRealm();
- // 按照哀求 URI 实行获取设置的安详束缚
- SecurityConstraint [] constraints = realm.findSecurityConstraints(request, this.context);
-
- if ((constraints == null) /* && (!Constants.FORM_METHOD.equals(config.getAuthMethod())) */ ) {
- // 为 null 暗示会见的资源没有安详束缚,直接会见下一个阀门
- getNext().invoke(request, response);
- return;
- }
- // 确保受束缚的资源不会被 Web 署理或赏识器缓存,由于缓存也许会造成安详裂痕
- if (disableProxyCaching &&
- !"POST".equalsIgnoreCase(request.getMethod())) {
- if (securePagesWithPragma) {
- response.setHeader("Pragma", "No-cache");
- response.setHeader("Cache-Control", "no-cache");
- } else {
- response.setHeader("Cache-Control", "private");
- }
- response.setHeader("Expires", DATE_ONE);
- }
- int i;
- // 1. 搜查用户数据的传输安详束缚
- if (!realm.hasUserDataPermission(request, response, constraints)) {
- // 验证失败
- // Authenticator已经配置了恰当的HTTP状态代码,因此我们不必做任何非凡的工作
- return;
- }
- // 2. 搜查是否包括授权束缚,也就是脚色验证
- boolean authRequired = true;
- for(i=0; i < constraints.length && authRequired; i++) {
- if(!constraints[i].getAuthConstraint()) {
- authRequired = false;
- } else if(!constraints[i].getAllRoles()) {
- String [] roles = constraints[i].findAuthRoles();
- if(roles == null || roles.length == 0) {
- authRequired = false;
- }
- }
- }
- // 3. 验证用户名和暗码
- if(authRequired) {
- // authenticate 是一个抽象要领,由差异的验证要领实现
- if (!authenticate(request, response, config)) {
- return;
- }
- }
- // 4. 验证用户是否包括授权的脚色
- if (!realm.hasResourcePermission(request, response,constraints,this.context)) {
- return;
- }
- // 5. 已满意任何和全部指定的束缚
- getNext().invoke(request, response);
- }
(编辑:河北网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|