Spring Cloud Gateway+Spring Security OAuth2+JWT(二)

本文详细介绍了如何在SpringCloud Gateway中使用JWT进行身份验证,包括解析和验证JWT令牌,以及将用户信息传递给下游服务的过程。

Spring Cloud Gateway+Spring Security OAuth2+JWT(二)

前言

上一篇搭建了授权服务,这篇我们来讲讲网关。

Gateway校验token

我们的目标是校验token并将其解密之后传给服务。
校验token jwt有自带的。其实自己也能实现。jwt由3部分组成都有一个点分隔。

原理是第一部分base64加密 加上一个点 再加上第二部分的base64加密 利用私钥签名。
所以我们只要验第三部分,利用RSA公钥验签就行了。

package com.ljl.filter;
import com.alibaba.fastjson.JSONObject;
import com.ljl.common.util.RSAUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;

import static com.ljl.common.util.RSAUtil.verify;
import static com.sun.scenario.Settings.set;
import static java.security.KeyRep.Type.SECRET;
import static org.bouncycastle.crypto.tls.SignatureAlgorithm.rsa;

/**
 * Created by Administrator on 2019/9/9 0009.
 */
@Component
public class JwtTokenFilter implements GlobalFilter,Ordered {

    private String[] skipAuthUrls = {"/ljl-auth/oauth/token"};
    //需要从url中获取token
    private String[] urlToken = {"/ljl-server-chat/websocket"};

    /**
     * 过滤器
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String url = exchange.getRequest().getURI().getPath();
        System.out.println(url);
        //跳过不需要验证的路径
        if (null != skipAuthUrls && Arrays.asList(skipAuthUrls).contains(url)) {
            return chain.filter(exchange);
        }
        //获取token
        String token = exchange.getRequest().getHeaders().getFirst("Authorization");
        if(null != urlToken && Arrays.asList(urlToken).contains(url)){
            //该方法需要修改
            String tokens[] =  exchange.getRequest().getURI().getQuery().split("=");
            token = tokens[1];
        }
        if (StringUtils.isBlank(token)) {
            //没有token
            return returnAuthFail(exchange, "请登陆");
        } else {
            //有token
            try {
                //解密token
                Claims jwt = getTokenBody(token);

                ServerHttpRequest oldRequest= exchange.getRequest();
                URI uri = oldRequest.getURI();
                ServerHttpRequest  newRequest = oldRequest.mutate().uri(uri).build();
                // 定义新的消息头
                HttpHeaders headers = new HttpHeaders();
                headers.putAll(exchange.getRequest().getHeaders());
                headers.remove("Authorization");
                headers.set("Authorization",jwt.toString());

                newRequest = new ServerHttpRequestDecorator(newRequest) {
                    @Override
                    public HttpHeaders getHeaders() {
                        HttpHeaders httpHeaders = new HttpHeaders();
                        httpHeaders.putAll(headers);
                        return httpHeaders;
                    }
                };

                return chain.filter(exchange.mutate().request(newRequest).build());
                /*System.out.println(jwt.toString());
                //RSA公钥验签
                String jwtData[] =  token.split("\\.");
                Boolean isSgin = RSAUtil.verify((jwtData[0]+"."+jwtData[1]).getBytes(),jwtData[2]);
                if(isSgin){
                    return chain.filter(exchange);
                }else{
                    return returnAuthFail(exchange,"token验签失败");
                }*/
            }catch (ExpiredJwtException e) {
                e.printStackTrace();
                return returnAuthFail(exchange,"token超时");
            }catch (Exception e) {
                e.printStackTrace();
                return returnAuthFail(exchange,"token验签失败");
            }
        }
    }

    /**
     * 返回校验失败
     *
     * @param exchange
     * @return
     */
    private Mono<Void> returnAuthFail(ServerWebExchange exchange,String message) {
        ServerHttpResponse serverHttpResponse = exchange.getResponse();
        serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
        String resultData = "{\"status\":\"-1\",\"msg\":"+message+"}";
        byte[] bytes = resultData.getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
        return exchange.getResponse().writeWith(Flux.just(buffer));
    }

    private static Claims getTokenBody(String token){
        return Jwts.parser()
                .setSigningKey(RSAUtil.getPublicKey())
                .parseClaimsJws(token)
                .getBody();
    }

    @Override
    public int getOrder() {
        return -201;
    }
}

