Spring Cloud Gateway微服务网关鉴权

2021/01/17 08:23
阅读数 942

一 网关鉴权

1 问题

当我们在未登录状态下点击“购买课程”按钮时,会显示“未知错误”,查看trade微服务控制台,发现,JWT为空,无法鉴权。

2 解决方案

微服务网关中添加自定义全局过滤器,统一处理需要鉴权的服务。

3 鉴权逻辑描述

  • 当客户端第一次请求服务时,服务端对用户进行信息认证(登录)

  • 认证通过,将用户信息进行加密形成token,返回给客户端

  • 作为登录凭证以后每次请求,客户端都携带认证的token

  • 服务端对token进行解密,判断是否有效

对于验证用户是否已经登录鉴权的过程可以在网关统一检验。检验的标准就是请求中是否携带token凭证以及token的正确性。

下面的我们自定义一个GlobalFilter,去校验所有的请求参数中是否包含“token”,如何不包含请求

参数“token”则不转发路由,否则执行正常的逻辑。

二 开发鉴权逻辑

1 网关中添加依赖

<dependencies>
    <!-- 网关 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!--服务注册-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>com.atguigu</groupId>
        <artifactId>common_util</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <!--排除spring-boot-starter-web,否则和gateway中的webflux冲突-->
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!--将随着spring-boot-starter-web排除的servlet-api添加回来 -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
    <!--gson-->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
    </dependency>
</dependencies>

2 排除数据源自动配置

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

3 创建过滤器

@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        // 判断路径中如果含有 /api/**/auth/**,则需要鉴权
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        if(antPathMatcher.match("/api/**/auth/**", path)){
            // 获取token
            List<String> tokenList = request.getHeaders().get("token");
            // 没有token
            if(tokenList == null){
                ServerHttpResponse response = exchange.getResponse();
                return out(response);
            }
            // token校验失败
            boolean isCheck = JwtUtils.checkJwtTToken(tokenList.get(0));
            if(!isCheck){
                ServerHttpResponse response = exchange.getResponse();
                return out(response);
            }
        }
        // 放行: 使用过滤器链,将请求继续向下传递
        return chain.filter(exchange);
    }
    // 定义当前过滤器的优先级。值越小,优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
    // 使用webflux输入请求信息
    private Mono<Void> out(ServerHttpResponse response) {
        JsonObject message = new JsonObject();
        message.addProperty("success", false);
        message.addProperty("code", 28004);
        message.addProperty("data", "");
        message.addProperty("message", "鉴权失败");
        byte[] bytes = message.toString().getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bytes);
        // 指定编码,否则在浏览器中会中文乱码
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        // 输出http响应
        return response.writeWith(Mono.just(buffer));
    }
}

测试:未登录状态下点击立即购买显示“鉴权失败”。

4 前端修改

guli-site的utils/request.js中修改响应过滤器 ,添加分支:

else if (res.code === 28004) { // 鉴权失败
    window.location.href = '/login'
    return
}

修改pages/login.vue的submitLogin方法:登录后回到原来的页面

// 跳转到首页// window.location.href = '/'if (document.referrer.indexOf('register') !== -1) {    window.location.href = '/'} else {    history.go(-1)}

 

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部