Skip to content

接口请求流程

每个接口请求时,请求头中都要携带封装了用户身份的token请求头,请求通过网关路由到后端具体的服务,并在网关的系统会将请求头中的token信息解析成用户信息(userId, orgId, name, account 等信息),再次封装到请求头中。

  1. 大致流程:
shell
"acuity-web-pro"->"acuity-gateway-server": 1 使用账号密码登录
"acuity-gateway-server"->"acuity-oauth-server": 2 路由请求到认证服务,验证账号密码
"acuity-oauth-server" --> "acuity-web-pro": 3 返回Token、用户基本信息
"acuity-web-pro"->"acuity-gateway-server":  4 请求头中携带Token、TenantId、ApplicationId、Authorization发起任意请求
Note Right of "acuity-gateway-server":  5 gateway将请求头中参数封装到请求头 
"acuity-gateway-server"-->"acuity-web-pro": 5.1 验证失败,直接返回无权限
"acuity-gateway-server"->"acuity-base-server": 5.2 验证通过,路由请求到后端服务
Note Right of "acuity-base-server":  后端拦截器解析请求头中的用户信息,封装到ThreadLocal
"acuity-base-server"-->"acuity-web-pro": 6 返回数据
"acuity-web-pro"->"acuity-gateway-server": 1 使用账号密码登录
"acuity-gateway-server"->"acuity-oauth-server": 2 路由请求到认证服务,验证账号密码
"acuity-oauth-server" --> "acuity-web-pro": 3 返回Token、用户基本信息
"acuity-web-pro"->"acuity-gateway-server":  4 请求头中携带Token、TenantId、ApplicationId、Authorization发起任意请求
Note Right of "acuity-gateway-server":  5 gateway将请求头中参数封装到请求头 
"acuity-gateway-server"-->"acuity-web-pro": 5.1 验证失败,直接返回无权限
"acuity-gateway-server"->"acuity-base-server": 5.2 验证通过,路由请求到后端服务
Note Right of "acuity-base-server":  后端拦截器解析请求头中的用户信息,封装到ThreadLocal
"acuity-base-server"-->"acuity-web-pro": 6 返回数据
  1. 详细流程:

解释

通过上图可以知道,正常流程中,调用后端请求时,需要在请求头中携带Token、TenantId、ApplicationId、Authorization等参数,在网关解析并验证Token、TenantId后,将UserId、TenantId、EmployeeId、ApplicationId等基础信息封装到请求头中,转发请求到具体的 业务服务中,每个业务服务都有一个上下文拦截器HeaderThreadLocalInterceptor, 用于将请求头中的UserId、TenantId、EmployeeId、ApplicationId等信息封装到ContextUtil中。 这样,当请求到达Controller->Service->Mapper 层时,程序就能通过ContextUtil获取当前登录人的信息和租户编码用于切换数据源和业务处理了。

由上可知,默认情况下,每个请求默认都会携带Token、TenantId、ApplicationId、Authorization4个请求头,他们的用途如下:

  1. Token:当前请求来自那个用户(userId)、员工(employeeId)

    主要用途:封装用户信息,鉴别请求是谁发起的,请求头中携带了Token参数,才能进行URI鉴权

    Token设置了8小时的有效期,有效期结束表示用户登录失效。

  2. TenantId:当前请求来自那个租户ID

    主要用途:用于切换数据源。请求头中携带了TenantId参数,才能操作租户库URI鉴权

  3. ApplicationId:当前请求来自那个应用ID

    主要用途:请求头中携带了ApplicationId参数,才能进行应用鉴权URI鉴权

  4. Authorization:当前请求来自那个客户端

如何让请求不携带租户ID(TanantId) ?

注意

配置acuity.ignore.anyTenant后,该请求就不需要携带 TanantIdToken,后台接口不能操作 租户数据库,不能获取租户信息,用户信息,不能校验URI权限

  • 在 acuity-gateway-server.yml 中配置:
