微服务网关与用户身份识别,JWT+Spring Security进行网关安全认证

本文详细介绍了如何使用JWT和Spring Security进行微服务网关的安全认证。用户登录时,UAA服务生成JWT令牌发送给前端,前端在请求微服务时将令牌放入请求头。Zuul网关结合Spring Security验证JWT有效性,确保安全访问。此外,讨论了JWT令牌的结构和认证流程,以及Zuul如何与UAA微服务协同处理会话共享。
摘要由CSDN通过智能技术生成

JWT+Spring Security进行网关安全认证

JWT和Spring Security相结合进行系统安全认证是目前使用比较多的一种安全认证组合。疯狂创客圈crazy-springcloud微服务开发脚手架使用JWT身份令牌结合Spring Security的安全认证机制完成用户请求的安全权限认证。整个用户认证的过程大致如下:

(1)前台(如网页富客户端)通过REST接口将用户名和密码发送到UAA用户账号与认证微服务进行登录。

(2)UAA服务在完成登录流程后,将Session ID作为JWT的负载(payload),生成JWT身份令牌后发送给前台。

(3)前台可以将JWT令牌存到localStorage或者sessionStorage中,当然,退出登录时,前端必须删除保存的JWT令牌。

(4)前台每次在请求微服务提供者的REST资源时,将JWT令牌放到请求头中。crazy-springcloud脚手架做了管理端和用户端的前台区分,管理端前台的令牌头为Authorization,用户端前台的令牌头为token。

(5)在请求到达Zuul网关时,Zuul会结合Spring Security进行拦截,从而验证JWT的有效性。

(6)Zuul验证通过后才可以访问微服务所提供的REST资源。

需要说明的是,在crazy-springcloud微服务开发脚手架中,Provider微服务提供者自身不需要进行单独的安全认证,Provider之间的内部远程调用也是不需要安全认证的,安全认证全部由网关负责。严格来说,这套安全机制是能够满足一般的生产场景安全认证要求的。如果觉得这个安全级别不是太高,单个的Provider微服务也需要进行独立的安全认证,那么实现起来也是很容易的,只需要导入公共的安全认证模块base-auth即可。实际上早期的crazy-springcloud脚手架也是这样做的,后期发现这样做纯属多虑,而且大大降低了Provider服务提供者模块的可复用性和可移植性(这是微服务架构的巨大优势之一)。所以,crazy-springcloud后来将整体架构调整为由网关(如Zuul或者Nginx)负责安全认证,去掉了Provider服务提供者的安全认证能力。

JWT安全令牌规范详解

JWT(JSON Web Token)是一种用户凭证的编码规范,是一种网络环境下编码用户凭证的JSON格式的开放标准(RFC 7519)。JWT令牌的格式被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)、用户身份认证等场景。

一个编码之后的JWT令牌字符串分为三部分:header+payload+signature。这三部分通过点号“.”连接,第一部分常被称为头部(header),第二部分常被称为负载(payload),第三部分常被称为签名(signature)。

1.JWT的header

编码之前的JWT的header部分采用JSON格式,一个完整的头部就像如下的JSON内容:

{
 "typ":"JWT",
 "alg":"HS256"
}

其中,"typ"是type(类型)的简写,值为"JWT"代表JWT类型;"alg"是加密算法的简写,值为"HS256"代表加密方式为HS256。

采用JWT令牌编码时,header的JSON字符串将进行Base64编码,编码之后的字符串构成了JWT令牌的第一部分。

2.JWT的playload

编码之前的JWT的playload部分也是采用JSON格式,playload是存放有效信息的部分,一个简单的playload就像如下的JSON内容:

{
 "sub":"session id",
 "exp":1579315717,
 "iat":1578451717
}

采用JWT令牌编码时,playload的JSON字符串将进行Base64编码,编码之后的字符串构成了JWT令牌的第二部分。

3.JWT的signature

JWT的第三部分是一个签名字符串,这一部分是将header的Base64编码和payload的Base64编码使用点号(.)连接起来之后,通过header声明的加密算法进行加密所得到的密文。为了保证安全,加密时需要加入盐(salt)。

下面是一个演示用例:用Java代码生成JWT令牌,然后对令牌的header部分字符串和payload部分字符串进行Base64解码,并输出解码后的JSON。

package com.crazymaker.demo.auth;
//省略import
@Slf4j
public class JwtDemo
{
 @Test
 public void testBaseJWT()
 {
 try
 {
 /**
 *JWT的演示内容
 */
 String subject = "session id";
 /**
 *签名的加密盐
 */
 String salt = "user password";
 /**
 *签名的加密算法
 */
 Algorithm algorithm = Algorithm.HMAC256(salt);
 //签发时间
 long start = System.currentTimeMillis() - 60000;
 //过期时间,在签发时间的基础上加上一个有效时长
 Date end = new Date(start + SessionConstants.SESSION_TIME_OUT *1000);
 /**
 *获取编码后的JWT令牌
 */
 String token = JWT.create()
 .withSubject(subject)
 .withIssuedAt(new Date(start))
 .withExpiresAt(end)
 .sign(algorithm);
 log.info("token=" + token);
 //编码后输出demo为:
 //token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzZXNza
W9uIGlkIiwiZXhwIjoxNTc5MzE1NzE3LCJpYXQiOjE1Nzg0NTE3MTd9.iANh9Fa0B_6H5TQ11bLCWcEpmWxuCwa2Rt6rnzBWteI
 //以.分隔令牌
 String[] parts = token.split("\\." );
 /**
 *对第一部分和第二部分进行解码
 *解码后的第一部分:header
 */
 String headerJson =
StringUtils.newStringUtf8(Base64.decodeBase64(parts[0]));
 log.info("parts[0]=" + headerJson);
 //解码后的第一部分输出的示例为: //parts[0]={"typ":"JWT","alg":"HS256"}
 /**
 *解码后的第二部分:payload
 */
 String payloadJson;
 payloadJson = StringUtils.newStringUtf8
(Base64.decodeBase64(parts[1]));
 log.info("parts[1]=" + payloadJson);
 //输出的示例为:
 //解码后的第二部分:parts[1]={"sub":"session id","exp":1579315535,"iat":
1578451535}
 } catch (Exception e)
 {
 e.printStackTrace();
 }
 }
 ...
}

在编码前的JWT中,payload部分JSON中的属性被称为JWT的声明。JWT的声明分为两类:

(1)公有的声明(如iat)。

(2)私有的声明(自定义的JSON属性)。

公有的声明也就是JWT标准中注册的声明,主要为以下JSON属性:

ÿ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值