[プロジェクトの実際の戦闘] Spring CloudGatewayはJWT /トークンログイン認証プロセスを実装します

目次

I.はじめに

1. Spring CloudGatewayの概要

2.トークンの概要

2.ログイン認証プロセス

3.実際の戦闘を計画する

1.認証認証サービス

2.ゲートウェイゲートウェイサービス


I.はじめに

この記事では、ログイン時にトークンを作成し、ゲートウェイでトークンを確認し、ユーザー情報をヘッダーリクエストヘッダーに抽出して、ダウンストリームのビジネスシステムに送信する方法について説明します。

このプロジェクトでは、マイクロサービスの統合ルーティングのゲートウェイとしてSpring Cloud Gatewayを使用しています。認証認証モジュールはトークンの生成を担当し、ゲートウェイゲートウェイモジュールはトークンの検証と入口の統合を担当します

ゲートウェイの役割は、APIサービスへのアクセスを保護、拡張、および制御するためのAPIアーキテクチャとしての役割です。APIゲートウェイは、アプリケーションまたはサービス(REST APIインターフェイスサービスを提供)の前にあるシステムであり、承認、アクセス制御、トラフィック制限などを管理するために使用されます。これにより、REST APIインターフェイスサービスはAPIゲートウェイによって保護され、すべての呼び出し元に対して透過的になります。 。したがって、APIゲートウェイの背後に隠されたビジネスシステムは、これらの戦略的インフラストラクチャを処理する代わりに、サービスの作成と管理に集中できます。

1. Spring CloudGatewayの概要

スプリングクラウドゲートウェイは公式れるスプリングゲートウェイは、以下のような技術に基づいて開発されたスプリング5.0、スプリングブート2.0プロジェクト炉。スプリングクラウドゲートウェイはmicroserviceアーキテクチャの管理方法をルーティングする単純かつ効果的な統一されたAPIを提供することを目的とします。Spring Cloudエコシステムのゲートウェイとして、Spring Cloud GatewayはZUULの置き換えを目指しています。これは、統一されたルーティング方法を提供するだけでなく、セキュリティ、監視/埋め込みポイント、電流制限など、フィルタチェーンに基づくゲートウェイの基本機能も提供します。待つ。

2.トークンの概要

JWTは、実際には3つの部分から構成された文字列であり、ヘッダペイロード、および署名。これら三つの部品はすべてJSON形式です。

詳細については、https//blog.csdn.net/a1036645146/article/details/103726635を参照してください。

2.1ヘッダー

ヘッダーは、タイプや署名アルゴリズムなど、JWTに関する最も基本的な情報を説明するために使用されます。

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

ここでは、これがJWTであり、使用する署名アルゴリズムがHS256アルゴリズムであることを説明しました

2.2ペイロード

ペイロードを使用して、機密性の低い情報を入力できます。

{
    "iss": "John Wu JWT",
    "iat": 1441593502,
    "exp": 1441594722,
    "aud": "www.example.com",
    "sub": "[email protected]",
    "from_user": "B",
    "target_user": "A"
}

最初の5つのフィールドは、JWT標準によって定義されています。

  • iss:JWTの発行者
  • sub:このJWTの対象となるユーザー
  • aud:JWTを受け取る当事者
  • exp(期限切れ):いつ期限切れになりますか?これがUnixのタイムスタンプです
  • iat(発行日):いつ発行されたか

ヘッダーとペイロードをそれぞれBase64エンコードした後、2つの文字列が取得.され、エンコードされた2つの文字列が英語のピリオド(頭から先)で接続され、新しい文字列が形成されます。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0

2.3署名

最後に、HS256アルゴリズムを使用して、上記のスプライスされた文字列を暗号化します。暗号化するときは、秘密も提供する必要があります。暗号化されたコンテンツも文字列であり、最後の文字列が署名です。この署名を文字列の後ろに接続することで、完全なjwtを取得できます。ヘッダー部分とペイロード部分が改ざんされた場合、改ざん者はキーが何であるかを認識せず、新しい署名部分を生成できないため、サーバーは通過できません。jwtでは、メッセージ本文は透過的であり、署名はメッセージが透過的でないことを保証できます。改ざんされています。

もちろん、誰かのトークンが他人に盗まれた場合、私はそれを助けることはできません。また、泥棒は正当なユーザーであると思います。これは、実際には、他人に盗まれた人のセッションIDと同じです。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJyb2xlcyI6InB1cmNoYXNlciIsInNvdXJjZSI6IlBDIiwidXNlck5hbWUiOiIxNTYyMjEzMzIzMSIsImV4cCI6MTYwNDgwMDM5NSwidXNlcklkIjoiMTE5MCJ9
.zpM6dyakS1N6kySMIEHuOZfN8l4WlybRbq6VK1cDqGc

 

2.ログイン認証プロセス

ユーザーが正常にログインすると、認証サービス(authモジュール)がトークンを生成し、後続のすべてのフロントエンド要求はこのトークンを要求ヘッダーに入れます。ゲートウェイは、トークンを検証し、ユーザー情報を要求のヘッダーに配置して、ダウンストリームシステムがユーザー情報を簡単に取得できるようにします。

