用户登录实现流程

相关代码文件为:src/main/java/org/geoserver/security/filter/GeoServerUserNamePasswordAuthenticationFilter.java

package org.geoserver.security.filter;

import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.geoserver.security.GeoServerSecurityFilterChain;
import org.geoserver.security.config.SecurityNamedServiceConfig;
import org.geoserver.security.config.UsernamePasswordAuthenticationFilterConfig;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;

/**
 * User name / password authentication filter
 * 
 * 核心功能:处理基于表单的用户名/密码认证流程
 * 1. 拦截登录请求
 * 2. 提取用户名/密码
 * 3. 执行认证逻辑
 * 4. 处理认证结果(成功/失败)
 * 5. 提供认证入口点
 *
 * @author christian
 */
public class GeoServerUserNamePasswordAuthenticationFilter extends GeoServerCompositeFilter
        implements GeoServerAuthenticationFilter {

    // 登录处理相关URL常量
    public static final String URL_LOGIN_SUCCCESS = "/web";  // 登录成功后的默认重定向地址
    public static final String URL_LOGIN_FAILURE =  // 登录失败重定向地址(带错误参数)
            "/web/wicket/bookmarkable/org.geoserver.web.GeoServerLoginPage?error=true";
    public static final String URL_LOGIN_FORM =     // 登录表单地址(无错误状态)
            "/web/wicket/bookmarkable/org.geoserver.web.GeoServerLoginPage?error=false";

    private LoginUrlAuthenticationEntryPoint aep;  // 认证入口点(处理未认证请求)
    String[] pathInfos;  // 需要启用认证的路径模式

    /**
     * 从配置初始化过滤器
     * 核心配置方法:创建并配置Spring Security的认证过滤器链
     */
    @Override
    public void initializeFromConfig(SecurityNamedServiceConfig config) throws IOException {
        super.initializeFromConfig(config);

        // 获取需要认证的路径列表(逗号分隔)
        pathInfos = GeoServerSecurityFilterChain.FORM_LOGIN_CHAIN.split(",");

        // 转换为用户名密码过滤器的专用配置
        UsernamePasswordAuthenticationFilterConfig upConfig = 
            (UsernamePasswordAuthenticationFilterConfig) config;

        // 1. 配置认证入口点(当访问受保护资源时跳转到登录页)
        aep = new LoginUrlAuthenticationEntryPoint(URL_LOGIN_FORM);
        aep.setForceHttps(false);  // 不强制HTTPS(生产环境应设为true)
        try {
            aep.afterPropertiesSet();  // Spring标准初始化方法
        } catch (Exception e2) {
            throw new IOException(e2);
        }

        // 2. 获取RememberMe服务(实现"记住我"功能)
        RememberMeServices rms = securityManager.getRememberMeService();

        // 3. 创建Spring Security的用户名密码认证过滤器
        UsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter() {
            /**
             * 重写认证请求判断逻辑
             * 只对特定路径的请求启用认证处理
             */
            @Override
            protected boolean requiresAuthentication(
                HttpServletRequest request, HttpServletResponse response) {
                
                // 检查当前请求路径是否在需要认证的路径列表中
                for (String pathInfo : pathInfos) {
                    if (getRequestPath(request).startsWith(pathInfo)) 
                        return true;
                }
                return false;
            }
        };

        // 4. 配置过滤器的基本参数
        filter.setPasswordParameter(upConfig.getPasswordParameterName());  // 密码参数名(默认"password")
        filter.setUsernameParameter(upConfig.getUsernameParameterName());  // 用户名参数名(默认"username")
        filter.setAuthenticationManager(getSecurityManager().authenticationManager()); // 设置认证管理器
        
        // 5. 配置附加服务
        filter.setRememberMeServices(rms);  // 集成RememberMe功能
        GeoServerWebAuthenticationDetailsSource s = new GeoServerWebAuthenticationDetailsSource();
        filter.setAuthenticationDetailsSource(s);  // 自定义认证详情源(获取IP等信息)

        // 6. 会话固定攻击防护
        try {
            // Servlet 3.1+ 使用会话ID变更策略(更安全)
            filter.setSessionAuthenticationStrategy(new ChangeSessionIdAuthenticationStrategy());
        } catch (IllegalStateException e) {
            // 旧版Servlet使用会话迁移策略
            filter.setSessionAuthenticationStrategy(new SessionFixationProtectionStrategy());
        }
        filter.setAllowSessionCreation(false);  // 禁止随意创建会话

        // 7. 配置认证结果处理器
        // 7.1 认证成功处理器
        SimpleUrlAuthenticationSuccessHandler successHandler = 
            new SimpleUrlAuthenticationSuccessHandler();
        successHandler.setDefaultTargetUrl(URL_LOGIN_SUCCCESS);  // 登录成功重定向到主页
        filter.setAuthenticationSuccessHandler(successHandler);
        
        // 7.2 认证失败处理器
        SimpleUrlAuthenticationFailureHandler failureHandler = 
            new SimpleUrlAuthenticationFailureHandler();
        failureHandler.setDefaultFailureUrl(URL_LOGIN_FAILURE);  // 登录失败重定向回登录页(带错误标记)
        filter.setAuthenticationFailureHandler(failureHandler);

        // 8. 将配置好的过滤器添加到嵌套过滤器链
        getNestedFilters().add(filter);
    }

    /**
     * 获取认证入口点
     * 当用户访问受保护资源时,此入口点负责引导到登录页
     */
    @Override
    public AuthenticationEntryPoint getAuthenticationEntryPoint() {
        return aep;
    }

    /**
     * 过滤器核心执行方法
     * 将认证入口点存入请求属性,供后续流程使用
     */
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        // 在请求中存储认证入口点引用
        req.setAttribute(GeoServerSecurityFilter.AUTHENTICATION_ENTRY_POINT_HEADER, aep);
        // 继续执行过滤器链
        super.doFilter(req, res, chain);
    }

    /** 标识此过滤器适用于HTML界面 */
    @Override
    public boolean applicableForHtml() {
        return true;  // 处理Web界面认证
    }

    /** 标识此过滤器不适用于服务端点 */
    @Override
    public boolean applicableForServices() {
        return false;  // 不处理WFS/WMS等OGC服务认证
    }
}

创建工作空间

src/main/java/org/geoserver/web/data/workspace/WorkspaceNewPage.java

WorkspaceNewPage 类继承自 GeoServerSecuredPage,确保该页面需要经过权限验证才能访问。

用户点击“提交”按钮时触发 submitLink.onSubmit() 方法,调用 handleOnSubmit() 方法进行实际处理。

private void handleOnSubmit() {
    // Catalog 是 GeoServer 中管理所有数据(如工作区、命名空间等)的核心类。通过 getCatalog() 获取当前的目录实例,后续操作都依赖它。
    Catalog catalog = getCatalog();
    // 获取用户输入的工作区信息。model 是一个封装了用户表单数据的模型对象(CompoundPropertyModel<WorkspaceInfo>)。调用 getObject() 获取用户填写的工作区信息(如名称等)。
    WorkspaceInfo workspace = model.getObject();
    // 创建并设置命名空间信息 命名空间前缀(Prefix)使用工作区名称 命名空间 URI 从页面输入框中获取 是否隔离(Isolated)与工作区保持一致
    NamespaceInfo namespace = catalog.getFactory().createNamespace();
    namespace.setPrefix(workspace.getName());
    namespace.setURI(nsUriTextField.getDefaultModelObjectAsString());
    namespace.setIsolated(workspace.isIsolated());
    // 验证工作区和命名空间是否合法
    // 调用 catalog.validate(...) 检查工作区和命名空间是否符合规范(如名称是否合法、是否重复等)。使用 validateAndReport(...) 包装验证逻辑,自动显示错误提示。如果任一验证失败,终止流程并返回错误信息。
    /* validateAndReport(...) 方法说明:
    接收一个函数式参数(验证逻辑),执行校验。
    如果验证失败,记录日志并提示错误。
    返回布尔值表示验证是否通过。*/
    if (!validateAndReport(() -> catalog.validate(workspace, true))
            || !validateAndReport(() -> catalog.validate(namespace, true))) {
        // at least one validation fail
        return;
    }
    // 保存工作区和命名空间若添加过程中发生异常(例如数据库写入失败),调用 cleanAndReport(...) 清理已保存的数据以保证一致性。
    /*cleanAndReport(...) 方法说明:
    判断是否只保存了工作区而未保存命名空间。
    如果是,则删除已保存的工作区,确保两者要么都存在,要么都不存在。
    记录异常信息并提示用户。*/
    try {
        catalog.add(workspace);
        catalog.add(namespace);
    } catch (Exception exception) {
        LOGGER.log(Level.INFO, "Error storing workspace related objects.", exception);
        cleanAndReport(exception);
    }
    // 设置默认工作区(如果勾选) 如果用户勾选了“设为默认工作区”,则更新目录中的默认工作区配置。
    if (infoPanel.defaultWs) {
        catalog.setDefaultWorkspace(workspace);
    }
    //如果用户有权限并且打开了“Security”选项卡,调用 accessdataPanel.save() 保存访问控制规则。成功后跳转回工作区管理页面。如果保存失败,记录日志并提示错误信息。
    try {
        if (accessdataPanel != null) accessdataPanel.save();
        doReturn(WorkspacePage.class);
    } catch (IOException e) {
        LOGGER.log(Level.INFO, "Error saving access rules associated to workspace " + workspace.getName(), e);
        error(e.getMessage() == null ? e.toString() : e.getMessage());
    }
}

添加工作空间和命名空间的代码详解

@Override
public void add(WorkspaceInfo workspace) {
    //确保workspace对象不为null
    workspace = resolve(workspace);
    //校验workspace
    validate(workspace, true);
    // 检查是否已存在同名工作区
    if (getWorkspaceByName(workspace.getName()) != null) {
        throw new IllegalArgumentException("Workspace with name '" + workspace.getName() + "' already exists.");
    }
    // 调用 beforeadded(workspace) 方法,通知监听器即将添加一个工作区。
    beforeadded(workspace);
    // 获取当前默认工作区
    WorkspaceInfo defaultWorkspace = getDefaultWorkspace();
    WorkspaceInfo added;
        /*如果当前没有默认工作区:
    使用 synchronized 锁定 facade 对象,确保线程安全。
    调用 facade.add(workspace) 将工作区添加进目录。
    如果添加后仍然没有默认工作区,就将当前添加的工作区设为默认工作区:setDefaultWorkspace(workspace)
    如果已有默认工作区:
    直接调用 facade.add(workspace) 添加新工作区即可。*/
    if (defaultWorkspace == null) {
        synchronized (facade) {
            added = facade.add(workspace);
            // if there is no default workspace use this one as the default
            if (getDefaultWorkspace() == null) {
                setDefaultWorkspace(workspace);
            }
        }
    } else {
        added = facade.add(workspace);
    }
    added(added);
}

添加工作空间的具体底层实现方法

将一个 CatalogInfo 类型的对象(如 WorkspaceInfo, NamespaceInfo, LayerInfo 等)添加到内存索引中

public T add(T value) {
    //判断传入的 value 是否是一个 Java 动态代理对象(例如由 ModificationProxy.create(...) 创建的代理)。如果是,则通过反射获取其内部封装的真实对象(避免多次代理嵌套导致问题)。
    if (Proxy.isProxyClass(value.getClass())) {
        ModificationProxy h = (ModificationProxy) Proxy.getInvocationHandler(value);
        value = (T) h.getProxyObject();
    }
    // 获取名称映射表(nameMap)
    Map<Name, T> nameMap = getMapForValue(nameMultiMap, value);
    // 生成对象名称(Name)
    Name name = nameMapper.apply(value);
    // 获取 ID 映射表(idMap)
    Map<String, T> idMap = getMapForValue(idMultiMap, value);
    // 加锁写入数据(线程安全)
    writeLock.lock();
    try {
        nameMap.put(name, value);
        return idMap.put(value.getId(), value);
    } finally {
        writeLock.unlock();
    }
}

添加命名空间

public void add(NamespaceInfo namespace) {
    // 验证命名空间信息的有效性
    validate(namespace, true);

    NamespaceInfo added;
    // 解析命名空间信息,以处理可能的命名空间冲突或重复
    final NamespaceInfo resolved = resolve(namespace);
    // 在添加命名空间之前执行的预处理步骤
    beforeadded(namespace);

    // 获取当前的默认命名空间
    NamespaceInfo defaultNamespace = getDefaultNamespace();
    if (defaultNamespace == null) {
        // 如果当前没有默认命名空间,同步添加过程以避免并发问题
        synchronized (facade) {
            // 添加解析后的命名空间
            added = facade.add(resolved);
            // 如果系统中仍然没有默认命名空间,将新添加的命名空间设置为默认
            if (getDefaultNamespace() == null) {
                setDefaultNamespace(resolved);
            }
        }
    } else {
        // 如果已有默认命名空间,直接添加解析后的命名空间
        added = facade.add(resolved);
    }
    // 添加完成后执行的后处理步骤
    added(added);
}

