Class HttpServletAuthService

java.lang.Object
tech.riemann.nif.auth.service.HttpServletAuthService
All Implemented Interfaces:
AuthService
Direct Known Subclasses:
DefaultAuthService

public abstract class HttpServletAuthService extends Object implements AuthService
基于HttpServlet的认证服务抽象类

实现了AuthService接口,提供了基于HTTP Servlet的认证服务基础实现

主要功能: 1. 从HTTP请求中获取Token(支持Header、Cookie、Parameter多种方式) 2. 用户登录/登出(通过Cookie管理Token) 3. Token刷新机制(自动使用Refresh Token刷新Access Token) 4. URL认证检查(支持白名单配置)

Token获取优先级: 1. Cookie中的Token 2. Authorization Header 3. 自定义Header 4. 请求参数(H10:默认禁用,防 Token 泄露进浏览器历史/访问日志/Referer;需要时子类覆写 allowQueryToken() 返回 true 或通过配置 nif.auth.allow-query-token 开启)

子类需要实现的抽象方法: - getRequest(): 获取HttpServletRequest - getResponse(): 获取HttpServletResponse - userDetailService(): 获取UserDetailService实例 - jwtEncoder(): 获取JWTEncoder实例 - jwtDecoder(): 获取JWTDecoder实例

