微服务聚合JWT实现权鉴

整体流程如下:

第一步:使用java自带工具生成证书

在jdk的bin目录下用cmd命令操作keytool工具生成证书,记得以管理员身份运行否则会失败,命令如下:

keytool -genkeypair -alias jwt -keyalg RSA -keypass 123456 -keystore jwt.jks -storepass 123456
  • Keytool 是一个java提供的证书管理工具

    • alias:密钥的别名
    • keyalg:使用的hash算法
    • keypass:密钥的访问密码
    • keystore:密钥库文件名,secre.jks保存了生成的证书
    • storepass:密钥库的访问密码

得到文件后将文件拷贝到鉴权微服务的source目录下。

第二步:添加依赖

 <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.20</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-rsa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

第三步:添加权鉴方法

@Data
@AllArgsConstructor
@NoArgsConstructor
public class JwtTemplate {

    // 证书文件
    private String path = "jwt.jks";
    // 密钥库文件的密码
    private String keyStoreSecurity = "123456";
    // 密钥库的别名
    private String alias = "jwt";

    /**
     * 创建密钥对对象
     */
    private KeyPair keyPair(){
        // 创建密钥对对象
        KeyStoreKeyFactory factory = new KeyStoreKeyFactory(
                new ClassPathResource(path),keyStoreSecurity.toCharArray()
        );
        KeyPair keyPair = factory.getKeyPair(alias);
        return keyPair;
    }

    /**
     * 创建签名器
     */
    private JWTSigner jwtSigner(){
        return JWTSignerUtil.createSigner("RSA",keyPair());
    }

    /**
     * 【生成token】
     */
    public String createToken(Map<String,Object> playload) {
        return JWTUtil.createToken(playload,jwtSigner());
    }

    /**
     * 【校验token】
     */
    public boolean verify(String token) {
        return JWTUtil.verify(token, jwtSigner());
    }

    /**
     * 【解析token】
     */
    public Object parseToken(String token,String key) {
        JWT jwt = JWTUtil.parseToken(token);
        return jwt.getPayload(key);
    }

}

第四步:添加依赖

这里添加权鉴微服务的依赖以便调用

<dependency>
            <groupId>com.woniu</groupId>
            <artifactId>sk-common-jwt</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

第五步:网关yml文件添加配置

这里的path就是权鉴证书名,别名和秘钥要和前面保持一致。

auth:
  security:
    path: jwt.jks
    keyStoreSecurity: 123456
    alias: jwt

第六步:增加redis和JWT的配置类

Redis配置类

@Configuration
public class RedisConfig {
    /**
     * 配置RedisTemplate的序列化方式
     */
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(factory);
        // 指定key的序列化方式:string
        redisTemplate.setKeySerializer(RedisSerializer.string());
        // 指定value的序列化方式:json
        redisTemplate.setValueSerializer(RedisSerializer.json());
        return redisTemplate;
    }
}

JWT的配置类

@Configuration
public class JwtConfig {
    @Value("${auth.security.path}")
    private String path;
    @Value("${auth.security.keyStoreSecurity}")
    private String keyStoreSecurity;
    @Value("${auth.security.alias}")
    private String alias;

    @Bean
    public JwtTemplate jwtTemplate(){
        return new JwtTemplate(path,keyStoreSecurity,alias);
    }
}

第七步:增加过滤器

@Component
@Slf4j
public class AuthFilter implements GlobalFilter, Ordered {

    @Autowired
    private JwtTemplate jwtTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        // 1、获取请求路径,并进行判断路径中是否包含/auth
        String path = request.getURI().getPath();
        AntPathMatcher pathMatcher = new AntPathMatcher();
        // 请求路径如:/promotion-seckill/auth/order/100
        boolean match = pathMatcher.match("/**/auth/**", path);
        if (match) { // 需要对当前路径进行鉴权

            //2. 鉴权要求:请求头中Authorizaiton中携带token
            String token = request.getHeaders().getFirst("Authorization");
            if (StringUtils.isEmpty(token)) {
                // 鉴权失败,返回错误
                log.error("请求头中未携带token!");
                return error(response);
            }

            //3. 请求头中的token值的格式:  Bearer xx
            token = token.replace("Bearer ","");

            //4. token校验, 校验失败:JWTException: The token was expected 3 parts, but got 1.
            boolean verify = jwtTemplate.verify(token);
            if (!verify) {
                log.error("token不合法!");
                return error(response);
            }

            //5. 解析token,获取token中存储的userId。后面会作为redis中的key
            String userId = (String) jwtTemplate.parseToken(token, "userId");

            //6. 从Redis中获取token并校验
            String tokenKey = "user:token:"+userId;
            String redisToken = (String) redisTemplate.opsForValue().get(tokenKey);
            if (StringUtils.isEmpty(redisToken)) {
                log.error("token无效!");
                return error(response);
            }

            //7. 重写请求头,把userId放入请求头中;这样各个微服务就可以从请求头中获取认证的userId
            ServerHttpRequest.Builder mutate = request.mutate();
            mutate.header("userId",userId);

            //8. 更新redis中token的有效时间
            redisTemplate.expire(tokenKey, Duration.ofMinutes(30));
        }

        // 放行,执行下一个过滤器或者执行微服务
        return chain.filter(exchange);
    }


    // 自动处理检查异常的注解
    @SneakyThrows
    private Mono<Void> error(ServerHttpResponse response)  {
        Result<Object> result = Result.fail(ResultCode.PERMISSION_NO_ACCESS);
        ObjectMapper objectMapper = new ObjectMapper();
        byte[] bytes = objectMapper.writeValueAsBytes(result);
        DataBuffer dataBuffer = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(dataBuffer));
    }

    // 控制过滤器执行顺序,数字越小越先执行
    @Override
    public int getOrder() {
        return 0;
    }





}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小海海不怕困难

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值