命名空间添加底层代码

public NamespaceInfo add(NamespaceInfo value) {
    writeLock.lock();
    try {
        // 调用public T add(T value) 将传入的 NamespaceInfo 添加到其内部存储结构中
        NamespaceInfo ns = super.add(value);
        // 此方法用于更新当前类维护的 ConcurrentHashMap<String, List<NamespaceInfo>> index 索引。它会根据 NamespaceInfo 的 URI 将其加入对应的列表中,并保持列表按 ID 排序。
        addInternal(value);
        return ns;
    } finally {
        writeLock.unlock();
    }
}

存储仓库

Catalog

Catalog是 GeoServer 中用于访问元信息的核心接口,提供了对工作区(Workspace)、命名空间(Namespace)、样式(Style)、图层(Layer)、资源(Resource)等对象的统一访问方式。

关键方法:

add(StoreInfo store):添加一个存储仓库。

remove(StoreInfo store):移除一个存储仓库。

save(StoreInfo store):保存已修改的存储仓库。

getStore(String id, Class<T> clazz):根据 ID 获取特定类型的存储仓库。

getStoreByName(WorkspaceInfo workspace, String name, Class<T> clazz):根据名称和所属工作区获取特定类型的存储仓库。

getStoresByWorkspace(WorkspaceInfo workspace, Class<T> clazz):获取某个工作区下的所有指定类型的存储仓库。

CatalogFacade

CatalogFacade提供对持久化存储中 Catalog 对象的访问能力,封装了底层数据库或文件系统的具体实现细节。

关键方法:

add(StoreInfo store):向持久化存储中添加一个存储仓库。

remove(StoreInfo store):从持久化存储中移除一个存储仓库。

save(StoreInfo store):将对存储仓库的修改持久化。

getStore(String id, Class<T> clazz):根据 ID 和类型获取存储仓库。

getStoreByName(WorkspaceInfo workspace, String name, Class<T> clazz):根据工作区和名称获取存储仓库。

getStoresByWorkspace(WorkspaceInfo workspace, Class<T> clazz):获取某个工作区下的所有指定类型的存储仓库。

DefaultCatalogFacade

DefaultCatalogFacade是CatalogFacade 的默认实现类,基于内存中的结构进行管理

示例

添加存储仓库

catalog.add(store)

Catalog -> CatalogFacade -> DefaultCatalogFacade -> stores.add()

创建使用样式

创建样式页面代码地址/src/web/wms/src/main/java/org/geoserver/wms/web/data/StyleNewPage.java

用户提交后的执行代码

protected void onStyleFormSubmit() {
    // 获取目录和模型对象
    Catalog catalog = getCatalog();
    StyleInfo model = styleForm.getModelObject();
    // 使用目录工厂创建一个全新的 StyleInfo 对象,并通过 CatalogBuilder 把表单数据复制进去。
    StyleInfo s = catalog.getFactory().createStyle();
    CatalogBuilder builder = new CatalogBuilder(catalog);
    builder.updateStyle(s, model);

    StyleHandler styleHandler = styleHandler();

    // 写入 SLD 文件如果图例为空或没有在线资源地址,则将图例设为 null
    if (null == s.getLegend()
            || null == s.getLegend().getOnlineResource()
            || s.getLegend().getOnlineResource().isEmpty()) {
        s.setLegend(null);
    }

    // 写入 SLD 文件,如果文件名为空,则根据样式名称生成文件名(如 mystyle.sld),然后把用户输入的原始样式内容(rawStyle)写入资源池中。
    try {
        if (s.getFilename() == null) {
            // TODO: check that this does not override any existing files
            s.setFilename(s.getName() + "." + styleHandler.getFileExtension());
        }
        catalog.getResourcePool().writeStyle(s, new ByteArrayInputStream(rawStyle.getBytes()));
    } catch (IOException e) {
        throw new WicketRuntimeException(e);
    }

    // 获取样式版本号,设置格式版本,然后调用 catalog.add(s) 将样式正式添加进目录。成功后提示“Style saved”。
    try {
        Version version = styleHandler.version(rawStyle);
        s.setFormatVersion(version);
        catalog.add(s);
        styleForm.info("Style saved");

    } catch (Exception e) {
        LOGGER.log(Level.SEVERE, "Error occurred saving the style", e);
        error(e.getMessage());
    }
}