Author:
Kerbores(kerbores@riemann.tech)
  • Field Details

  • Constructor Details

    • HttpServletAuthService

      public HttpServletAuthService()
  • Method Details

    • getRequest

      public abstract jakarta.servlet.http.HttpServletRequest getRequest()
      获取HttpServletRequest对象

      子类必须实现此方法以提供HTTP请求上下文

      Returns:
      HttpServletRequest实例
    • getResponse

      public abstract jakarta.servlet.http.HttpServletResponse getResponse()
      获取HttpServletResponse对象

      子类必须实现此方法以提供HTTP响应上下文,用于设置Cookie

      Returns:
      HttpServletResponse实例
    • userDetailService

      public abstract UserDetailService userDetailService()
      获取用户详情服务

      子类必须实现此方法以提供用户详情服务实例

      Returns:
      UserDetailService实例
    • jwtEncoder

      public abstract JWTEncoder jwtEncoder()
      获取JWT编码器

      子类必须实现此方法以提供JWT编码器实例

      Returns:
      JWTEncoder实例
    • jwtDecoder

      public abstract JWTDecoder jwtDecoder()
      获取JWT解码器

      子类必须实现此方法以提供JWT解码器实例

      Returns:
      JWTDecoder实例
    • cookieSettings

      public CookieSettings cookieSettings()
      获取Cookie配置

      默认返回空的Cookie配置,子类可以重写以自定义配置

      Returns:
      CookieSettings实例
    • tokenKey

      public String tokenKey()
      获取Token的键名

      可以从Header、Cookie或请求参数中获取,此方法返回键名 默认使用Cookie配置中的Token Cookie名称

      Returns:
      Token键名
    • refreshTokenKey

      public String refreshTokenKey()
      获取Refresh Token的键名

      可以从Header、Cookie或请求参数中获取,此方法返回键名 默认使用Cookie配置中的Refresh Token Cookie名称

      Returns:
      Refresh Token键名
    • token

      public String token()
      获取当前用户的访问令牌(Token)

      按优先级从不同位置获取Token: 1. Cookie中的Token 2. Authorization Header 3. 自定义Header(使用tokenKey) 4. 请求参数(H10:默认禁用,需子类覆写 allowQueryToken() 返回 true 才启用)

      Specified by:
      token in interface AuthService
      Returns:
      Token字符串,如果未找到则返回空字符串
    • refreshToken

      public String refreshToken()
      获取当前用户的刷新令牌(Refresh Token)

      按优先级从不同位置获取Refresh Token: 1. Cookie中的Refresh Token 2. Header中的Refresh Token 3. 请求参数中的Refresh Token(H10:默认禁用,需覆写 allowQueryToken() 返回 true 才启用)

      Specified by:
      refreshToken in interface AuthService
      Returns:
      Refresh Token字符串,如果未找到则返回空字符串
    • allowQueryToken

      protected boolean allowQueryToken()
      是否允许从 URL 查询参数中获取 Token(H10)

      默认返回 false(禁止)——Token 走 query 会进入浏览器历史、访问日志、Referer,泄露面大。 确需支持(如旧客户端/文件下载场景)时,子类覆写返回 true;框架默认安全。

      Returns:
      true 表示允许从 query 参数取 Token,默认 false
    • tokenOrRefreshToken

      public String tokenOrRefreshToken()
      获取有效的Token(优先Access Token,否则使用Refresh Token)

      先检查Access Token是否有效,如果无效则尝试使用Refresh Token

      Returns:
      有效的Token字符串
    • subject

      public String subject()
      获取当前会话的Subject(主题)

      从tokenOrRefreshToken()中解析Subject信息 如果解析失败,返回空字符串

      M9: 仅解析验签通过的 token——tokenOrRefreshToken() 在 access token 失效时回退返回 refresh token(未验签),未验签 token 的 subject 不可信,直接返回空串

      Specified by:
      subject in interface AuthService
      Returns:
      Subject字符串,解析失败时返回空字符串
    • token

      public String token(String subject)
      为指定用户生成访问令牌
      Specified by:
      token in interface AuthService
      Parameters:
      subject - 用户标识
      Returns:
      生成的访问令牌
    • refreshToken

      public String refreshToken(String subject)
      为指定用户生成刷新令牌
      Specified by:
      refreshToken in interface AuthService
      Parameters:
      subject - 用户标识
      Returns:
      生成的刷新令牌
    • login

      public AuthUser login(AuthUser user)
      用户登录

      执行登录操作: 1. 将Token和Refresh Token写入Cookie 2. 通过UserDetailService保存用户信息

      Specified by:
      login in interface AuthService
      Parameters:
      user - 用户信息对象(包含Token和Refresh Token)
      Returns:
      登录后的用户信息
    • logout

      public void logout()
      用户登出

      清除Cookie中的Token和Refresh Token,使其失效

      Specified by:
      logout in interface AuthService
    • user

      public AuthUser user()
      获取当前登录用户

      执行流程: 1. 如果Token和Refresh Token都为空,返回匿名用户 2. 如果Access Token有效,直接使用它获取用户信息 3. 否则尝试使用Refresh Token刷新

      Specified by:
      user in interface AuthService
      Returns:
      当前登录用户,未登录时返回匿名用户
    • tryRefreshToken

      public AuthUser tryRefreshToken(String refreshToken)
      尝试使用Refresh Token刷新Access Token

      执行流程: 1. 验证Refresh Token是否有效 2. 通过UserDetailService获取用户信息(同时会生成新的Token) 3. 将新的Token写入Cookie

      Parameters:
      refreshToken - Refresh Token字符串
      Returns:
      刷新后的用户信息,如果刷新失败返回匿名用户
    • addCookie

      public void addCookie(String name, String value, int age)
      添加Cookie

      使用配置的Cookie设置(secure、httpOnly、path、domain)添加Cookie

      Parameters:
      name - Cookie名称
      value - Cookie值
      age - Cookie有效期(分钟)
    • authentication

      public boolean authentication(List<String> withoutAuthenticationUrlRegulars)
      URL认证检查

      检查当前请求是否通过认证,判断逻辑: 1. 如果skip()返回true,直接通过 2. 如果URL匹配白名单Ant路径模式,直接通过 3. 否则检查 access token 是否有效(只读判定,不触发 token 刷新、不写响应 Cookie)

      P1-8: 认证检查为只读语义——不再经由 user() 的惰性刷新路径(那会静默写 Set-Cookie), access token 过期即判定未认证;刷新由显式调用 user() / tryRefreshToken(String) 触发

      Specified by:
      authentication in interface AuthService
      Parameters:
      withoutAuthenticationUrlRegulars - 不需要检查的URL(Ant路径模式)列表
      Returns:
      认证检查通过状态
    • pass

      public boolean pass(String uri, List<String> withoutAuthenticationUrlRegulars)
      检查URL是否匹配白名单路径模式

      先将原始 URI 归一化(normalizeUri(String)),再遍历所有模式做 Ant 匹配; 归一化失败(含百分号编码或穿越残留)一律返回 false——白名单语义为"无法安全判定即不放行"。

      P0-2: 原实现直接对 getRequestURI() 的原始串做 Ant 匹配,未消解 ../、 未剥离 matrix 参数、未拒绝百分号编码,攻击者可构造 /public/../admin/users 或 /public/health;jsessionid=x/../admin/users 使鉴权看到的路径与容器实际路由的路径不一致,从而绕过白名单。

      M8: 使用 Ant 路径模式(Spring 语义)——/api/** 匹配任意层级子路径, /health 精确匹配;原正则写法(如 /swagger-resources.*)请改为 Ant

      Parameters:
      uri - 请求地址
      withoutAuthenticationUrlRegulars - 放行URL模式列表
      Returns:
      是否放行
    • skip

      public boolean skip()
      是否跳过认证检查

      默认返回false,子类可以重写以实现自定义跳过逻辑

      Returns:
      true表示跳过检查,false表示不跳过
    • authorizationHeaderKey

      public String authorizationHeaderKey()
      获取认证Header的Key

      默认返回HttpHeaders.AUTHORIZATION("Authorization") 子类可以重写以使用自定义的Header名称

      Returns:
      认证Header的Key
    • optionalUser

      public Optional<AuthUser> optionalUser()
      尝试获取当前认证用户

      返回Optional包装的用户对象,如果用户为null则返回空Optional

      Specified by:
      optionalUser in interface AuthService
      Returns:
      包含当前用户信息的Optional