微服务网关限流&鉴权3

简介: 微服务网关限流&鉴权

4.加密算法(了解)

由于在学习JWT的时候会涉及使用很多加密算法, 所以在这里做下扫盲, 简单了解就可以

加密算法种类有:

4.1.可逆加密算法

解释: 加密后, 密文可以反向解密得到密码原文.

4.1.1. 对称加密

【文件加密和解密使用相同的密钥,即加密密钥也可以用作解密密钥】

解释: 在对称加密算法中,数据发信方将明文和加密密钥一起经过特殊的加密算法处理后,使其变成复杂的加密密文发送出去,收信方收到密文后,若想解读出原文,则需要使用加密时用的密钥以及相同加密算法的逆算法对密文进行解密,才能使其回复成可读明文。在对称加密算法中,使用的密钥只有一个,收发双方都使用这个密钥,这就需要解密方事先知道加密密钥。

优点: 对称加密算法的优点是算法公开、计算量小、加密速度快、加密效率高。

缺点: 没有非对称加密安全.

用途: 一般用于保存用户手机号、身份证等敏感但能解密的信息。

常见的对称加密算法有: AES、DES、3DES、Blowfish、IDEA、RC4、RC5、RC6、HS256

4.1.2. 非对称加密

【两个密钥:公开密钥(publickey)和私有密钥,公有密钥加密,私有密钥解密

**解释: ** 同时生成两把密钥:私钥和公钥,私钥隐秘保存,公钥可以下发给信任客户端.

加密与解密:

  • 私钥加密,持有私钥或公钥才可以解密
  • 公钥加密,持有私钥才可解密

签名:

  • 私钥签名, 持有公钥进行验证是否被篡改过.

**优点: ** 非对称加密与对称加密相比,其安全性更好;

缺点: 非对称加密的缺点是加密和解密花费时间长、速度慢,只适合对少量数据进行加密。

用途: 一般用于签名和认证。私钥服务器保存, 用来加密, 公钥客户拿着用于对于令牌或者签名的解密或者校验使用.

常见的非对称加密算法有: RSA、DSA(数字签名用)、ECC(移动设备用)、RS256 (采用SHA-256 的 RSA 签名)

4.2.不可逆加密算法

解释: 一旦加密就不能反向解密得到密码原文.

种类: Hash加密算法, 散列算法, 摘要算法等

**用途:**一般用于效验下载文件正确性,一般在网站上下载文件都能见到;存储用户敏感信息,如密码、 卡号等不可解密的信息。

常见的不可逆加密算法有: MD5、SHA、HMAC

4.3.Base64编码

Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一。Base64编码可用于在HTTP环境下传递较长的标识信息。采用Base64编码解码具有不可读性,即所编码的数据不会被人用肉眼所直接看到。注意:Base64只是一种编码方式,不算加密方法。

在线编码工具:

http://www.jsons.cn/img2base64/

5. JWT 实现微服务鉴权

5.1 什么是微服务鉴权

我们之前已经搭建过了网关,使用网关在系统中比较适合进行权限校验。

那么我们可以采用JWT的方式来实现鉴权校验。

5.2 JWT

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。

一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。

头部(Header)

头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象。

{"typ":"JWT","alg":"HS256"}

在头部指明了签名算法是HS256算法。 我们进行BASE64编码http://base64.xpcha.com/,编码后的字符串如下:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

载荷(playload)

载荷就是存放有效信息的地方。

定义一个payload:

{"sub":"1234567890","name":"John Doe","admin":true}

然后将其进行base64加密,得到Jwt的第二部分。

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

签证(signature)

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

header (base64后的)

payload (base64后的)

secret

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

5.3 JJWT签发与验证token

JJWT是一个提供端到端的JWT创建和验证的Java库。永远免费和开源(Apache License,版本2.0),JJWT很容易使用和理解。它被设计成一个以建筑为中心的流畅界面,隐藏了它的大部分复杂性。

官方文档:

https://github.com/jwtk/jjwt

5.3.1 创建token

(1)新建项目jwtTest中的pom.xml中添加依赖:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>

(2) 创建测试类,代码如下

JwtBuilder builder= Jwts.builder()
    .setId("888")   //设置唯一编号
    .setSubject("小白")//设置主题  可以是JSON数据
    .setIssuedAt(new Date())//设置签发日期
    .signWith(SignatureAlgorithm.HS256,"itcast");//设置签名 使用HS256算法,并设置SecretKey(字符串)
//构建 并返回一个字符串 
System.out.println( builder.compact() );

