单点登录JWT与Spring Security OAuth
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单点登录JWT与Spring Security OAuth相关的知识,希望对你有一定的参考价值。
参考技术A
通过 JWT 配合 Spring Security OAuth2 使用的方式,可以避免 每次请求 都 远程调度 认证授权服务。 资源服务器 只需要从 授权服务器 验证一次,返回 JWT。返回的 JWT 包含了 用户 的所有信息,包括 权限信息 。
1. 什么是JWT
JSON Web Token(JWT)是一种开放的标准(RFC 7519),JWT 定义了一种 紧凑 且 自包含 的标准,旨在将各个主体的信息包装为 JSON 对象。 主体信息 是通过 数字签名 进行 加密 和 验证 的。经常使用 HMAC 算法或 RSA( 公钥 / 私钥 的 非对称性加密 )算法对 JWT 进行签名, 安全性很高 。
2. JWT的结构
JWT 的结构由三部分组成:Header(头)、Payload(有效负荷)和 Signature(签名)。因此 JWT 通常的格式是 xxxxx.yyyyy.zzzzz。
2.1. Header
Header 通常是由 两部分 组成:令牌的 类型 (即 JWT)和使用的 算法类型 ,如 HMAC、SHA256 和 RSA。例如:
将 Header 用 Base64 编码作为 JWT 的 第一部分 ,不建议在 JWT 的 Header 中放置 敏感信息 。
2.2. Payload
下面是 Payload 部分的一个示例:
将 Payload 用 Base64 编码作为 JWT 的 第二部分 ,不建议在 JWT 的 Payload 中放置 敏感信息 。
2.3. Signature
要创建签名部分,需要利用 秘钥 对 Base64 编码后的 Header 和 Payload 进行 加密 ,加密算法的公式如下:
签名 可以用于验证 消息 在 传递过程 中有没有被更改。对于使用 私钥签名 的 token,它还可以验证 JWT 的 发送方 是否为它所称的 发送方 。
3. JWT的工作方式
客户端 获取 JWT 后,对于以后的 每次请求 ,都不需要再通过 授权服务 来判断该请求的 用户 以及该 用户的权限 。在微服务系统中,可以利用 JWT 实现 单点登录 。认证流程图如下:
4. 案例工程结构
工程原理示意图如下:
5. 构建auth-service授权服务
UserServiceDetail.java
UserRepository.java
实体类 User 和上一篇文章的内容一样,需要实现 UserDetails 接口,实体类 Role 需要实现 GrantedAuthority 接口。
User.java
Role.java
jks 文件的生成需要使用 Java keytool 工具,保证 Java 环境变量没问题,输入命令如下:
其中,-alias 选项为 别名 ,-keyalg 为 加密算法 ,-keypass 和 -storepass 为 密码选项 ,-keystore 为 jks 的 文件名称 ,-validity 为配置 jks 文件 过期时间 (单位:天)。
生成的 jks 文件作为 私钥 ,只允许 授权服务 所持有,用作 加密生成 JWT。把生成的 jks 文件放到 auth-service 模块的 src/main/resource 目录下即可。
对于 user-service 这样的 资源服务 ,需要使用 jks 的 公钥 对 JWT 进行 解密 。获取 jks 文件的 公钥 的命令如下:
这个命令要求安装 openSSL 下载地址,然后手动把安装的 openssl.exe 所在目录配置到 环境变量 。
输入密码 fzp123 后,显示的信息很多,只需要提取 PUBLIC KEY,即如下所示:
新建一个 public.cert 文件,将上面的 公钥信息 复制到 public.cert 文件中并保存。并将文件放到 user-service 等 资源服务 的 src/main/resources 目录下。至此 auth-service 搭建完毕。
maven 在项目编译时,可能会将 jks 文件 编译 ,导致 jks 文件 乱码 ,最后不可用。需要在 pom.xml 文件中添加以下内容:
6. 构建user-service资源服务
注入 JwtTokenStore 类型的 Bean,同时初始化 JWT 转换器 JwtAccessTokenConverter,设置用于解密 JWT 的 公钥 。
配置 资源服务 的认证管理,除了 注册 和 登录 的接口之外,其他的接口都需要 认证 。
新建一个配置类 GlobalMethodSecurityConfig,通过 @EnableGlobalMethodSecurity 注解开启 方法级别 的 安全验证 。
拷贝 auth-service 模块的 User、Role 和 UserRepository 三个类到本模块。在 Service 层的 UserService 编写一个 插入用户 的方法,代码如下:
配置用于用户密码 加密 的工具类 BPwdEncoderUtil:
实现一个 用户注册 的 API 接口 /user/register,代码如下:
在 Service 层的 UserServiceDetail 中添加一个 login() 方法,代码如下:
AuthServiceClient 作为 Feign Client,通过向 auth-service 服务接口 /oauth/token 远程调用获取 JWT。在请求 /oauth/token 的 API 接口中,需要在 请求头 传入 Authorization 信息, 认证类型 ( grant_type )、用户名 ( username ) 和 密码 ( password ),代码如下:
其中,AuthServiceHystrix 为 AuthServiceClient 的 熔断器 ,代码如下:
JWT 包含了 access_token、token_type 和 refresh_token 等信息,代码如下:
UserLoginDTO 包含了一个 User 和一个 JWT 成员属性,用于返回数据的实体:
登录异常类 UserLoginException
全局异常处理 切面类 ExceptionHandle
在 Web 层的 UserController 类中新增一个登录的 API 接口 /user/login 如下:
依次启动 eureka-service,auth-service 和 user-service 三个服务。
7. 使用Postman测试
因为没有权限,访问被拒绝。在数据库手动添加 ROLE_ADMIN 权限,并与该用户关联。重新登录并获取 JWT,再次请求 /user/foo 接口。
在本案例中,用户通过 登录接口 来获取 授权服务 加密后的 JWT。用户成功获取 JWT 后,在以后每次访问 资源服务 的请求中,都需要携带上 JWT。 资源服务 通过 公钥解密 JWT, 解密成功 后可以获取 用户信息 和 权限信息 ,从而判断该 JWT 所对应的 用户 是谁,具有什么 权限 。
获取一次 Token,多次使用, 资源服务 不再每次访问 授权服务 该 Token 所对应的 用户信息 和用户的 权限信息 。
一旦 用户信息 或者 权限信息 发生了改变,Token 中存储的相关信息并 没有改变 ,需要 重新登录 获取新的 Token。就算重新获取了 Token,如果原来的 Token 没有过期,仍然是可以使用的。一种改进方式是在登录成功后,将获取的 Token 缓存 在 网关上 。如果用户的 权限更改 ,将 网关 上缓存的 Token 删除 。当请求经过 网关 ,判断请求的 Token 在 缓存 中是否存在,如果缓存中不存在该 Token,则提示用户 重新登录 。
Spring Security + JWT 实现单点登录,还有谁不会??
以上是关于单点登录JWT与Spring Security OAuth的主要内容,如果未能解决你的问题,请参考以下文章
Spring Security + JWT 实现单点登录,还有谁不会。。。
Spring Security整合JWT,实现单点登录,So Easy~!
Java开发 - 单点登录初体验(Spring Security + JWT)