詳しくはJWTでSprigCloudを認証し、認証する。


JWT(JSON WEB TOKEN)は、RFC 7519規格に基づいて定義された、安全に伝送できるコンパクトさと、自己が含むJSONオブジェクトである。データはデジタル署名を使用していますので、信頼性と安全性があります。JWTは、HMACアルゴリズムを使用してsecretを暗号化したり、RSAの公開鍵秘密鍵ペアを使用して署名したりすることができる。
JWTは通常ヘッド、ロード、署名の三つの部分から構成され、中間は.号で分離され、そのフォーマットはHeader.Payload.Signatureである。
Header:トークンの種類と使用アルゴリズムを宣言する。
  • alg:署名のアルゴリズム
  • typ:tokenのタイプ、例えばJWT
  • Payload:JWT Cliaimsともいいます。ユーザーの情報を含みます。
    システムが保持している宣言(Reserved claims):
  • iss:署名者
  • exp(expiration time):賞味期限
  • sub(subject):テーマ
  • aud(audiience):アクセプター
  • nbf(Not Before):これまでは
  • は利用できません。
  • iat(Issued At):署名時間
  •  jti(JWT ID):JWT固有の識別は、JWTが
  • を繰り返し使用することを防止するために使用できます。
    公的声明(public):http://www.iana.org/assignments/jwt/jwt.xhtmlを参照してください。
    プライベートステートメント(prvate claims):ビジネスニーズに応じて自分で定義したデータ
    Signature:署名
    署名のフォーマット: HMACSHA256(base 64 UrlEnccode+「.」+base 64 UrlEnccode、secret)
    JWTの特徴:
  • JWTはデフォルトでは暗号化されていないので、ユーザーの敏感な情報をPayloadの部分に置くことはできません。
  • JWTは認証のためだけでなく、情報交換のためにも利用できます。
  • JWTの最大の欠点は、サーバがセッション状態を保存しないことであるので、使用中にトークンをキャンセルしたり、トークンの権限を変更したりすることは不可能である。
  • JWT自身は認証情報を含んでいます。盗用を減らすために、JWTの有効期間は長く設定するべきではありません。
  • は、盗用と盗用を低減するために、JWTはHTTPプロトコルを使用してコードを送信することを提案しない代わりに、暗号化されたHTTPSプロトコルを用いて伝送する。
  • 初めてtokenを生成するのは比較的に遅く、CPUを消耗し、高合併の場合はCPU占有問題を考慮する必要がある。
  • によって生成されたtokenは比較的に長く、流量問題を考慮する必要があるかもしれない。
  • 認証の原理:
  • クライアントはサーバにライセンスを申請し、サーバ認証後、token文字列を生成してクライアントに戻り、その後クライアントは
  • を要求する。
  • 保護されたリソースは、このtokenを携帯し、サービス側で検証し、このtokenからユーザのアイデンティティ情報を解析する。
  • JWTの使い方:HTTP要求のヘッダ情報Authorzationフィールドに入れる方法で、フォーマットは以下の通りである。Authorization: <token> すべてのドメインからの要求を受け入れるようにサーバーを設定する必要があります。Access-Coontrol-Origin:*
      もう一つの方法は、ドメインをまたぐ時、JWTはPOSTが要求したデータ体に入れます。
    JWTに対してtoken更新を実現する方法:
    1、新たなtokenを取得するためにrefshTokenを追加的に生成し、refresh Tokenはサービス端末に保存する必要があり、その期限がJWTの期限切れより少し長い。
    2、ユーザはrefreshTokenパラメータ要求tokenを携帯してインターフェースを更新し、サービス端末はrefshTokenが期限切れしていないと判断した後、関連するユーザ情報と現在tokenを取り出す。
    3、現在のユーザ情報を用いてtokenを再生成し、古いtokenをブラックリストにセットして、新しいtokenを返す。
    登録認証用のプロジェクトauth-serviceを作成します。
    1、pom.xmlファイルを作成する
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 
     <modelVersion>4.0.0</modelVersion> 
     <groupId>com.seasy.springcloud</groupId> 
     <artifactId>auth-service</artifactId> 
     <version>1.0.0</version> 
     <packaging>jar</packaging> 
      
     <parent> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-parent</artifactId> 
      <version>2.0.8.RELEASE</version> 
      <relativePath/> 
     </parent> 
     
     <properties> 
      <java.version>1.8</java.version> 
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> 
     </properties> 
      
     <dependencies> 
      <dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-web</artifactId> 
      </dependency> 
      <dependency> 
        <groupId>org.springframework.boot</groupId> 
        <artifactId>spring-boot-starter-actuator</artifactId> 
      </dependency> 
       
      <!-- spring cloud --> 
      <dependency> 
        <groupId>org.springframework.cloud</groupId> 
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 
      </dependency> 
       
      <!-- redis --> 
      <dependency> 
        <groupId>org.springframework.boot</groupId> 
        <artifactId>spring-boot-starter-data-redis</artifactId> 
      </dependency> 
      <dependency> 
        <groupId>org.apache.commons</groupId> 
        <artifactId>commons-pool2</artifactId> 
      </dependency> 
       
      <!-- jwt --> 
      <dependency> 
        <groupId>com.auth0</groupId> 
        <artifactId>java-jwt</artifactId> 
        <version>3.7.0</version> 
      </dependency> 
     </dependencies> 
      
     <dependencyManagement> 
      <dependencies> 
        <dependency> 
          <groupId>org.springframework.cloud</groupId> 
          <artifactId>spring-cloud-dependencies</artifactId> 
          <version>Finchley.RELEASE</version> 
          <type>pom</type> 
          <scope>import</scope> 
        </dependency> 
      </dependencies> 
     </dependencyManagement> 
    </project> 
    2、JWTツール類
    
    public class JWTUtil { 
      public static final String SECRET_KEY = "123456"; //   
      public static final long TOKEN_EXPIRE_TIME = 5 * 60 * 1000; //token     
      public static final long REFRESH_TOKEN_EXPIRE_TIME = 10 * 60 * 1000; //refreshToken     
      private static final String ISSUER = "issuer"; //    
     
      /** 
       *      
       */ 
      public static String generateToken(String username){ 
        Date now = new Date(); 
        Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY); //   
         
        String token = JWT.create() 
          .withIssuer(ISSUER) //    
          .withIssuedAt(now) //     
          .withExpiresAt(new Date(now.getTime() + TOKEN_EXPIRE_TIME)) //     
          .withClaim("username", username) //       
          .sign(algorithm); 
        return token; 
      } 
       
      /** 
       *   token 
       */ 
      public static boolean verify(String token){ 
        try { 
          Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY); //   
          JWTVerifier verifier = JWT.require(algorithm) 
              .withIssuer(ISSUER) 
              .build(); 
          verifier.verify(token); 
          return true; 
        } catch (Exception ex){ 
          ex.printStackTrace(); 
        } 
        return false; 
      } 
       
      /** 
       *  token  username 
       */ 
      public static String getUsername(String token){ 
        try{ 
          return JWT.decode(token).getClaim("username").asString(); 
        }catch(Exception ex){ 
          ex.printStackTrace(); 
        } 
        return ""; 
      } 
    }
    3、Login Controller類
    
    @RestController 
    public class LoginController { 
      @Autowired 
      StringRedisTemplate redisTemplate; 
       
      /** 
       *      
       * @param username     
       * @param password    
       */ 
      @GetMapping("/login") 
      public AuthResult login(@RequestParam String username, @RequestParam String password) { 
        if("admin".equals(username) && "admin".equals(password)){ 
          //  token 
          String token = JWTUtil.generateToken(username); 
           
          //  refreshToken 
          String refreshToken = StringUtil.getUUIDString(); 
           
          //    redis 
          redisTemplate.opsForHash().put(refreshToken, "token", token); 
          redisTemplate.opsForHash().put(refreshToken, "username", username); 
           
          //  token      
          redisTemplate.expire(refreshToken, JWTUtil.REFRESH_TOKEN_EXPIRE_TIME, TimeUnit.MILLISECONDS); 
           
          return new AuthResult(0, "success", token, refreshToken); 
        }else{ 
          return new AuthResult(1001, "username or password error"); 
        } 
      } 
       
      /** 
       *   token 
       */ 
      @GetMapping("/refreshToken") 
      public AuthResult refreshToken(@RequestParam String refreshToken) { 
        String username = (String)redisTemplate.opsForHash().get(refreshToken, "username"); 
        if(StringUtil.isEmpty(username)){ 
          return new AuthResult(1003, "refreshToken error"); 
        } 
     
        //    token 
        String newToken = JWTUtil.generateToken(username); 
        redisTemplate.opsForHash().put(refreshToken, "token", newToken); 
        return new AuthResult(0, "success", newToken, refreshToken); 
      } 
     
      @GetMapping("/") 
      public String index() { 
        return "auth-service: " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); 
      } 
    } 
    4、アプリの設定情報
    
    spring.application.name=auth-service 
    server.port=4040 
     
    eureka.instance.hostname=${spring.cloud.client.ip-address} 
    eureka.instance.instance-id=${spring.cloud.client.ip-address}:${server.port} 
    eureka.instance.prefer-ip-address=true 
     
    eureka.client.service-url.defaultZone=http://root:123456@${eureka.instance.hostname}:7001/eureka/ 
     
    #redis 
    spring.redis.database=0 
    spring.redis.timeout=3000ms 
    spring.redis.lettuce.pool.max-active=100 
    spring.redis.lettuce.pool.max-wait=-1ms 
    spring.redis.lettuce.pool.min-idle=0 
    spring.redis.lettuce.pool.max-idle=8 
     
    #standalone 
    spring.redis.host=192.168.134.134 
    spring.redis.port=7001 
     
    #sentinel 
    #spring.redis.sentinel.master=mymaster 
    #spring.redis.sentinel.nodes=192.168.134.134:26379,192.168.134.134:26380 
    5、起動クラス
    
    @SpringBootApplication 
    @EnableEurekaClient 
    public class Main{ 
      public static void main(String[] args){ 
        SpringApplication.run(Main.class, args); 
      } 
    } 
    
    
    SprigCloud Gatewayを改造するプロジェクト
    1、pom.xmlファイルに依存を追加する
    
    <!-- redis --> 
    <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-data-redis</artifactId> 
    </dependency> 
    <dependency> 
      <groupId>org.apache.commons</groupId> 
      <artifactId>commons-pool2</artifactId> 
    </dependency> 
     
    <!-- jwt --> 
    <dependency> 
      <groupId>com.auth0</groupId> 
      <artifactId>java-jwt</artifactId> 
      <version>3.7.0</version> 
    </dependency>
    2、グローバルフィルタJWT AuthFilterを作成する
    
    @Component 
    public class JWTAuthFilter implements GlobalFilter, Ordered{ 
      @Override 
      public int getOrder() { 
        return -100; 
      } 
       
      @Override 
      public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { 
        String url = exchange.getRequest().getURI().getPath(); 
         
        //    url   
        if(url.indexOf("/auth-service/") >= 0){ 
          return chain.filter(exchange); 
        } 
         
        //       token 
        String token = exchange.getRequest().getHeaders().getFirst("Authorization"); 
        if(StringUtil.isEmpty(token)){ 
          ServerHttpResponse response = exchange.getResponse(); 
          response.setStatusCode(HttpStatus.OK); 
          response.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); 
           
          Response res = new Response(401, "401 unauthorized"); 
          byte[] responseByte = JSONObject.fromObject(res).toString().getBytes(StandardCharsets.UTF_8); 
           
          DataBuffer buffer = response.bufferFactory().wrap(responseByte); 
          return response.writeWith(Flux.just(buffer)); 
        } 
         
        //    token   redis    
        boolean verifyResult = JWTUtil.verify(token); 
        if(!verifyResult){ 
          ServerHttpResponse response = exchange.getResponse(); 
          response.setStatusCode(HttpStatus.OK); 
          response.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); 
     
          Response res = new Response(1004, "invalid token"); 
          byte[] responseByte = JSONObject.fromObject(res).toString().getBytes(StandardCharsets.UTF_8); 
           
          DataBuffer buffer = response.bufferFactory().wrap(responseByte); 
          return response.writeWith(Flux.just(buffer)); 
        } 
         
        return chain.filter(exchange); 
      } 
    } 
    3、キーのアプリ配置情報
    
    spring: 
     application: 
      name: service-gateway 
     cloud: 
      gateway: 
       discovery: 
        locator: 
         enabled: true 
         lowerCaseServiceId: true 
       routes: 
        #       
        - id: auth-service 
         predicates: 
          - Path=/auth-service/** 
         uri: lb://auth-service 
         filters: 
          - StripPrefix=1
    以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。