运行打印结果:

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NTc5MDQxODF9.ThecMfgYjtoys3JX7dpx3hu6pUm0piZ0tXXreFU_u3Y

再次运行,会发现每次运行的结果是不一样的,因为我们的载荷中包含了时间。

5.3.2 解析token

我们刚才已经创建了token ,在web应用中这个操作是由服务端进行然后发给客户端,客户端在下次向服务端发送请求时需要携带这个token(这就好像是拿着一张门票一样),那服务端接到这个token 应该解析出token中的信息(例如用户id),根据这些信息查询数据库返回相应的结果。

String compactJwt="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NTc5MDQxODF9.ThecMfgYjtoys3JX7dpx3hu6pUm0piZ0tXXreFU_u3Y";
Claims claims = Jwts.parser().setSigningKey("itcast").parseClaimsJws(compactJwt).getBody();
System.out.println(claims);

运行打印效果:

{jti=888, sub=小白, iat=1557904181}

试着将token或签名秘钥篡改一下,会发现运行时就会报错,所以解析token也就是验证token.

5.3.3 设置过期时间

有很多时候,我们并不希望签发的token是永久生效的,所以我们可以为token添加一个过期时间。

(1)创建token 并设置过期时间

//当前时间
long currentTimeMillis = System.currentTimeMillis();
Date date = new Date(currentTimeMillis);
JwtBuilder builder= Jwts.builder()
    .setId("888")   //设置唯一编号
    .setSubject("小白")//设置主题  可以是JSON数据
    .setIssuedAt(new Date())//设置签发日期
    .setExpiration(date)
    .signWith(SignatureAlgorithm.HS256,"itcast");//设置签名 使用HS256算法,并设置SecretKey(字符串)
//构建 并返回一个字符串
System.out.println( builder.compact() );

解释:

.setExpiration(date)//用于设置过期时间 ,参数为Date类型数据

运行,打印效果如下:

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NTc5MDUzMDgsImV4cCI6MTU1NzkwNTMwOH0.4q5AaTyBRf8SB9B3Tl-I53PrILGyicJC3fgR3gWbvUI

(2)解析TOKEN

String compactJwt="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NTc5MDUzMDgsImV4cCI6MTU1NzkwNTMwOH0.4q5AaTyBRf8SB9B3Tl-I53PrILGyicJC3fgR3gWbvUI";
Claims claims = Jwts.parser().setSigningKey("itcast").parseClaimsJws(compactJwt).getBody();
System.out.println(claims);

打印效果:

当前时间超过过期时间,则会报错。

5.3.4 自定义claims

我们刚才的例子只是存储了id和subject两个信息,如果你想存储更多的信息(例如角色)可以定义自定义claims。

创建测试类,并设置测试方法:

创建token:

@Test
public void createJWT(){
    //当前时间
    long currentTimeMillis = System.currentTimeMillis();
    currentTimeMillis+=1000000L;
    Date date = new Date(currentTimeMillis);
    JwtBuilder builder= Jwts.builder()
        .setId("888")   //设置唯一编号
        .setSubject("小白")//设置主题  可以是JSON数据
        .setIssuedAt(new Date())//设置签发日期
        .setExpiration(date)//设置过期时间
        .claim("roles","admin")//设置角色
        .signWith(SignatureAlgorithm.HS256,"itcast");//设置签名 使用HS256算法,并设置SecretKey(字符串)
    //构建 并返回一个字符串
    System.out.println( builder.compact() );
}

运行打印效果:

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NTc5MDU4MDIsImV4cCI6MTU1NzkwNjgwMiwicm9sZXMiOiJhZG1pbiJ9.AS5Y2fNCwUzQQxXh_QQWMpaB75YqfuK-2P7VZiCXEJI

解析TOKEN:

//解析
@Test
public void parseJWT(){
    String compactJwt="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NTc5MDU4MDIsImV4cCI6MTU1NzkwNjgwMiwicm9sZXMiOiJhZG1pbiJ9.AS5Y2fNCwUzQQxXh_QQWMpaB75YqfuK-2P7VZiCXEJI";
    Claims claims = Jwts.parser().setSigningKey("itcast").parseClaimsJws(compactJwt).getBody();
    System.out.println(claims);
}

运行效果:

5.4 畅购微服务鉴权代码实现

5.4.1 思路分析

