您的位置:首页 > 编程语言 > Java开发

Spring Cloud Gateway——(三)微服务网关实现JWT鉴权

2020-01-11 18:09 1501 查看

Spring Cloud Gateway——(三)微服务网关实现JWT鉴权

1. JWT 实现微服务鉴权

1.1 什么是JWT?

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。

  • JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。

    头部(Header)

    头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。

  • 载荷(playload)

    载荷就是存放有效信息的地方

  • 签证(signature)

    这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

1.2 JJWT签发与验证token

JJWT是一个提供端到端的JWT创建和验证的Java库。永远免费和开源(Apache License,版本2.0),JJWT很容易使用和理解。它被设计成一个以建筑为中心的流畅界面,隐藏了它的大部分复杂性。
官方文档:https://github.com/jwtk/jjwt

1.3 JJWT快速入门

  1. 在项目中添加依赖

    <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
    </dependency>
  2. 创建测试类生成token

    JwtBuilder builder= Jwts.builder()
    .setId("007") //设置唯一编号
    .setSubject("小马")//设置主题 可以是JSON数据
    .setIssuedAt(new Date())//设置签发日期
    .setExpiration(new Date(System.currentTimeMillis()+3600))//设置过期时间
    .signWith(SignatureAlgorithm.HS256,"xm");//设置签名 使用HS256算法,并设置SecretKey(字符串)
    //构建 并返回一个字符串
    System.out.println( builder.compact() );
  3. 解析token

    String compactJwt="eyJhbGciOiJIUzI1NiJ9.eyJqdGk...之前生成的token...";
    Claims claims =Jwts.parser()
    .setSigningKey("xm")
    .parseClaimsJws(compactJwt)
    .getBody();
    System.out.println(claims);

1.4 在微服务中实现jjwt鉴权

(1)在网关服务和用户登录服务中添加依赖依赖

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>

(2)在服务中创建类: JwtUtil 用于jjwt token生成和解析

package com.xm.system.util;

import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;

/**
* @title: JwtUtil  JWT工具类
* @projectName:
* @description: TODO
* @author: Zack_Tzh
* @date: 2019/12/10  9:31
*/
public class JwtUtil {

/**
* 设置有效期为 60 * 60 秒
*/
private static final Long JWT_TTL = 3600000L;
/**
* 设置秘钥明文
*/
private static final String JWT_KEY= "NVIDIA";

/**
* 创建toke
* @param id
* @param subject
* @param ttlMillis
* @return
*/
public static String createJWT(String id,String subject,Long ttlMillis){
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
if(ttlMillis==null){
ttlMillis=JwtUtil.JWT_TTL;
}
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
SecretKey secretKey = generalKey();

JwtBuilder builder = Jwts.builder()
//唯一的ID
.setId(id)
// 主题  可以是JSON数据
.setSubject(subject)
// 签发者
.setIssuer("admin")
// 签发时间
.setIssuedAt(now)
//使用HS256对称加密算法签名, 第二个参数为秘钥
.signWith(signatureAlgorithm, secretKey)
// 设置过期时间
.setExpiration(expDate);
return builder.compact();
}
/**
* 解析JWT
* @param compactJwt
* @return
* @throws Exception
*/
public static Claims parseJWT(String  compactJwt) throws Exception {
SecretKey secretKey = generalKey();
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws( compactJwt).getBody();
}
/**
* 生成加密后的秘钥 secretKey
* @return
*/
public static SecretKey generalKey() {
byte[] encodedKey = Base64.getEncoder().encode(JwtUtil.JWT_KEY.getBytes());
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
}

(3)在用户登录的login方法时,用户登录成功 则 签发TOKEN

/**
* 登录
* @param admin
* @return
*/
@PostMapping("/login")
public Result login(@RequestBody Admin admin){
boolean result = adminService.login(admin);//验证密码是否正确
if (result){
//密码是正确的
//生成jwt令牌,返回到客户端
Map<String,String> info = new HashMap<>();
info.put("username",admin.getLoginName());
//基于工具类生成jwt令牌
String jwt = JwtUtil.createJWT(UUID.randomUUID().toString(), admin.getLoginName(), null);
info.put("token",jwt);
return new Result(true, StatusCodeEnum.OK,info);
}else{
//密码错误
return new Result(false, StatusCodeEnum.ERROR);
}
}

(3)在网关创建过滤器,用于token验证

package com.xm.system.filter;

import com.xm.system.util.JwtUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
* @title: AuthorizeFilter
* @projectName: xm
* @description: TODO
* @author: Zack_Tzh
* @date: 2019/12/10  14:46
*/
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1. 获取请求
ServerHttpRequest request = exchange.getRequest();
//2. 则获取响应
ServerHttpResponse response = exchange.getResponse();
//3. 如果是登录请求则放行
if (request.getURI().getPath().contains("/admin/login")) {
return chain.filter(exchange);
}
//4. 获取请求头
HttpHeaders headers = request.getHeaders();
//5. 请求头中获取令牌
String token = headers.getFirst("token");
//6. 判断请求头中是否有令牌
if (StringUtils.isEmpty(token)) {
//7. 响应中放入返回的状态吗, 没有权限访问
response.setStatusCode(HttpStatus.UNAUTHORIZED);
//8. 返回
return response.setComplete();
}
//9. 如果请求头中有令牌则解析令牌
try {
JwtUtil.parseJWT(token);
} catch (Exception e) {
e.printStackTrace();
//10. 解析jwt令牌出错, 说明令牌过期或者伪造等不合法情况出现
response.setStatusCode(HttpStatus.UNAUTHORIZED);
//11. 返回
return response.setComplete();
}
//12. 放行
return chain.filter(exchange);
}

@Override
public int getOrder() {
return 0;
}
}
  • 点赞
  • 收藏
  • 分享
  • 文章举报
Zack_tzh 发布了11 篇原创文章 · 获赞 2 · 访问量 3315 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: