服务网关Gateway实现用户鉴权_什么是JWT?
什么是JWT :
JWT是一种用于双方之间传递安全信息的简洁的、URL安全的声明规 范。定义了一种简洁的,自包含的方法用于通信双方之间以Json对 象的形式安全的传递信息。特别适用于分布式站点的单点登录 (SSO)场景.
传统的session认证 每次提到无状态的 JWT 时相信都会看到另一种基于 Session 的用户 认证方案介绍,这里也不例外,Session 的认证流程通常会像这 样:
缺点:
- 安全性:CSRF攻击因为基于cookie来进行用户识别, cookie如果被截获,用户就会很容易受 到跨站请求伪造的攻击。
- 扩展性:对于分布式应用,需要实现 session 数据共享
- 性能:每一个用户经过后端应用认证之后,后端应用都要在服务端做一次记录,以方便用户 下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开 销会明显增大,与REST风格不匹配。因为它在一个无状态协议里注入了状态。
JWT方式
优点:
- 无状态
- 适合移动端应用
- 单点登录友好
配置跳过验证路由yml文件配置
org: my: jwt: #跳过认证的路由 skipAuthUrls: - /user/login
创建Response相应类
@Data public class Response { //响应状态码 private Integer code; //相应信息 private String message; public Response(Integer code, String message) { this.code = code; this.message = message; } public Response() { } }
创建JWT工具类JWTUtils
public class JWTUtil { // 秘钥 public static final String SECRET_KEY = "erbadagang-123456"; // token过期时间 public static final long TOKEN_EXPIRE_TIME = 5 * 60 * 1000; // 签发人 private static final String ISSUER = "issuer"; // 用户名 private static final String USER_NAME = "username"; } //生成签名方法 public static String token(String username) { Date now = new Date(); //SECRET_KEY是用来加密数据签名秘钥 Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY); String token = JWT.create() // 签发人 .withIssuer(ISSUER) // 签发时间 .withIssuedAt(now) // 过期时间 .withExpiresAt(new Date(now.getTime() + TOKEN_EXPIRE_TIME)) // 保存权限标记 .withClaim(USER_NAME, username) .sign(algorithm); log.info("jwt generated user={}", username); return token; } //验证签名 public static boolean verify(String token) { try { //SECRET_KEY是用来加密数据签名秘钥 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; }
创建LoginGlobalFilter全局过滤器
@Data @Slf4j @Component @ConfigurationProperties("org.my.jwt") public class LoginGlobalFilter implements GlobalFilter, Ordered { // 跳过路由数组 private String[] skipAuthUrls; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //获取请求url地址 String url = exchange.getRequest().getURI().getPath(); //跳过不需要验证的路径 if (null != skipAuthUrls && isSkipUrl(url)) { return chain.filter(exchange); } //从请求头中取得token String token = exchange.getRequest().getHeaders().getFirst( "Authorization"); if (StringUtils.isEmpty(token)) { return createResponseObj(exchange,500,"token参数缺 失"); } //请求中的token是否有效 boolean verifyResult = JWTUtil.verify(token); if (!verifyResult) { return createResponseObj(exchange,500,"token 失效"); } //如果各种判断都通过,执行chain上的其他业 务逻辑 return chain.filter(exchange); } @Override public int getOrder() { return 0; } /** * 判断当前访问的url是否开头URI是在配置的忽略 url列表中 * * @param url * @return */ public boolean isSkipUrl(String url) { for (String skipAuthUrl : skipAuthUrls) { if (url.startsWith(skipAuthUrl)) { return true; } } return false; } // 组装返回数据 private Mono<Void> createResponseObj(ServerWebExchange exchange,Integer code,String message){ ServerHttpResponse response = exchange.getResponse(); // 设置响应状态码200 response.setStatusCode(HttpStatus.OK); // 设置响应头 response.getHeaders().add("ContentType", "application/json;charset=UTF-8"); // 创建响应对象 Response res = new Response(code, message); // 把对象转成字符串 byte[] responseByte = JSONObject.toJSONString(res).toString().getB ytes(StandardCharsets.UTF_8); DataBuffer buffer = response.bufferFactory().wrap(responseByte); return response.writeWith(Flux.just(buffer)); } }
测试