1. 用户进入网关开始登陆,网关过滤器进行判断,如果是登录,则路由到后台管理微服务进行登录
2. 用户登录成功,后台管理微服务签发JWT TOKEN信息返回给用户
3. 用户再次进入网关开始访问,网关过滤器接收用户携带的TOKEN 
4. 网关过滤器解析TOKEN ,判断是否有权限,如果有,则放行,如果没有则返回未认证错误

5.4.2 系统微服务签发token

(1)在changgou_service_system添加依赖

<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.9.0</version>
</dependency>

(2)在changgou_service_system中创建类: JwtUtil

package com.changgou.system.util;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
/**
 * JWT工具类
 */
public class JwtUtil {
    //有效期为
    public static final Long JWT_TTL = 3600000L;// 60 * 60 *1000  一个小时
 //设置秘钥明文
    public static final String JWT_KEY = "itcast";
    /**
     * 创建token
     * @param id
     * @param subject
     * @param ttlMillis
     * @return
     */
    public static String createJWT(String id, String subject, Long ttlMillis) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        if(ttlMillis==null){
            ttlMillis=JwtUtil.JWT_TTL;
        }
        long expMillis = nowMillis + ttlMillis;
        Date expDate = new Date(expMillis);
        SecretKey secretKey = generalKey();
        JwtBuilder builder = Jwts.builder()
                .setId(id)              //唯一的ID
                .setSubject(subject)   // 主题  可以是JSON数据
                .setIssuer("admin")     // 签发者
                .setIssuedAt(now)      // 签发时间
                .signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥
                .setExpiration(expDate);// 设置过期时间
        return builder.compact();
    }
    /**
     * 生成加密后的秘钥 secretKey
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }
}

(3)修改AdminController的login方法, 用户登录成功 则 签发TOKEN

/**
     * 登录
     * @param admin
     * @return
     */
    @PostMapping("/login")
    public Result login(@RequestBody Admin admin){
        boolean login = adminService.login(admin);
        if(login){  //如果验证成功
            Map<String,String> info = new HashMap<>();
            info.put("username",admin.getLoginName());
            String token = JwtUtil.createJWT(UUID.randomUUID().toString(), admin.getLoginName(), null);
            info.put("token",token);
            return new Result(true, StatusCode.OK,"登录成功",info);
        }else{
            return new Result(false,StatusCode.LOGINERROR,"用户名或密码错误");
        }
    }

使用postman 测试

5.4.3 网关过滤器验证token

(1)在changgou_gateway_system网关系统添加依赖

<!--鉴权-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>

(2)创建JWTUtil类

package com.changgou.gateway.util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
/**
 * jwt校验工具类
 */
public class JwtUtil {
    //有效期为
    public static final Long JWT_TTL = 3600000L;// 60 * 60 *1000  一个小时
 //设置秘钥明文
    public static final String JWT_KEY = "itcast";
    /**
     * 生成加密后的秘钥 secretKey
     *
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }
    /**
     * 解析
     *
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJWT(String jwt) throws Exception {
        SecretKey secretKey = generalKey();
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(jwt)
                .getBody();
    }
}

(3)创建过滤器,用于token验证

/**
 * 鉴权过滤器 验证token
 */
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
    private static final String AUTHORIZE_TOKEN = "token";
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  //1. 获取请求
        ServerHttpRequest request = exchange.getRequest();
        //2. 则获取响应
        ServerHttpResponse response = exchange.getResponse();
        //3. 如果是登录请求则放行
        if (request.getURI().getPath().contains("/admin/login")) {
            return chain.filter(exchange);
        }
        //4. 获取请求头
        HttpHeaders headers = request.getHeaders();
        //5. 请求头中获取令牌
        String token = headers.getFirst(AUTHORIZE_TOKEN);
        //6. 判断请求头中是否有令牌
        if (StringUtils.isEmpty(token)) {
            //7. 响应中放入返回的状态吗, 没有权限访问
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            //8. 返回
            return response.setComplete();
        }
        //9. 如果请求头中有令牌则解析令牌
        try {
            JwtUtil.parseJWT(token);
        } catch (Exception e) {
            e.printStackTrace();
            //10. 解析jwt令牌出错, 说明令牌过期或者伪造等不合法情况出现
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            //11. 返回
            return response.setComplete();
        }
        //12. 放行
        return chain.filter(exchange);
    }
    @Override
    public int getOrder() {
        return 0;
    }
}

(4)测试:

注意: 数据库中管理员账户为 : admin , 密码为 : 123456

如果不携带token直接访问,则返回401错误

如果携带正确的token,则返回查询结果

