weblog/doc/7、Gateway 网关搭建与接口鉴权/7.6 优化:网关登录校验提示信息.md
2025-02-17 11:57:55 +08:00

7.0 KiB
Raw Blame History

上小节 中,我们为 Gateway 网关添加了全局异常处理器,实现了接口出参格式的统一。但是,依然还存在一个小问题 —— 提示信息不够友好

登录校验不通过,提示不友好问题

接下来,演示一下登录校验不通过,提示不友好的问题。当我们调用登出接口,并且未携带 Token 令牌,如下图所示:

可以看到,提示信息为权限不足。这个提示信息不够友好,应该提示请先登录,或者未携带 Token 令牌之类,这个提示,会让调用者一脸闷逼状态~

同时,观察网关服务的控制台日志,如下所示,从全局异常处理器打印的异常信息分析,明显是检测到了未提交 Token 令牌的问题,只不过需要对 SaTokenException 异常进行细化处理,而不是统一都返回权限不足提示:

SaToken 异常

在 SaToken 框架中,SaTokenException 是一个非常核心的异常,诸如未登录异常、权限不足异常、不具备对应角色异常,均继承于它:

-- SaTokenException
    -- NotLoginException // 未登录异常
    -- NotPermissionException // 权限不足异常
    -- NotRoleException // 不具备对应角色异常
    -- ...

抛出异常

在上小节中,我们在 SaToken 配置类中,将 .setError() 方法删除掉了此时当登录校验不通过、权限校验不通过等情况SaToken 都会统一抛出一个 SaTokenException 父异常,这会导致全局异常处理器无法判断具体的异常类型:

再次将 .setError() 方法添加上,并抛出具体异常类型,代码如下:

package com.quanxiaoha.xiaohashu.gateway.auth;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.reactor.filter.SaReactorFilter;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author: 犬小哈
 * @date: 2024/6/13 14:48
 * @version: v1.0.0
 * @description: [Sa-Token 权限认证] 配置类
 **/
@Configuration
public class SaTokenConfigure {
    // 注册 Sa-Token全局过滤器
    @Bean
    public SaReactorFilter getSaReactorFilter() {
        return new SaReactorFilter()
                // 拦截地址
                .addInclude("/**")    /* 拦截全部path */
                // 鉴权方法:每次访问进入
                .setAuth(obj -> {
                    // 省略..
                })
                // 异常处理方法每次setAuth函数出现异常时进入
                .setError(e -> {
                    // return SaResult.error(e.getMessage());
                    // 手动抛出异常,抛给全局异常处理器
                    if (e instanceof NotLoginException) { // 未登录异常
                        throw new NotLoginException(e.getMessage(), null, null);
                    } else if (e instanceof NotPermissionException || e instanceof NotRoleException) { // 权限不足,或不具备角色,统一抛出权限不足异常
                        throw new NotPermissionException(e.getMessage());
                    } else { // 其他异常,则抛出一个运行时异常
                        throw new RuntimeException(e.getMessage());
                    }
                })
                ;
    }
}

解释一下 setError() 方法中的逻辑:

  • NotLoginException 当用户未登录时,手动抛出 NotLoginException 异常;
  • NotPermissionExceptionNotRoleException : 权限不足,或不具备对应角色,统一抛出权限不足异常;
  • 其他异常,则手动抛出一个运行时异常;

异常处理

接着,编辑 GlobalExceptionHandler 全局异常处理器,对不同的异常类型,返回不同的错误提示,代码如下:

		// 省略...

        // 根据捕获的异常类型,设置不同的响应状态码和响应消息
        if (ex instanceof NotLoginException) { // 未登录异常
            // 设置 401 状态码
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            // 构建响应结果
            result = Response.fail(ResponseCodeEnum.UNAUTHORIZED.getErrorCode(), "未携带 Token 令牌");
        } else if (ex instanceof NotPermissionException) { // 无权限异常
            // 权限认证失败时,设置 401 状态码
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            // 构建响应结果
            result = Response.fail(ResponseCodeEnum.UNAUTHORIZED.getErrorCode(), ResponseCodeEnum.UNAUTHORIZED.getErrorMessage());
        } else { // 其他异常,则统一提示 “系统繁忙” 错误
            result = Response.fail(ResponseCodeEnum.SYSTEM_ERROR);
        }

		// 省略...

测试一波

登录校验不通过

编码完成后,重启网关服务。测试一波登出接口,先在请求头中不添加 Token 令牌,如下图所示,可以看到这次提示信息就正常了:未携带 Token 令牌

权限校验不通过

再来测试一下权限校验不通过的情况,由于已经配置了登出接口必须具有 admin 角色才能请求,而目前登录的用户只具备普通角色:

携带上 Token 令牌请求接口,效果如下:

成功提示权限不足。至此网关服务的认证鉴权提示就相对比较友好了后续如果还有调整的地方我们再来改。OK, 本小节优化完毕~

补充:令牌过期提示问题

2024.6.22 补充:测试的时候,漏掉了令牌过期的问题,当令牌过期时,提示如下:

提示信息依然还有问题当令牌失效时SaToken 同样会抛出 NotLoginException,如下图所示:

这就不能写死提示为:未携带令牌了,干脆直接用异常本身的错误信息,修改 GlobalExceptionHandler 全局异常处理器如下:

		if (ex instanceof NotLoginException) { // 未登录异常
            // 设置 401 状态码
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            // 构建响应结果
            result = Response.fail(ResponseCodeEnum.UNAUTHORIZED.getErrorCode(), ex.getMessage());
        }

修改完毕后,重启网关再次测试,提示信息就正常了~

本小节源码下载

https://t.zsxq.com/y6XCb