トークン認証に基づく基本的な認証プロセスは次のとおりです。

  1. ユーザーはログイン情報を入力し(またはトークンインターフェイスを呼び出してユーザー情報を渡し)、認証のために認証サービスに送信します。注:ID認証サービスは、マイクロサービスの分割に応じて、サーバーと一緒にすることも、分離することもできます。ここに単一の認証モジュールがあります。
  2. 認証サービス(auth)は、ログイン情報が正しいかどうかを確認し、正しい場合はトークンを返します(一般的なインターフェイスには、ユーザーの基本情報、権限の範囲、有効時間などの情報が含まれます)。
  3. クライアントはトークンを保存し、別のインターフェイスを再度要求すると、トークンをHTTP要求ヘッダーに配置して、関連するAPI呼び出しを開始できます。
  4. バックエンドインターフェイスにアクセスするには、クライアントはゲートウェイを通過する必要があります。ゲートウェイはインターセプターを均一に構成し、トークンの権限を確認してから、対応するバックエンドの他のマイクロサービスインターフェイスに転送します。
  5. 検証に合格すると、サーバーは関連するリソースとデータを返します。

プロジェクトには次のモジュールが含まれます。

  • ゲートウェイ-ゲートウェイサービス
  • auth-認証サービス
  • ユーザーサービス
  • 注文サービス
  • (その他のマイクロサービス)..。

各モジュールアプリケーションはSpringBootに基づいて構築され、構成センターおよび登録センターとしてNacosを使用し、サービス間のインターフェイス呼び出しにFeignを使用し、Httpインターフェイス(REST API)を公開し、API統合入口としてゲートウェイを使用します。フロントエンド要求はダウンストリームマイクロサービスを直接呼び出すことはできません。特定のインターフェース。

3.実際の戦闘を計画する

1.認証認証サービス

SpringBootを介して認証認証サービスモジュールを構築し、登録および構成センターでNacosを使用します。

このモジュールは主にログインインターフェイスを提供し、トークンを生成します。

1.1 pom.xml

jwtツールキットをインポートし、最新の安定バージョンを使用する必要があります。

<dependency>
      <groupId>com.auth0</groupId>
      <artifactId>java-jwt</artifactId>
      <version>${jwt.version}</version>
</dependency>

1.2JWTUtils.javaツールクラス

/**
 * jwt工具类
 *
 * @author stwen_gan
 * @since 
 */
public class JWTUtils {

    // token 签名的秘钥,可设置到配置文件中
    private static final String SECRET_KEY = "secretKey:123456";
    // token过期时间
    public static final long TOKEN_EXPIRE_TIME = 7200 * 1000;

    /**
     * 生成jwt
     */
    public String createJwt(String userId){
        Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY);
        //设置头信息
        HashMap<String, Object> header = new HashMap<>(2);
        header.put("typ", "JWT");
        header.put("alg", "HS256");
        // 生成 token:头部+载荷+签名
        return JWT.create().withHeader(header)
                .withClaim(RequestKeyConstants.USER_ID,userId)
                .withExpiresAt(new Date(System.currentTimeMillis()+TOKEN_EXPIRE_TIME)).sign(algorithm);
    }

    /**
     * 解析jwt
     */
    public Map<String, Claim> parseJwt(String token) {
        Map<String, Claim> claims = null;
        try {
            Algorithm algorithm = Algorithm.HMAC256(key);
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(token);
            claims = jwt.getClaims();
            return claims;
        } catch (Exception e) {
            return null;
        }
    }
}

注:このツールの署名キーと有効期限を構成センターのファイルに一律に構成してから、メソッドを介してパラメーターを渡すことをお勧めします。

トークン構成:

{
    "typ": "JWT",
    "alg": "HS256"
}
  • ペイロード:ユーザーは、機密性の低いユーザー情報(ユーザーID、ユーザー名、権限、役割など)を保存します。
  • 署名:指定された秘密鍵を使用して、上記の2つの部分に署名します

1.3ログインインターフェイス

LoginController

注:デモンストレーションの便宜上、コードの一部は省略されています。

@PostMapping("/auth/login")
public Result login(HttpServletRequest request, @Valid @RequestBody LoginDTO loginDto) {

    String ip = IpAddressUtils.getIpAddr(request);
    // 获取用户信息、比对密码
    Result<UserDTO> result = loginFeignApi.login(loginDto,ip);
    if(ResultCode.SUCCESS.getCode()!=result.getCode()){
        log.error(result.getMsg());
        return result;
    }
    UserDTO user = result.getData();
    String token = JWTUtils.createJwt(user.getId() + "");
    data.put("token",token);
    return Result.success(data);
}

その中で、loginFeignApi.login(loginDto、ip) 、feignを介しユーザーサービスモジュールを呼び出すため のインターフェイスです。ユーザー名に従ってユーザー情報を照会し、次のようにパスワードに対してMD5暗号化とソルト検証を実行します。