catalog.add(s)执行过程类似于前面创建工作空间,存储仓库的代码,都是基于内存中的结构进行管理,代码逻辑相似。

服务发布

OGC 服务是通过 Web 访问和作地理空间数据的标准化接口。GeoServer 实施以下主要 OGC 服务规范:

  1. Web Map Service (WMS) - 将地图渲染为图像(PNG、JPEG 等)

  2. Web Feature Service (WFS) - 检索和作矢量要素数据

  3. Web Coverage Service (WCS) - 提供对 coverage/栅格数据的访问

请求处理架构

所有 OGC 服务请求都遵循 GeoServer 中的通用模式。OWS Dispatcher 接收请求,OWS Dispatcher 确定服务类型和作,分析请求参数,并将请求路由到相应的服务实现。

主要组件与类说明

1. Dispatcher

位置: org.geoserver.ows.Dispatcher

作用:

OWS 请求的入口类

根据请求内容选择合适的服务实现类(Service)

调用对应的操作处理器(OperationHandler)

关键方法:

@Override
protected ModelAndView handleRequestInternal(HttpServletRequest httpRequest, HttpServletResponse httpResponse)
        throws Exception {
    // 对 HTTP 请求进行预处理,括设置编码
    preprocessRequest(httpRequest);

    // 初始化一个自定义的 Request 对象,用于封装整个请求过程中的上下文信息。
    Request request = new Request();

    // 将原始的 HTTP 请求和响应对象保存进 Request 中,便于后续使用。
    request.setHttpRequest(httpRequest);
    request.setHttpResponse(httpResponse);

    Service service = null;

    try {
        // 调用 init() 方法对请求进行进一步初始化
        /* 该函数 init 的主要功能是初始化一个 OWS 请求对象(Request),根据 HTTP 请求的方法和内容类型解析请求参数、处理不同类型的请求体(如 SOAP、multipart/form-data、XML POST),并设置相关字段以供后续处理使用。*/  
        request = init(request);

        // 使用 ThreadLocal 存储当前线程的请求上下文,避免多线程间冲突。
        REQUEST.set(request);

        // 根据请求参数查找对应的 Service 实现类(如 WMS/WFS/WCS 等)。如果找不到服务或出错,会触发异常处理逻辑。
        try {
            service = service(request);
        } catch (Throwable t) {
            exception(t, null, request);

            return null;
        }

        // 如果在初始化阶段就已经出现错误,直接抛出不再继续执行。
        if (request.getError() != null) {
            throw request.getError();
        }

        // 根据请求内容(如 GetFeature, GetMap)确定要执行的操作,并创建 Operation 对象。
        Operation operation = dispatch(request, service);
        request.setOperation(operation);
        // 如果是 SOAP 请求,做一些特殊标记或处理。
        if (request.isSOAP()) {
            flagAsSOAP(operation);
        }

        // 调用具体服务的操作处理器,执行业务逻辑(如查询数据库、生成 GML 数据等)。
        Object result = execute(request, operation);

        // 如果有返回结果,构建响应内容并写入 HTTP 响应流中。
        if (result != null) {
            response(result, request, operation);
        }
    } catch (Throwable t) {
        // 捕获所有异常,如果是安全相关的异常(如权限不足),重新抛出以便上层处理;否则调用 exception() 方法统一处理错误。
        if (isSecurityException(t)) throw (Exception) t;
        exception(t, service, request);
    } finally {
        // 触发完成后的回调(如日志记录、清理临时文件等)。移除线程局部变量中的请求对象,防止内存泄漏。
        fireFinishedCallback(request);
        REQUEST.remove();
    }

    return null;
}

2. Request

位置: org.geoserver.ows.Request

作用:

封装整个请求过程中的上下文信息

包括原始 HTTP 请求、解析后的 KVP、请求服务类型、输出格式等

