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去请求服务接口
在这里插入图片描述

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

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