User user = userService.getUserByName(loginDTO.getUserName());
// 密码md5 校验
if (!user.getPassword().equals(MD5Utils.toMD5Password(user.getUserSalt(), loginDTO.getPassword()))){
    userService.userLog(user,ip,UserLogTypeEnum.LOGIN_FAIL,loginDTO.getSource());
    return Result.error(ResultCode.PASSWORD_ERROR);
}

2.ゲートウェイゲートウェイサービス

同様に、ゲートウェイサービスモジュールはSpringBootを介して構築され、登録および構成センターはNacosを均一に使用し、他のサービスAPIの統合エントリとして他のサービスインターフェイスのルーティング戦略を構成します。

ゲートウェイは、トークンを検証し、ユーザー関連情報を要求のヘッダーに入れて、ダウンストリームシステムがユーザー情報を簡単に取得できるようにします。

2.1 pom.xml  

同様に、jwtツールキットを導入する必要があります

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>${jwt.version}</version>
</dependency>

2.2 application.properties

server.port=1234
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

# gateway 路由策略
spring.cloud.gateway.routes[0].id=user
spring.cloud.gateway.routes[0].uri=lb://user-service
spring.cloud.gateway.routes[0].predicates[0]=Path=/userService/**
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=1
spring.cloud.gateway.routes[1].id=order
spring.cloud.gateway.routes[1].uri=lb://order-service
spring.cloud.gateway.routes[1].predicates[0]=Path=/orderService/**
spring.cloud.gateway.routes[1].filters[0]=StripPrefix=1

### 。。。。。。。

2.3トークンの確認

GatewayFilter または GlobalFilterフィルターインターフェイスを実装できます。フロントエンドリクエストがゲートウェイを通過すると、トークンの有効性を検証するためにフィルター処理されます。ここでは、GlobalFilterインターフェイスを実装します。

TokenFilter.java

/**
 * @description: token过滤器
 * @author: xianhao_gan
 * @date:
 **/
@Slf4j
@Component
public class TokenFilter implements GlobalFilter, Ordered {

    @Autowired
    private JwtUtils jwtUtils;

    /**
     * 校验token
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String token = request.getHeaders().getFirst(RequestKeyConstants.TOKEN);
        //检查token是否为空
        if (StringUtils.isEmpty(token)) {
            return denyAccess(exchange, ResultCode.TOKEN_NULL);
        }

        Map claimMap1 = jwtUtils.parseJwt(token);
        //token有误
        if (claimMap.containsKey("exception")) {
            log.error() (claimMap1.get("exception").toString());
            return denyAccess(exchange, ResultCode.TOKEN_INVALID);
        }

        //token无误,将用户信息设置进header中,传递到下游服务
        Map<String, Claim> claimMap = claimMap1;
        String userId = claimMap.get(RequestKeyConstants.USER_ID).asString();
        Consumer<HttpHeaders> headers = httpHeaders -> {
            httpHeaders.add(RequestKeyConstants.USER_ID, userId);
        };
        request.mutate().headers(headers).build();
        
        // todo 权限校验

        return chain.filter(exchange);
    }

    /**
     * 拦截并返回自定义的json字符串
     */
    private Mono<Void> denyAccess(ServerWebExchange exchange, ResultCode resultCode) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.OK);
        //这里在返回头添加编码,否则中文会乱码
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        byte[] bytes = JSON.toJSONBytes(Result.error(resultCode), SerializerFeature.WriteMapNullValue);
        DataBuffer buffer = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(buffer));
    }

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

}

  • もちろん、実際のプロジェクトのコードはこれよりも複雑になります。デモンストレーションを容易にし、基本的な考え方を説明するために、コードの一部を省略しています。実際の設計と開発は、この考え方に従って拡張できます。

  • ログイン後に生成されたトークンの場合、バックエンドは固定の有効期間を設定します。有効期間中、ユーザーはアクセス用のトークンを携帯します。トークンの有効期限が切れると、トークンは無効になります。フロントエンドはログインページにジャンプしてユーザーに再度ログインさせますが、ユーザーエクスペリエンスはフレンドリーではありません。

  • 改善:アクティブユーザーは、トークンの有効期限が切れた後、気付かないうちに新しいトークンを自動的に取得し、アクセスのためにこの新しいトークンを携帯する必要があります。一方、長期間非アクティブだったユーザーは、JWTの有効期限が切れた後に再度ログインする必要があります。後で、トークンタイムアウト更新戦略を追加する時間があります。

参照:https//www.cnblogs.com/cjsblog/p/12425912.html

史上最強のTomcat8パフォーマンス最適化

なぜアリババは90秒で100億に抵抗できるのですか?-サーバー側の高同時分散アーキテクチャの進化

B2Beコマースプラットフォーム--ChinaPayUnionPay電子決済機能

Zookeeperの分散ロックを学び、インタビュアーに感心してあなたを見てもらいましょう

SpringCloudeコマーススパイクマイクロサービス-Redisson分散ロックソリューション

もっと良い記事をチェックして、公式アカウントを入力してください-私にお願いします-過去に素晴らしい

深くソウルフルなパブリックアカウント0.0

 

おすすめ

転載: blog.csdn.net/a1036645146/article/details/109546416