相关文章
|
2天前
|
负载均衡 监控 应用服务中间件
微服务架构下的API网关设计与实践
【6月更文挑战第11天】在现代软件开发中,微服务架构因其灵活性和可扩展性而受到青睐。作为微服务系统的入口,API网关承担着请求路由、负载均衡、安全认证等关键职责。本文将深入探讨API网关的设计要点与实践策略,旨在为读者提供构建高效、稳定API网关的实用指南。
|
2天前
|
监控 应用服务中间件 API
深入理解微服务架构中的API网关
在现代后端开发中,微服务架构已经成为主流选择,而API网关作为其关键组件之一,起到了不可替代的作用。本文将深入探讨API网关的基本概念、功能、常见实现方式以及在微服务架构中的重要性。通过实际案例和技术细节,帮助你全面了解如何有效地利用API网关来提升系统的性能与安全性。
|
2天前
|
监控 负载均衡 安全
探索微服务架构中的API网关模式
【6月更文挑战第10天】本文将深入探讨微服务架构中的一个重要组件——API网关。我们将详细分析API网关的功能、优势以及在微服务架构中的关键作用,并通过实例展示如何有效实现和部署API网关以提升系统性能和安全性。
19 2
|
5天前
|
安全 API 开发者
探索微服务架构中的API网关模式
【6月更文挑战第8天】本文深入探讨了在构建可扩展的微服务系统中,API网关所扮演的关键角色。我们将从API网关的定义和作用出发,分析其如何作为系统入口统一流量管理、提供安全控制、实现服务聚合以及优化客户端与各微服务间的通信。通过具体案例,本文将展示如何有效设计API网关以提升系统的灵活性和响应速度。
|
6天前
|
存储 缓存 数据库
【万字长文】微服务整合Shiro+Jwt,源码分析鉴权实战
介绍如何整合Spring Boot、Shiro和Jwt,以实现一个支持RBAC的无状态认证系统。通过生成JWT token,实现用户无状态登录,并能根据用户角色动态鉴权,而非使用Shiro提供的注解,将角色和权限信息硬编码。此外,文章还探讨了如何对Shiro的异常进行统一捕获和处理。作为应届生,笔者在学习Shiro的过程中进行了一些源码分析,尽管可能存在不足和Bug,但希望能为同样需要实现权限管理的开发者提供参考,并欢迎各位大佬指正完善。
149 65
【万字长文】微服务整合Shiro+Jwt,源码分析鉴权实战
|
6天前
|
监控 负载均衡 安全
微服务架构中的API网关模式
【6月更文挑战第7天】本文将深入探讨微服务架构中API网关的关键作用,分析其如何作为系统的统一入口点,实现请求路由、负载均衡、认证授权和监控日志等功能。我们将通过一个虚构的案例,展示API网关在提升系统性能和安全性方面的实际应用。
|
8天前
|
负载均衡 安全 应用服务中间件
微服务架构下的API网关设计与实现
【6月更文挑战第5天】本文将探讨在微服务架构中,如何设计并实现一个高效的API网关。我们将分析API网关的重要性,以及它如何帮助解决微服务中的一些关键问题,如服务发现、负载均衡和安全控制等。
11 1
|
9天前
|
缓存 负载均衡 API
探索微服务架构中的API网关模式
【6月更文挑战第4天】在微服务架构的海洋中,API网关扮演着灯塔的角色,为复杂的服务网络提供导航。本文将深入探讨API网关的设计原则、实现方式以及它如何优化微服务间的通信流程。我们将一起揭开API网关的神秘面纱,理解其在现代软件架构中的重要性。
|
1天前
|
监控 Cloud Native 开发者
云原生技术浪潮下的微服务架构实践
云原生技术正引领着现代软件开发的潮流,其中微服务架构作为其核心理念之一,为复杂应用提供了灵活、可扩展的解决方案。本文将探讨在云原生环境下实施微服务架构的策略和挑战,并结合实际案例分析微服务设计的最佳实践,旨在为开发者提供一套可行的微服务部署与管理指南。
|
1天前
|
消息中间件 监控 API
构建微服务架构:从理论到实践的全面指南
本文将深入探讨微服务架构的设计原则、实施步骤和面临的挑战。与传统的单体架构相比,微服务通过其独立性、可伸缩性和灵活性,为现代应用开发提供了新的视角。文章将介绍如何从零开始规划和部署一个微服务系统,包括选择合适的技术栈、处理数据一致性问题以及实现服务间通信。此外,我们还将讨论在迁移至微服务架构过程中可能遇到的技术和组织挑战,以及如何克服这些难题以实现顺利过渡。

热门文章

最新文章