JWTを使用したマイクロサービスのパスワードの暗号化と認証の詳細

[TOC]

1.1マイクロサービスの状況を把握

マイクロサービスは、RESTスタイルのインターフェイスの外部に設けられている、それぞれのサービスをクラスタ化し、最も重要な仕様の1つの残りスタイルは次のとおりです。サービスのステートレス。

ステートレスとは何ですか?

1.サーバは、クライアントの情報要求を保存しません

2.各リクエストのクライアントは、この情報は、クライアント端末のアイデンティティを識別する自己記述情報でなければなりません

マイクロオープンサービスでません状態、利点?

サービス側に依存しないクライアント要求の情報は、あなたが同じサービスへのアクセスを持っているために、それ以上の要求を必要としない1

2.クライアントへのサーバークラスタ透明かどうか

3.サーバーは、任意および伸縮を移行することができます

4.サーバー上に格納された減圧

1.2、非国家の原則を達成するためにログオンします

  • 本番サーバ固有の識別子(注:最終チェックが必要です)
    • スキーム1:UUID、情報の過剰な種類が含まれていない単一のデータ。
    • プログラム2:RAS暗号化、データとアルゴリズムを使用するための多様な、必要性、いくつかの困難を理解することがあります。[用途]
  • ブラウザと自動的にキャリーデータストレージ
    • シナリオ1:クッキーを使用して、多くの制限があります(サイズ、数)
    • スキーム2:リクエストパラメータが、長さの制限を有するURL GETリクエストは、各パスが面倒プロセスを必要とします。
    • シナリオ3:ブラウザのlocalStroageストレージ、リクエストヘッダを運びます。[用途]

2.1、RASツール

データのセキュリティを確保するために別のサービスでは、データ、分析データに氏はJWTを使用して、サービスおよびデータサービス間での共有、データは、RASを使用して暗号化されます。

使用RAS加密保证token数据在传输过程中不会被篡改

  • RAS:非対称暗号化アルゴリズム
    • 機能
      • 公開鍵と秘密鍵:鍵のペアの生産ながら
      • 公開鍵:暗号化するために使用されます
      • プライベートキー:解読するために使用

ツールRasUtils

package com.czxy.utils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; /** * @author 庭前云落 * @Date 2019/12/13 22:01 * @description */ public class RasUtils { /** * 从文件中读取公钥 * * @param filename 公钥保存路径,相对于classpath * @return 公钥对象 * @throws Exception */ public static PublicKey getPublicKey(String filename) throws Exception { byte[] bytes = readFile(filename); return getPublicKey(bytes); } /** * 从文件中读取密钥 * * @param filename 私钥保存路径,相对于classpath * @return 私钥对象 * @throws Exception */ public static PrivateKey getPrivateKey(String filename) throws Exception { byte[] bytes = readFile(filename); return getPrivateKey(bytes); } /** * 获取公钥 * * @param bytes 公钥的字节形式 * @return * @throws Exception */ public static PublicKey getPublicKey(byte[] bytes) throws Exception { X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes); KeyFactory factory = KeyFactory.getInstance("RSA"); return factory.generatePublic(spec); } /** * 获取密钥 * * @param bytes 私钥的字节形式 * @return * @throws Exception */ public static PrivateKey getPrivateKey(byte[] bytes) throws Exception { PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes); KeyFactory factory = KeyFactory.getInstance("RSA"); return factory.generatePrivate(spec); } /** * 根据密文,生存rsa公钥和私钥,并写入指定文件 * * @param publicKeyFilename 公钥文件路径 * @param privateKeyFilename 私钥文件路径 * @param secret 生成密钥的密文 * @throws Exception */ public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret) throws Exception { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); SecureRandom secureRandom = new SecureRandom(secret.getBytes()); keyPairGenerator.initialize(1024, secureRandom); KeyPair keyPair = keyPairGenerator.genKeyPair(); // 获取公钥并写出 byte[] publicKeyBytes = keyPair.getPublic().getEncoded(); writeFile(publicKeyFilename, publicKeyBytes); // 获取私钥并写出 byte[] privateKeyBytes = keyPair.getPrivate().getEncoded(); writeFile(privateKeyFilename, privateKeyBytes); } private static byte[] readFile(String fileName) throws Exception { return Files.readAllBytes(new File(fileName).toPath()); } private static void writeFile(String destPath, byte[] bytes) throws IOException { File dest = new File(destPath); //创建父文件夹 if(!dest.getParentFile().exists()){ dest.getParentFile().mkdirs(); } //创建需要的文件 if (!dest.exists()) { dest.createNewFile(); } Files.write(dest.toPath(), bytes); } } 

2.1.1ツール

//生成公钥和私钥
RasUtils.generateKey(公钥位置,私钥位置,密码);
RasUtils.generateKey(pubKeyPath,priKeyPath,"234");
//获得公钥
RasUtils.getPublicKey(pubKeyPath);
//获得私钥
RasUtils.getPrivateKey(priKeyPath);
package com.czxy;

