OAuth 2.0 简单介绍
**角色**:先区分下OAuth 2.0 中有哪些角色,注意这里根据自己理解来写的,阮一峰博客里写的更精确:
Client: 客户端,也就是Third-party application - 第三方应用程序
Service:服务端,也就是服务的提供者
User: 用户,也就是Resource Owner - 资源所有者
User Agent:用户代理,如浏览器,下文中将其与Client合并考虑。
Authorization Server:认证服务器,即服务提供商专门用来处理认证的服务器。
Resource Server:资源服务器,即服务提供商存放用户生成的资源的服务器。
**模式**
在不需要第三方认证支持时,我们常用的就是简化模式:
(A)客户端将用户导向认证服务器。
(B)用户决定是否给于客户端授权。
(C)假设用户给予授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。
(D)浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。
(E)资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。
(F)浏览器执行上一步获得的脚本,提取出令牌。
(G)浏览器将令牌发给客户端。
**Spring Cloud 微服务下**
Spring Cloud 下我们这里使用简化模式,主要是登录、授权、Token管理,角色大体如下:
User: 也就是用户,用户一般直接与Client交互,REST API后台一般不需要考虑。
Gateway + Resource Server :资源服务器对请求进行认证,一般整合在网关中,这样可以很方便的统一处理所有请求。
Authorization Server: 授权服务器,进行授权和Token管理。
Client: 调用API的应用,一般是前端、移动App或者第三方应用
Token Store: 令牌存储,多个服务如果每次请求都通过授权服务器进行Token查询,效率底下,所以需要统一存储、交互令牌信息,常用Redis
Services: 提供正在业务/功能/API的服务。
大概画个图,这里以Client为前端为例,注意不涉及用户和前端的交互:
**JWT 简介**
JWT -- Json Web Token, 如其名,使用Json方式保存Web Token的协议。网上有各种解读,个人理解,这就是一个 客户端Session - Session保存在客户端,而不是通常的保存在服务端。
构成
JWT三部分组成:
Header 头部:JSON方式描述JWT基本信息,如类型和签名算法。使用Base64编码为字符串
Payload 载荷:JSON方式描述JWT信息,除了标准定义的,还可以添加自定义的信息。同样使用Base64编码为字符串。
iss: 签发者
sub: 用户
aud: 接收方
exp(expires): unix时间戳描述的过期时间
iat(issued at): unix时间戳描述的签发时间
Signature 签名:将前两个字符串用 . 连接后,使用头部定义的加密算法,利用密钥进行签名,并将签名信息附在最后。
注意: Payload 使用 Base64编码,所以就是明文的,不要存放任何机密信息。
优缺点
当然带来一些好处:
服务端内存占用少了
不需要维护session状态了,真正无状态
单点登录 so easy,只要后台服务能解读,Cookie 设置为顶级域名
有好处当然就有不太好的:
每个请求就要对JWT进行解密,验证
Token有效期只有超时,没有退出。当然有一些做法,上面也提到了,jwt-authentication-how-to-implement-logout
XSS攻击问题,一个讨论: Is it OK to store the JWT in local/session storage
我个人的看法是: 使用JWT,同时在Redis保存信息,在API网关进行详细的验证;各服务则只简单校验Token本身是否篡改。
Spring Cloud OAuth 解读
角色
Spring Cloud OAuth中将角色为三个,这点从源码中包org.springframework.security.oauth2.config.annotation.web.configurers 中包含三个Enable注解就可以看出来:
EnableAuthorizationServer -- 使能授权服务器
EnableResourceServer -- 使能资源服务器
EnableOAuth2Client -- 使能客户端,如需要第三方授权来调用,应该使用此注解。
AuthorizationServer 授权服务配置
一. 首先当然需要使能,在配置类或 Application 上类添加注解: @EnableAuthorizationServer,添加该注解后会自动添加OAuth2的多个endpoint, 相关实现代码在包 org.springframework.security.oauth2.provider.endpoint:
/oauth/authorize:验证接口, AuthorizationEndpoint
/oauth/token:获取token
/oauth/confirm_access:用户授权
/oauth/error:认证失败
/oauth/check_token:资源服务器用来校验token
/oauth/token_key:jwt模式下获取公钥;位于:TokenKeyEndpoint ,通过 JwtAccessTokenConverter 访问key
二. 配置入口为接口:AuthorizationServerConfigurer, 通过扩展AuthorizationServerConfigurerAdapter 实现来进行配置。
Spring Boot 2中很多 Adapter已经取消,直接利用 Java8 Interface Default特性来实现,不过到我写此文时 security 还没改,当然也许是我没注意到。
三. 简单看一下 AuthorizationServerConfigurer 接口的方法, 一共配置三个属性:
AuthorizationServerSecurityConfigurer :声明安全约束,哪些允许访问,哪些不允许访问。配置 AuthorizationServer 的安全属性,也就是endpoint /oauth/token 。/oauth/authorize 则和其它用户 REST 一样保护。可以不配置。
ClientDetailsServiceConfigurer : 配置 ClientDetailsService 独立client客户端的信息。包括权限范围、授权方式、客户端权限等配置。授权方式有4种:implicit, client_redentials, password , authorization_code, 其中密码授权方式必须结合 AuthenticationManager 进行配置。必须至少配置一个客户端。
AuthorizationServerEndpointsConfigurer : 配置AuthorizationServer 端点的非安全属性,也就是 token 存储方式、token 配置、用户授权模式等。默认不需做任何配置,除非使用 密码授权方式, 这时候必须配置 AuthenticationManager。
四. 其中,Token管理:
Token 生命周期管理接口 AuthorizationServerTokenServices, 默认使用: DefaultTokenServices。
Token存储通过配置 TokenStore,默认使用内存存储。AuthorizationServerEndpointsConfigurer 或 DefaultTokenServices 入口配置。配置方式有
InMemoryTokenStore 默认方式,保存在本地内存
JdbcTokenStore 存储数据库
RedisTokenStore 存储Redis,这应该是微服务下比较常用方式
JwtTokenStore
AccessTokenConverter
五. 加密算法配置
在spring5之后,必须配置加密算法。
测试时候可以用无加密算法,参考:no-passwordencoder-mapped-id-null
@SuppressWarnings("deprecation")
@Bean
public NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
配置加密算法,当然也可以配置其它算法:
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
具体参考:
https://www.jianshu.com/p/4089c9cc2dfd