重要字段:

private HttpServletRequest httpRequest;

private Map<String, Object> kvp; // 解析后的键值对参数

private BufferedReader input; // POST 请求体输入流

private String service; // 服务类型(wms/wfs/wcs)

private String request; // 操作名称(GetMap/GetFeature)

private String version; // 协议版本号(1.3.0 / 2.0.0)

private Operation operation; // 当前执行的操作对象

private Service serviceDescriptor; // 当前调用的服务描述

3. Operation

位置: org.geoserver.platform.Operation

作用:

表示一个完整的 OGC 操作(如 GetFeature)

封装了操作名、服务描述、请求对象、响应对象

4. Service

位置: org.geoserver.platform.Service

作用:

抽象表示某个服务的实现(如 WMS/WFS/WCS)

包含该服务支持的所有操作及其处理器

5. KvpParser

位置: org.geoserver.ows.KvpParser

作用:

将 KVP 参数字符串解析为 Java 对象

支持多种数据类型(String、Integer、Double、Enum、Time、Elevation 等)

6. XmlRequestReader

位置: org.geoserver.ows.XmlRequestReader

作用:

解析 XML 格式的请求体(如 SOAP 请求)

使用 SAX 或 DOM 解析器读取 XML 并生成 Java 对象

7. Response

位置: org.geoserver.ows.Response

作用:

封装响应数据

支持不同输出格式(GML、GeoJSON、CSV、HTML 等)

WCS服务

架构流程

公共模块 (wcs)

位于 src/wcs 目录下,包含多个 WCS 版本共享的基础类和接口。

核心类与功能:

1. WCSFactoryExtension.java

功能:用于扩展 WCS 的工厂类,支持插件机制动态创建不同类型的 WCS 实例。

使用场景:当需要根据配置或运行时条件生成特定实现时使用。

2. WCSInfo.java / WCSInfoImpl.java

功能:表示 WCS 服务的全局配置信息,如启用状态、支持的操作等。

结构:

定义了 WCS 服务的基本属性(如是否启用、支持的格式)。

提供持久化能力(通常由 Spring 配置管理)。

3. WCSCoverageClipCallback.java

功能:处理覆盖数据裁剪的回调逻辑。

场景:在响应客户端请求时,对输出图像进行边界裁剪(如子集裁剪)。

4. CoverageEncoder.java

功能:编码器基类,负责将 Coverage 数据转换为最终输出格式(如 GeoTIFF、PNG 等)。

子类包括:

GeoTIFFCoverageResponseDelegate

IMGCoverageResponseDelegate

5. CoverageResponseDelegate.java

功能:响应委托接口,定义如何将 Coverage 数据写入 HTTP 响应流中。

实现类:

GeoTIFFCoverageResponseDelegate:处理 GeoTIFF 格式输出。

IMGCoverageResponseDelegate:处理 PNG/JPG 等图像格式。

6. WCSXStreamLoader.java

功能:加载和保存 WCS 配置文件(如 XML),通常用于持久化存储服务配置。

使用方式:通过 XStream 序列化/反序列化对象模型。

7. WCSUtils.java

功能:工具类,提供一些通用方法(如坐标系处理、时间维度处理等)。

示例方法:CRS 转换、时间范围解析等。

8. WcsException.java

功能:异常类,封装 WCS 请求过程中可能抛出的错误信息(如参数非法、不支持的操作等)。

使用方式:统一抛出标准化错误码和描述,便于客户端识别。

WCS 1.0 模块 (wcs1_0)

位于 src/wcs1_0 目录下,实现了 OGC WCS 1.0.0 规范。

核心类与功能:

1. 请求解析 (kvp 包)

Wcs10KvpParser.java

功能:解析 KVP(Key-Value Pair)格式的请求参数。

子类包括:

BBoxKvpParser:解析 BBOX 参数。

TimeKvpParser:解析 TIME 维度参数。

ElevationKvpParser:解析 ELEVATION 维度参数。

InterpolationMethodKvpParser:解析 INTERPOLATION 方法。

Wcs10GetCoverageRequestReader.java

功能:读取并构建 GetCoverage 请求对象,调用相应的解析器处理参数。

输出:构造完整的 GetCoverageRequest 对象供后续处理。

2. 响应处理 (response 包)

