這篇文章主要為大家展示了“如何使用JWT對(duì)SpringCloud進(jìn)行認(rèn)證和鑒權(quán)”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“如何使用JWT對(duì)SpringCloud進(jìn)行認(rèn)證和鑒權(quán)”這篇文章吧。
創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供武岡網(wǎng)站建設(shè)、武岡做網(wǎng)站、武岡網(wǎng)站設(shè)計(jì)、武岡網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)與制作、武岡企業(yè)網(wǎng)站模板建站服務(wù),十年武岡做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。
JWT(JSON WEB TOKEN)是基于RFC 7519標(biāo)準(zhǔn)定義的一種可以安全傳輸?shù)男∏珊妥园腏SON對(duì)象。由于數(shù)據(jù)是使用數(shù)字簽名的,所以是可信任的和安全的。JWT可以使用HMAC算法對(duì)secret進(jìn)行加密或者使用RSA的公鑰私鑰對(duì)來進(jìn)行簽名。
JWT通常由頭部(Header),負(fù)載(Payload),簽名(Signature)三個(gè)部分組成,中間以.號(hào)分隔,其格式為Header.Payload.Signature
Header:聲明令牌的類型和使用的算法
alg:簽名的算法
typ:token的類型,比如JWT
Payload:也稱為JWT Claims,包含用戶的一些信息
系統(tǒng)保留的聲明(Reserved claims):
iss (issuer):簽發(fā)人
exp (expiration time):過期時(shí)間
sub (subject):主題
aud (audience):受眾用戶
nbf (Not Before):在此之前不可用
iat (Issued At):簽發(fā)時(shí)間
jti (JWT ID):JWT唯一標(biāo)識(shí),能用于防止JWT重復(fù)使用
公共的聲明(public):見 http://www.iana.org/assignments/jwt/jwt.xhtml
私有的聲明(private claims):根據(jù)業(yè)務(wù)需要自己定義的數(shù)據(jù)
Signature:簽名
簽名格式: HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
JWT的特點(diǎn):
JWT默認(rèn)是不加密的,不能把用戶敏感類信息放在Payload部分。
JWT 不僅可以用于認(rèn)證,也可以用于交換信息。
JWT的最大缺點(diǎn)是服務(wù)器不保存會(huì)話狀態(tài),所以在使用期間不可能取消令牌或更改令牌的權(quán)限。
JWT本身包含認(rèn)證信息,為了減少盜用,JWT的有效期不宜設(shè)置太長。
為了減少盜用和竊取,JWT不建議使用HTTP協(xié)議來傳輸代碼,而是使用加密的HTTPS協(xié)議進(jìn)行傳輸。
首次生成token比較慢,比較耗CPU,在高并發(fā)的情況下需要考慮CPU占用問題。
生成的token比較長,可能需要考慮流量問題。
認(rèn)證原理:
客戶端向服務(wù)器申請(qǐng)授權(quán),服務(wù)器認(rèn)證以后,生成一個(gè)token字符串并返回給客戶端,此后客戶端在請(qǐng)求
受保護(hù)的資源時(shí)攜帶這個(gè)token,服務(wù)端進(jìn)行驗(yàn)證再從這個(gè)token中解析出用戶的身份信息。
JWT的使用方式:一種做法是放在HTTP請(qǐng)求的頭信息Authorization字段里面,格式如下:
Authorization:
需要將服務(wù)器設(shè)置為接受來自所有域的請(qǐng)求,用Access-Control-Allow-Origin: *
另一種做法是,跨域的時(shí)候,JWT就放在POST請(qǐng)求的數(shù)據(jù)體里面。
對(duì)JWT實(shí)現(xiàn)token續(xù)簽的做法:
1、額外生成一個(gè)refreshToken用于獲取新token,refreshToken需存儲(chǔ)于服務(wù)端,其過期時(shí)間比JWT的過期時(shí)間要稍長。
2、用戶攜帶refreshToken參數(shù)請(qǐng)求token刷新接口,服務(wù)端在判斷refreshToken未過期后,取出關(guān)聯(lián)的用戶信息和當(dāng)前token。
3、使用當(dāng)前用戶信息重新生成token,并將舊的token置于黑名單中,返回新的token。
創(chuàng)建用于登錄認(rèn)證的工程auth-service:
1、 創(chuàng)建pom.xml文件
4.0.0 com.seasy.springcloud auth-service 1.0.0 jar org.springframework.boot spring-boot-starter-parent 2.0.8.RELEASE 1.8 UTF-8 UTF-8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.boot spring-boot-starter-data-redis org.apache.commons commons-pool2 com.auth0 java-jwt 3.7.0 org.springframework.cloud spring-cloud-dependencies Finchley.RELEASE pom import
2、JWT工具類
public class JWTUtil { public static final String SECRET_KEY = "123456"; //秘鑰 public static final long TOKEN_EXPIRE_TIME = 5 * 60 * 1000; //token過期時(shí)間 public static final long REFRESH_TOKEN_EXPIRE_TIME = 10 * 60 * 1000; //refreshToken過期時(shí)間 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ā)時(shí)間 .withExpiresAt(new Date(now.getTime() + TOKEN_EXPIRE_TIME)) //過期時(shí)間 .withClaim("username", username) //保存身份標(biāo)識(shí) .sign(algorithm); return token; } /** * 驗(yàn)證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; /** * 登錄認(rèn)證 * @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的過期時(shí)間 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、啟動(dòng)類
@SpringBootApplication @EnableEurekaClient public class Main{ public static void main(String[] args){ SpringApplication.run(Main.class, args); } }
改造SpringCloud Gateway工程
1、在pom.xml文件添加依賴
org.springframework.boot spring-boot-starter-data-redis org.apache.commons commons-pool2 com.auth0 java-jwt 3.7.0
2、創(chuàng)建全局過濾器JWTAuthFilter
@Component public class JWTAuthFilter implements GlobalFilter, Ordered{ @Override public int getOrder() { return -100; } @Override public Monofilter(ServerWebExchange exchange, GatewayFilterChain chain) { String url = exchange.getRequest().getURI().getPath(); //忽略以下url請(qǐng)求 if(url.indexOf("/auth-service/") >= 0){ return chain.filter(exchange); } //從請(qǐng)求頭中取得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)); } //請(qǐng)求中的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: #認(rèn)證服務(wù)路由 - id: auth-service predicates: - Path=/auth-service/** uri: lb://auth-service filters: - StripPrefix=1
以上是“如何使用JWT對(duì)SpringCloud進(jìn)行認(rèn)證和鑒權(quán)”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!