yaml
acuity:
 ignore:
   anyTenant:   
     ALL:   # ALL 表示任意的请求类型
       - /**/anyTenant/**  
     POST:  # post 类型的请求
       - /xxx/save
     GET:
     	- /xxx/getById
     PUT:
       - /xxx/update
     DELETE:
     	- /xxx/delete
acuity:
 ignore:
   anyTenant:   
     ALL:   # ALL 表示任意的请求类型
       - /**/anyTenant/**  
     POST:  # post 类型的请求
       - /xxx/save
     GET:
     	- /xxx/getById
     PUT:
       - /xxx/update
     DELETE:
     	- /xxx/delete

路径匹配规则是依照 AntPathMatcher 的来的

(1)? 匹配一个字符 (2)* 匹配0个或多个字符 (3)**匹配0个或多个目录

  • 调试 TokenContextFilter 过滤器, 配置了的地址只要使得 isIgnoreTenant 方法放回true, 请求就可以不携带租户信息。
java
 private void parseTenant(ServerHttpRequest request, ServerHttpRequest.Builder mutate) {
   // 使请求忽略验证请求中的 租户ID (TenantId) 参数
   if (isIgnoreTenant(request)) {
     return;
   }
   String tenant = getHeader(TENANT_ID_KEY, request);
   if (StrUtil.isNotEmpty(tenant)) {
     ContextUtil.setTenantId(tenant);
     addHeader(mutate, TENANT_ID_HEADER, ContextUtil.getTenantId());
     MDC.put(TENANT_ID_HEADER, tenant);
   }
 }
 private void parseTenant(ServerHttpRequest request, ServerHttpRequest.Builder mutate) {
   // 使请求忽略验证请求中的 租户ID (TenantId) 参数
   if (isIgnoreTenant(request)) {
     return;
   }
   String tenant = getHeader(TENANT_ID_KEY, request);
   if (StrUtil.isNotEmpty(tenant)) {
     ContextUtil.setTenantId(tenant);
     addHeader(mutate, TENANT_ID_HEADER, ContextUtil.getTenantId());
     MDC.put(TENANT_ID_HEADER, tenant);
   }
 }

如何让请求不携带用户凭据(Token) ?

注意

配置后,需要携带租户ID,但不需要携带Token(不需要登录) 且不需要校验权限。 即: 请求头中携带 TenantId, 但不携带 Token

  • 在 acuity-gateway-server.yml 中配置:
yaml
acuity:
  ignore:
    anyUser:       
      ALL:   # ALL 表示任意的请求类型
        - /**/anyUser/**  
      POST:  # post 类型的请求
        - /xxx/save
      GET:
      	- /xxx/getById
      PUT:
        - /xxx/update
      DELETE:
      	- /xxx/delete
acuity:
  ignore:
    anyUser:       
      ALL:   # ALL 表示任意的请求类型
        - /**/anyUser/**  
      POST:  # post 类型的请求
        - /xxx/save
      GET:
      	- /xxx/getById
      PUT:
        - /xxx/update
      DELETE:
      	- /xxx/delete
  • 实现方式:
java
public class TokenContextFilter implements WebFilter, Ordered {
    private Mono<Void> parseToken(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        ServerHttpRequest.Builder mutate = request.mutate();
        
      	// 判断接口是否需要忽略token验证
        if (isIgnoreToken(request.getPath().toString())) {
            log.debug("当前接口:{}, 不解析用户token", request.getPath().toString());
            return chain.filter(exchange);
        }
        // 后面的代码省略
    }
}
public class TokenContextFilter implements WebFilter, Ordered {
    private Mono<Void> parseToken(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        ServerHttpRequest.Builder mutate = request.mutate();
        
      	// 判断接口是否需要忽略token验证
        if (isIgnoreToken(request.getPath().toString())) {
            log.debug("当前接口:{}, 不解析用户token", request.getPath().toString());
            return chain.filter(exchange);
        }
        // 后面的代码省略
    }
}

如何让请求不验证 URI权限 和 应用访问权限

  • 全局禁用, 修改 acuity-gateway-server.yml : (禁用后,整个系统所有方法,都不需要校验URI权限)
yaml
acuity:
  ignore:
    # 是否启用网关的 uri权限鉴权 和 前端按钮权限 (设置为false,则不校验访问权限) 
    authEnabled: false
acuity:
  ignore:
    # 是否启用网关的 uri权限鉴权 和 前端按钮权限 (设置为false,则不校验访问权限) 
    authEnabled: false
  • 禁用指定URI接口地址: (禁用后,能匹配的URI,都不需要校验URI权限)
yaml
acuity:
  ignore:
    anyone:       # 需要携带TenantId和Token,但无需验证是否拥有 uri 权限的接口。 即: 请求头中携带 tenant,也携带 token, 但不对uri权限验证
      ALL:
        - /**/anyone/**
      POST:  # post 类型的请求
        - /xxx/save
      GET:
      	- /xxx/getById
      PUT:
        - /xxx/update
      DELETE:
      	- /xxx/delete
acuity:
  ignore:
    anyone:       # 需要携带TenantId和Token,但无需验证是否拥有 uri 权限的接口。 即: 请求头中携带 tenant,也携带 token, 但不对uri权限验证
      ALL:
        - /**/anyone/**
      POST:  # post 类型的请求
        - /xxx/save
      GET:
      	- /xxx/getById
      PUT:
        - /xxx/update
      DELETE:
      	- /xxx/delete

如何禁用前端的按钮权限

前端的按钮权限数据是由后端返回的,只要后端接口返回false,前端就不在校验按钮权限。

java
@GetMapping("/visible/resource")
public R<VisibleResourceVO> visible(@ApiIgnore @LoginUser SysUser sysUser,
@RequestParam(value = "employeeId", required = false) Long employeeId,
@RequestParam Long applicationId) {
  if (employeeId == null || employeeId <= 0) {
  	employeeId = sysUser.getEmployeeId();
  }
  return R.success(VisibleResourceVO.builder()
    .roleList(Arrays.asList("PT_ADMIN"))
    // 查询员工拥有的所有资源编码
    .resourceList(oauthResourceBiz.findVisibleResource(employeeId, applicationId))
    // 资源编码是否区分大小写               
    .caseSensitive(ignoreProperties.getCaseSensitive())
    // 是否启用按钮权限
    .enabled(ignoreProperties.getAuthEnabled())
  .build());
}
@GetMapping("/visible/resource")
public R<VisibleResourceVO> visible(@ApiIgnore @LoginUser SysUser sysUser,
@RequestParam(value = "employeeId", required = false) Long employeeId,
@RequestParam Long applicationId) {
  if (employeeId == null || employeeId <= 0) {
  	employeeId = sysUser.getEmployeeId();
  }
  return R.success(VisibleResourceVO.builder()
    .roleList(Arrays.asList("PT_ADMIN"))
    // 查询员工拥有的所有资源编码
    .resourceList(oauthResourceBiz.findVisibleResource(employeeId, applicationId))
    // 资源编码是否区分大小写               
    .caseSensitive(ignoreProperties.getCaseSensitive())
    // 是否启用按钮权限
    .enabled(ignoreProperties.getAuthEnabled())
  .build());
}
  • 修改配置
yaml
acuity:
  ignore:
    # 是否启用网关的 uri权限鉴权 和 前端按钮权限 (设置为false,则不校验访问权限)
    authEnabled: false
acuity:
  ignore:
    # 是否启用网关的 uri权限鉴权 和 前端按钮权限 (设置为false,则不校验访问权限)
    authEnabled: false

欢迎使用天源云Saas快速开发系统