Wcs10GetCoverageResponse.java

功能:处理 GetCoverage 请求,生成对应格式的响应(如 GeoTIFF)。

流程:

获取 Coverage 数据

调用 CoverageEncoder 编码器完成数据输出

Wcs10DescribeCoverageResponse.java

功能:返回 Coverage 描述信息(如元数据、维度范围等)。

Wcs10CapsTransformer.java

功能:将服务能力描述(Capabilities)转换为 XML 输出。

3. XML 处理 (xml/v1_0_0 包)

WcsXmlReader.java

功能:XML 请求解析器,用于处理 XML 格式的请求(如 DescribeCoverage)。

WCSParserDelegate.java

功能:解析器委托类,协调不同 XML 元素的解析逻辑。

4. 服务类

DefaultWebCoverageService100.java

功能:WCS 1.0 的默认实现类,处理所有操作(GetCapabilities、GetCoverage、DescribeCoverage)。

实现接口:WebCoverageService100

WebCoverageService100.java

功能:接口,定义 WCS 1.0 所需的所有操作(GetCapabilities、GetCoverage、DescribeCoverage)。

WCS10WorkspaceQualifier.java

功能:限定工作区逻辑,决定某个 Coverage 是否属于当前请求的工作空间。

WCS10ServiceExceptionHandler.java

功能:异常处理器,统一处理 WCS 1.0 请求中的异常并返回标准错误信息。

WCS 1.1 模块 (wcs1_1)

位于 src/wcs1_1 目录下,实现了 OGC WCS 1.1.1 规范。

核心类与功能:

1. 请求解析 (kvp 包)

BoundingBoxKvpParser.java

功能:解析 bbox 参数,支持更复杂的子集裁剪(如多边形区域)。

RangeSubsetKvpParser.java

功能:解析 range-subset 参数,用于选择波段或变量子集。

2. 响应处理 (response 包)

3. XML 处理 (xml 包)

DescribeCoverageXmlParserTest.java(略去测试部分)

功能:测试类,验证 XML 格式的 DescribeCoverage 请求解析逻辑。

4. 服务类

WebCoverageService111.java

功能:WCS 1.1 的主服务接口,定义所有操作(GetCapabilities、GetCoverage、DescribeCoverage、GetCoverageMultipart 等)。

DefaultWebCoverageService111.java

功能:WCS 1.1 的默认实现类,继承并实现 WebCoverageService111 接口。

支持特性:

多维 Coverage

分片响应(multipart response)

WCS11ServiceExceptionHandler.java

功能:异常处理类,统一处理 WCS 1.1 请求的错误。

WCS11WorkspaceQualifier.java

功能:工作区限定逻辑,确保访问权限控制。

WCS 2.0 模块 (wcs2_0)

位于 src/wcs2_0 目录下,实现了 OGC WCS 2.0 Core 及扩展规范。

核心类与功能:

1. 请求处理 (kvp 包)

CRSExtentionKVPTest.java(略去测试部分)

功能:测试 CRS 扩展参数的解析。

ScaleKvpTest.java(略去测试部分)

功能:测试 Scale 参数的解析逻辑。

SubsetKvpParserTest.java(略去测试部分)

功能:测试 Subset 参数的解析过程。

2. XML 请求处理 (xml 包)

CRSExtentionTest.java(略去测试部分)

功能:测试 XML 格式的 CRS 扩展参数处理。

ScalingExtentionTest.java(略去测试部分)

功能:测试 XML 格式的 Scaling 扩展参数处理。

RangeSubsetExtentionTest.java(略去测试部分)

功能:测试 XML 格式的 RangeSubset 扩展参数处理。

3. 响应处理 (response 包)

4. 服务类

DefaultWebCoverageService20.java

功能:WCS 2.0 的默认实现类,处理所有操作(GetCapabilities、GetCoverage、DescribeCoverage)。

新增特性:

支持分片(Trimming)

支持波段子集(RangeSubset)

支持缩放(Scaling)

WCS20ServiceExceptionHandler.java

功能:异常处理类,处理 WCS 2.0 请求的错误。

WCS20WorkspaceQualifier.java

功能:工作区限定类,控制 Coverage 访问权限。

WMS 服务

服务架构

1. 核心类与接口

WMS 类:

主要职责:作为 WMS 服务的入口点,提供获取样式、生成地图等功能。