import com.czxy.utils.RasUtils;
import org.junit.Test;

import java.security.PrivateKey;
import java.security.PublicKey; /** * @author 庭前云落 * @Date 2019/12/13 22:07 * @description */ public class TestRAS { private static final String pugbKeyPath="D:\\ras\\ras.pub"; private static final String priKeyPath="D:\\ras\\ras.pri"; @Test public void testRas() throws Exception { //生产公钥和私钥 RasUtils.generateKey(pugbKeyPath,priKeyPath,"234"); } @Test public void testGetRas() throws Exception { //获得公钥和私钥 PublicKey publicKey = RasUtils.getPublicKey(pugbKeyPath); PrivateKey privateKey = RasUtils.getPrivateKey(priKeyPath); System.out.println(publicKey.toString()); System.out.println(privateKey.toString()); } } 

3、JWTツール

3.1概要

公式サイト:します。https://jwt.io JWTは、JSONは軽量スタイルの認可と認証の仕様、ステートレスを有効にする、認可分散Webアプリケーションで、JSONウェブトークンの略

  • JWT JSONベースの認証基準。(JSONウェブトークン)
  • 使用JWT目的:データを生成するために、分析データ

JWTを使用して3.2、

ポンポン

    <properties>
        <jwt.jjwt.version>0.9.0</jwt.jjwt.version> <jwt.joda.version>2.9.7</jwt.joda.version> <lombok.version>1.16.20</lombok.version> <beanutils.version>1.9.3</beanutils.version> </properties> <dependencies> <!--网关依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <!--添加eureka客户端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--测试--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <!--jwt依赖--> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>${beanutils.version}</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>${jwt.jjwt.version}</version> </dependency> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>${jwt.joda.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <scope>provided</scope> </dependency> </dependencies> 

インポートツール

ツール:JwtUtils

package com.czxy.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm; import org.apache.commons.beanutils.BeanUtils; import org.joda.time.DateTime; import java.beans.BeanInfo; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.security.PrivateKey; import java.security.PublicKey; /** * @author 庭前云落 * @Date 2019/12/13 22:01 * @description */ public class JwtUtils { /** * 私钥加密token * @param data 需要加密的数据(载荷内容) * @param expireMinutes 过期时间,单位:分钟 * @param privateKey 私钥 * @return */ public static String generateToken(Object data, int expireMinutes, PrivateKey privateKey) throws Exception { //1 获得jwt构建对象 JwtBuilder jwtBuilder = Jwts.builder(); //2 设置数据 if( data == null ) { throw new RuntimeException("数据不能为空"); } BeanInfo beanInfo = Introspector.getBeanInfo(data.getClass()); PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { // 获得属性名 String name = propertyDescriptor.getName(); // 获得属性值 Object value = propertyDescriptor.getReadMethod().invoke(data); if(value != null) { jwtBuilder.claim(name,value); } } //3 设置过期时间 jwtBuilder.setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate()); //4 设置加密 jwtBuilder.signWith(SignatureAlgorithm.RS256, privateKey); //5 构建 return jwtBuilder.compact(); } /** * 通过公钥解析token * @param token 需要解析的数据 * @param publicKey 公钥 * @param beanClass 封装的JavaBean * @return * @throws Exception */ public static <T> T getObjectFromToken(String token, PublicKey publicKey,Class<T> beanClass) throws Exception { //1 获得解析后内容 Claims body = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody(); //2 将内容封装到对象JavaBean T bean = beanClass.newInstance(); BeanInfo beanInfo = Introspector.getBeanInfo(beanClass); PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { // 获得属性名 String name = propertyDescriptor.getName(); // 通过属性名,获得对应解析的数据 Object value = body.get(name); if(value != null) { // 将获得的数据封装到对应的JavaBean中 BeanUtils.setProperty(bean,name,value); } } return bean; } } 

時間処理ツール:日時

//当前时间
DateTime.now().toDate().toLocaleString()
//当前时间加5分钟
DateTime.now().plusMinutes(5).toDate().toLocaleString()
//当前时间减5分钟
DateTime.now().minusMinutes(5).toDate().toLocaleString() 

3.3試験

//生成数据, UserInfo --> String(加密)
//JwtUtils.generateToken(数据,过期时间(分钟), 私钥)
String token = JwtUtils.generateToken(userInfo,30, RasUtils.getPrivateKey(priKeyPath));

//解析数据, String(加密) --> UserInfo
// JwtUtils.getObjectFromToken(加密数据, 公钥, 封装对象.class); UserInfo userInfo = JwtUtils.getObjectFromToken(token, RasUtils.getPublicKey(pubKeyPath), UserInfo.class); 
  • 生産トークン
package com.czxy;

import com.czxy.utils.RasUtils;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.joda.time.DateTime; import org.junit.Test; /** * @author 庭前云落 * @Date 2019/12/13 22:32 * @description */ public class TestJWT { private static final String pugbKeyPath="D:\\ras\\ras.pub"; private static final String priKeyPath="D:\\ras\\ras.pri"; @Test public void testGenerateToken() throws Exception { String str = Jwts.builder() .claim("test","庭前云落") .setExpiration(DateTime.now().plusMinutes(60).toDate()) .signWith(SignatureAlgorithm.RS256, RasUtils.getPrivateKey(priKeyPath)) .compact(); System.out.println(str); } } 
"庭前云落":Token ---》eyJhbGciOiJSUzI1NiJ9.eyJ0ZXN0Ijoi5bqt5YmN5LqR6JC9IiwiZXhwIjoxNTc2MjkzMTEyfQ.a32GamgbG6F1xC-4NtEJNNLX8mcV6Ycyc2bf7_7wX6_xa4LzimqO5ZH9d4bSii-IixYudSreurJ2Rjq72aXvv3nv_VsZasmODeLkBMLtBGhKDztKW3hNQM7rcRLIxL4PFP48xjosJl48F-hXSgEWqYXuC6Voexlk8W4eonRcGqg 

  • トークンの構文解析
    @Test
    public void testParseToken() throws Exception { String token="\n" + "eyJhbGciOiJSUzI1NiJ9.eyJ0ZXN0Ijoi5bqt5YmN5LqR6JC9IiwiZXhwIjoxNTc2MjkzMTEyfQ.a32GamgbG6F1xC-4NtEJNNLX8mcV6Ycyc2bf7_7wX6_xa4LzimqO5ZH9d4bSii-IixYudSreurJ2Rjq72aXvv3nv_VsZasmODeLkBMLtBGhKDztKW3hNQM7rcRLIxL4PFP48xjosJl48F-hXSgEWqYXuC6Voexlk8W4eonRcGqg"; Claims claims = Jwts.parser().setSigningKey(RasUtils.getPublicKey(pubKeyPath)). parseClaimsJws(token).getBody(); String text = claims.get("test",String.class); System.out.println(text); } 

4、生産およびトークン検証トークン

書き込まれたテスト対象のUserInfo

package com.czxy.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/** * @author 庭前云落 * @Date 2019/12/13 21:55 * @description */ @Data @AllArgsConstructor @NoArgsConstructor public class UserInfo { private Long id; private String username; } 

テスト

package com.czxy;

import com.czxy.domain.UserInfo;
import com.czxy.utils.JwtUtils;
import com.czxy.utils.RasUtils;
import org.junit.Test; /** * @author 庭前云落 * @Date 2019/12/13 22:32 * @description */ public class TestJWT { private static final String pubKeyPath="D:\\ras\\ras.pub"; private static final String priKeyPath="D:\\ras\\ras.pri"; @Test public void testToken() throws Exception { UserInfo userInfo = new UserInfo(); userInfo.setId(10L); userInfo.setUsername("庭前云落"); String token = JwtUtils.generateToken(userInfo, 30, RasUtils.getPrivateKey(priKeyPath)); System.out.println(token); } @Test public void testParserToken() throws Exception { String token="eyJhbGciOiJSUzI1NiJ9.eyJjbGFzcyI6ImNvbS5jenh5LmRvbWFpbi5Vc2VySW5mbyIsImlkIjoxMCwidXNlcm5hbWUiOiLluq3liY3kupHokL0iLCJleHAiOjE1NzYyOTI1Mzd9.LlyCCBeW4f7fjU3LmE7cA8W7aNB1BXp23Yv9WQJouCRCtoD46GiXQAHn2kezuzuPfp2u5G0OXOIeahHtnvRMSDjtQFJ6s-cZcKNupJPOPK8BzuEnladx0ilcrSr5TeWNxujg-svSz5EJRwWj8KbRKhQluohpAg0VhERjJjD5wTY"; UserInfo userInfo = JwtUtils.getObjectFromToken(token, RasUtils.getPublicKey(pubKeyPath), UserInfo.class); System.out.println(userInfo); } } 

5、JWTトークン組成

JWT的token包含三部分数据:头部、载荷、签名。

名称 描述 组成部分
头部(Header) 通常头部有两部分信息 1. 声明类型,这里是JW2. 加密算法,自定义
载荷(Payload) 就是有效数据 1. 用户身份信息2. 注册声明
签名(Signature) 整个数据的认证信息 一般根据前两步的数据,再加上服务的的密钥(secret),通过加密算法生成。用于验证整个数据完整和可靠性
  • 生成的数据格式

来源:http://www.1994july.club/seo/

おすすめ

転載: www.cnblogs.com/1994jinnan/p/12038589.html