如何使用JWT對SpringCloud進行認證和鑒權(quán)

這篇文章主要為大家展示了“如何使用JWT對SpringCloud進行認證和鑒權(quán)”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學習一下“如何使用JWT對SpringCloud進行認證和鑒權(quán)”這篇文章吧。

創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供武岡網(wǎng)站建設(shè)、武岡做網(wǎng)站、武岡網(wǎng)站設(shè)計、武岡網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、武岡企業(yè)網(wǎng)站模板建站服務(wù),十年武岡做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。

JWT(JSON WEB TOKEN)是基于RFC 7519標準定義的一種可以安全傳輸?shù)男∏珊妥园腏SON對象。由于數(shù)據(jù)是使用數(shù)字簽名的,所以是可信任的和安全的。JWT可以使用HMAC算法對secret進行加密或者使用RSA的公鑰私鑰對來進行簽名。

JWT通常由頭部(Header),負載(Payload),簽名(Signature)三個部分組成,中間以.號分隔,其格式為Header.Payload.Signature

Header:聲明令牌的類型和使用的算法

  • alg:簽名的算法

  • typ:token的類型,比如JWT

Payload:也稱為JWT Claims,包含用戶的一些信息

系統(tǒng)保留的聲明(Reserved claims):

  • iss (issuer):簽發(fā)人

  • exp (expiration time):過期時間

  • sub (subject):主題

  • aud (audience):受眾用戶

  • nbf (Not Before):在此之前不可用

  • iat (Issued At):簽發(fā)時間

  •  jti (JWT ID):JWT唯一標識,能用于防止JWT重復使用

公共的聲明(public):見 http://www.iana.org/assignments/jwt/jwt.xhtml

私有的聲明(private claims):根據(jù)業(yè)務(wù)需要自己定義的數(shù)據(jù)

Signature:簽名

簽名格式: HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

JWT的特點:

  • JWT默認是不加密的,不能把用戶敏感類信息放在Payload部分。

  • JWT 不僅可以用于認證,也可以用于交換信息。

  • JWT的最大缺點是服務(wù)器不保存會話狀態(tài),所以在使用期間不可能取消令牌或更改令牌的權(quán)限。

  • JWT本身包含認證信息,為了減少盜用,JWT的有效期不宜設(shè)置太長。

  • 為了減少盜用和竊取,JWT不建議使用HTTP協(xié)議來傳輸代碼,而是使用加密的HTTPS協(xié)議進行傳輸。

  • 首次生成token比較慢,比較耗CPU,在高并發(fā)的情況下需要考慮CPU占用問題。

  • 生成的token比較長,可能需要考慮流量問題。

認證原理:

  • 客戶端向服務(wù)器申請授權(quán),服務(wù)器認證以后,生成一個token字符串并返回給客戶端,此后客戶端在請求

  • 受保護的資源時攜帶這個token,服務(wù)端進行驗證再從這個token中解析出用戶的身份信息。

JWT的使用方式:一種做法是放在HTTP請求的頭信息Authorization字段里面,格式如下:

Authorization: <token>

 需要將服務(wù)器設(shè)置為接受來自所有域的請求,用Access-Control-Allow-Origin: *

  另一種做法是,跨域的時候,JWT就放在POST請求的數(shù)據(jù)體里面。

對JWT實現(xiàn)token續(xù)簽的做法:

1、額外生成一個refreshToken用于獲取新token,refreshToken需存儲于服務(wù)端,其過期時間比JWT的過期時間要稍長。

2、用戶攜帶refreshToken參數(shù)請求token刷新接口,服務(wù)端在判斷refreshToken未過期后,取出關(guān)聯(lián)的用戶信息和當前token。

3、使用當前用戶信息重新生成token,并將舊的token置于黑名單中,返回新的token。

創(chuàng)建用于登錄認證的工程auth-service:

1、 創(chuàng)建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"; //簽發(fā)人 
 
  /** 
   * 生成簽名 
   */ 
  public static String generateToken(String username){ 
    Date now = new Date(); 
    Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY); //算法 
     
    String token = JWT.create() 
      .withIssuer(ISSUER) //簽發(fā)人 
      .withIssuedAt(now) //簽發(fā)時間 
      .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、LoginController類

@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(); 
       
      //數(shù)據(jù)放入redis 
      redisTemplate.opsForHash().put(refreshToken, "token", token); 
      redisTemplate.opsForHash().put(refreshToken, "username", username); 
       
      //設(shè)置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、application配置信息

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); 
  } 
}

改造SpringCloud 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、創(chuàng)建全局過濾器JWTAuthFilter

@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、關(guān)鍵的application配置信息

spring: 
 application: 
  name: service-gateway 
 cloud: 
  gateway: 
   discovery: 
    locator: 
     enabled: true 
     lowerCaseServiceId: true 
   routes: 
    #認證服務(wù)路由 
    - id: auth-service 
     predicates: 
      - Path=/auth-service/** 
     uri: lb://auth-service 
     filters: 
      - StripPrefix=1

以上是“如何使用JWT對SpringCloud進行認證和鑒權(quán)”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學習更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!

本文標題:如何使用JWT對SpringCloud進行認證和鑒權(quán)
本文網(wǎng)址:http://muchs.cn/article8/isjiip.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設(shè)網(wǎng)頁設(shè)計公司、移動網(wǎng)站建設(shè)、網(wǎng)站排名、定制開發(fā)品牌網(wǎng)站設(shè)計

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

小程序開發(fā)