服务获取token

这样我们就能够拿到我们的保存在token中的用户信息

/**
     * 根据 id 查询 基础数据
     * @param id 基础数据 id
     * @return
     */
    @RequestMapping(value="getBasicDataBy",method={RequestMethod.GET})
    @ApiOperation(value="查询基础数据", httpMethod="GET", notes="查询基础数据", produces="application/json; charset=utf-8")
    @ResponseBody
    public List getBasicDataBy(@ApiParam(required=true,value="id") @RequestParam(value="id",required=true)String id, HttpServletRequest request) {

        String str = request.getHeader("Authorization");
        System.out.println(str);
        return basicDataService.getBasicDataBy(id);
    }

效果

我用的password的模式。
我们先获取token 记得密码和client_secret 都是BCrypt过的,数据库存储的是加密之后的数据。
在这里插入图片描述

之后我们那token去请求服务接口
在这里插入图片描述

好了 初步的搭建已经完成了。不过也有很多没完成。

课程介绍 【完善体系+精品资料】本课程总计115课时,打造全网最全的微服务体系课程;从微服务是什么、能够做什么开始讲起,绝对零基础入门到精通类型。课程整体脉络十分清晰,每个章节一个知识点,画图+源码+运行讲解,不信你学不会。1、课程先讲解了什么是单体架构、什么是微服务架构、他们之间有什么区别和联系,各自有什么优缺点。2、从本质入手,使用最简单的Spring Boot搭建微服务,让你认清微服务是一种思想和解决问题的手段,而不是新兴技术。3、讲解Spring Boot 与 Spring Cloud 微服务架构之间的联系,原生的RestTemplate工具,以及Actuator监控端点的使用。4、带着微服务所带来的各种优缺点,为大家引入服务发现与注册的概念和原理,从而引入我们的第一个注册中心服务Eureka。5、引入负载均衡的理念,区分什么是服务端负载均衡,什么是客户端负载均衡,进而引入Ribbon负载均衡组件的详细使用。6、为了解决微服务之间复杂的调用,降低代码的复杂度,我们引入了Feign声明式客户端,让你几行代码学习服务的远程调用。7、为了解决服务之间的稳定性,避免发生雪崩问题,我们引入了Hystrix断路器,服务降级和熔断机制。8、微服务集群十分庞大,监控起来是十分困难的,尤其是对每一个接口的熔断情况进行监控,因此我们引入了Turbine微服务监控。9、微服务的调用是杂乱无章的,可以网状调用,怎么做到统一的入口出口,统一的授权、加密、解密、日志过滤,我们引入了第一代网关Zuul。10、微服务的配置分散,每次要修改配置都要重启服务,因此我们引入了Config配置中心。11、跟上主流,Consul是当前主流的服务注册与发现、配置中心一体化的解决方案。12、阿里的Nacos服务注册与发现、配置中心在国内炙手可热,Nacos 经历过双十一的微服务中间件。13、Turbin做微服务监控还是太弱,我们需要更强大,可视化,操作性更强的监控系统,因此我引入了Spring Boot Admin体系。14、Zuul已经停止更新支持,Spring Cloud官方推荐的代网关Spring Cloud Gateway更加强大。15、微服务的安全架构体系虽然复杂,但是是有学习条例的,什么是认证授权、什么是OAuth2.0的原理、 JWT、怎么样去开发实现。 课程资料 【独家资料】1、课程附带全部63个项目源码,其中Hoxton版本项目源码37个,Edgware版本项目26个,2230页高清PDF正版课件。3、附带nacos、consul、cmder等视频配套软件。学习方法1、每一节课程均有代码,较好的方式为一边听我的讲解,一边使用我提供的项目代码进行观察和运行。2、课程体系庞大,但是并不杂乱,每个章节只针对一个知识点,减轻学习压力。3、坚持每天学习1~2个章节,可以在地铁、公交上用手机学习。【完善知识体系图】
评论 14
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值