GetMapRequest 类:

主要职责:封装 WMS GetMap 请求的所有参数。

关键属性:bbox, layers, styles, time, elevation.

GetMap 类:

主要职责:根据 GetMapRequest 生成地图内容。

示例方法:run, executeInternal, addLayer.

GetMapKvpRequestReader 类:

主要职责:解析 KVP 格式的请求参数,生成 GetMapRequest.

示例方法:read, parseCRS, processSLDBody.

2. 请求处理流程

请求解析:

使用 GetMapKvpRequestReader 解析 HTTP 请求中的 KVP 参数。

构建 GetMapRequest 对象,包含所有必要的参数如 bbox, layers, styles.

地图生成:

调用 GetMap 类的 run 方法,传入 GetMapRequest.

创建 WMSMapContent 对象,设置视图范围、背景色等。

添加图层(矢量、栅格、WMS/WMTS)到地图内容中。

响应生成:

使用 GetMapOutputFormat 生成最终的地图图像。

设置 HTTP 响应头,返回图像数据。

3. 样式处理

本地样式:

通过 getStyleByName 获取本地存储的样式。

应用于指定图层。

远程样式:

支持通过 STYLE_URL 或 STYLE_BODY 指定远程样式。

使用 SLDXmlRequestReader 解析 SLD 文档。

动态样式:

支持运行时动态生成样式。

通过 DynamicStyling 配置控制是否启用。

4. 缓存与性能优化

HTTP 缓存:

使用 CachingHttpClientBuilder 构建支持缓存的 HTTP 客户端。

配置最大缓存条目数和单个条目大小。

连接池:

使用 HttpClientConnectionManager 管理 HTTP 连接池,提高并发性能。

5. 扩展机制

回调接口:

GetMapCallback 允许插件在地图生成前后执行自定义逻辑。

插件支持:

通过 GeoServerExtensions 加载和管理插件。

请求处理流程

WFS 服务

服务流程

核心接口与实现类

a. WebFeatureService

作用:定义 WFS 所有标准操作的接口。

关键方法:

  • getCapabilities: 获取服务能力文档。

  • describeFeatureType: 返回要素类型定义。

  • getFeature: 查询并返回要素集合。

  • transaction: 处理事务操作。

  • lockFeature: 锁定要素资源。

b. DefaultWebFeatureService

继承关系:实现 WebFeatureService 接口,并集成 Spring 上下文支持。

构造函数:接收 GeoServer 实例,初始化 Catalog。

方法实现:调用对应的操作类执行具体业务逻辑。

核心操作类

a. GetFeature

作用:执行 WFS GetFeature 请求,生成要素集合响应。

流程:

解析请求参数(如 bbox、filter、typeName)。

构建查询语句。

调用 DataStore 获取数据。

返回 FeatureCollectionResponse。

b. DescribeFeatureType

作用:根据请求中的 typeName 获取要素类型描述信息(Schema)。

流程:

解析 typeName。

从 Catalog 中获取 FeatureTypeInfo。

返回 XML 或 JSON 格式的 Schema。

c. Transaction

作用:处理 WFS Transaction 请求,执行 insert、update、delete 操作。

流程:

解析 Transaction 操作列表。

针对每个操作调用对应的处理器(InsertElementHandler、UpdateElementHandler 等)。

提交事务并返回结果。

存储查询(Stored Query)

a. StoredQuery

作用:支持预定义的查询模板,可复用。

核心方法:

compile():将查询表达式中的参数替换为实际值,并解析为 Query 对象。

validate():验证查询中引用的类型名是否在返回类型列表中。

b. StoredQueryProvider

作用:管理 StoredQuery 的注册与查找。

命名空间与 XML 解析支持

a. CatalogNamespaceSupport

作用:支持从 Catalog 中动态获取命名空间映射。

使用场景:XML 解析时自动识别命名空间前缀。

b. QNameKvpParser

作用:将 KVP 参数中的 QName 字符串解析为 QName 对象

配置与上下文支持

a. WFSInfo

作用:表示 WFS 服务的全局配置(版本、输出格式、最大要素数等)。

来源:通过 GeoServer.getService(WFSInfo.class) 获取。

b. ApplicationContextAware

作用:Spring 注入上下文,用于加载插件、监听器等扩展组件。