OAuth(Open Authorization,开放授权)是一个开放标准的授权协议,允许用户授权第三方应用访问他们存储在资源服务上受保护的信息,而不需要将用户名和密码提供给第三方应用,解耦了认证和授权。OAuth作为一种国际标准,目前传播广泛并被持续采用。OAuth2.0是OAuth协议的延续版本,更加安全,更易于实现,但不向后兼容OAuth1.0,即完全废止了OAuth1.0。OAuth2中涉及到名次术语如下所示:
- Third-party application: 第三方应用,又称"客户端"(client),如上面场景中的门禁系统。
- Resource Owner: 资源拥有者,也就是用户。
- Authorization server: 认证服务器,即服务提供商专门用来处理认证的服务器。
- Resource server: 资源服务器,即服务提供商存放用户生成的资源的服务器。与认证服务器是不同的逻辑节点,但是在物理上,双方是可以在一起的
- User Agent: 用户代理,一般就是指的浏览器。
- 客户端凭证: Client Id和密码用于认证用户。
- 访问令牌: 授权服务提供者在接收到用户请求后,颁发的访问令牌。
- 刷新令牌(Refresh Token): 用于获取一个新的令牌。由于令牌的有效期比较短,一旦失效,用户需要再获取令牌的流程是比较繁琐的。为了提升用户体验,可以使用reflesh_token来获取新的令牌。
- 授权码(Auhtorization Code Token):仅用于授权码类型,用于交换获取访问令牌的刷新令牌
- 作用域:客户端可访问的资源范围,客户请求访问令牌时,由资源拥有者指定细分权限
在上面提到的令牌中,还可以按获取令牌后是否就可以访问资源来区分Bearer Token和PoP
Bearer Token:不管谁拿到token都可以访问资源,像现金钞票
Proof of Possession(PoP) Token:可以校验client是否对Token有明确的拥有权
OAuth2在很多地方得到广泛应用是因为有很多优势,例如:支持短寿命的token,客户端不接触用户名和密码,服务端更易集中保护,这样使用OAuth2就更安全;另外,实现了资源服务器和授权服务器的解藕,采用HTTP/JSON请求和传递Token,利于使用。除了优势外,也有一些不足,例如:协议框架太宽泛,造成各种框架实现的兼容性和互操作性差,和OAuth1不兼容。最后,需要注意一点:OAuth2只是一个授权协议,不涉及认证相关内容。
前面接受了OAuth2的一些术语和优势等,接下来看看OAuth2支持的4种授权模式。
- 密码模式(resource owner password credentials)
- 授权码模式(authorization code)
- 简化模式(implicit)
- 客户端模式(client credentials)
密码模式:这种模式是不推荐,因为client可能存了用户密码这种模式主要用来做遗留项目升级为oauth2的适配方案,如果client是自家的应用,也是可以支持refresh token,一般在内部系统中使用,调用者是以用户为单位。
授权码模式:授权码模式是最复杂的,也是最安全的,设计了auth code,通过这个code再获取token,code是一次性的临时凭证,用来换取access_token和refresh_token,一旦使用,立即作废,不能再次使用。支持refresh token。是功能最完整、流程最严密的授权模式,通常使用在公网的开放平台中。
简化模式:这种模式比授权码模式少了code环节,回调url直接携带token这种模式的使用场景是基于浏览器的应用。这种模式基于安全性考虑,建议把token时效设置短一些,不支持refresh token,不安全,适用于纯静态页面应用
客户端模式:该模式直接根据client的id和密钥即可获取token,无需用户参与。这种模式比较适合消费api的后端服务,不支持refresh token。一般在内部系统之间的 API 调用;两个平台之间调用;调用者是以平台为单位如第三方,或者调用者是一个后端的模块,没有用户界面的时候,可以使用客户端模式。
实现OAuth2的框架很多,下面讲通过Spring-Security-OAuth2框架为例,通过小例子演示如何实现上述的4种授权模式。Demo例子来源于Gitee。
如果要使用Spring-Security-OAuth2首先需要引入Spring-Security相关的包。OAuth2的包是
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
除了添加OAuth2的包,如果采用spring-boot框架,还需要引入这个包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
那么spring-boot-starter-security包的作用是什么呢?引入这个包后,如何使用它呢?
spring-boot-starter-security是spring-boot集成了spring-security的相关功能而生成的jar包。而Spring Security是一个基于Spring AOP和Servlet过滤器的安全框架。它提供全面的安全性解决方案,同时在Web请求级和方法调用级处理身份确认和授权。对于一个开发好的web应用,如果引入上述包后,再次启动web应用,那么会弹出输入用户名和密码的alert框,如果不进行任何设置,程序会生成默认的用户名和密码。
也可以在resource下的application.properties中设置用户名和密码
security.user.name=user
security.user.password=password
设置后,启动web应用后必须在alert框中输入相应的用户名和密码才能进入web应用。当然spring-boot-starter-security除了上面的简单应用外,还支持写一个extends WebSecurityConfigurerAdapter的配置类来实现登陆的Form框等,更多spring-boot-starter-security的使用可查看这篇博客。
接下来看看OAuth2如何使用,引入包后,需要写两个配置类,一个是在授权服务器上,继承AuthorizationServerConfigurerAdapter,编写授权授权相关的配置。
一个实在资源服务器上,继承ResourceServerConfigurerAdapter,编写资源服务器相关配置。下面的配置说明所有/api/的请求都需要带上上面的获取的token才能进行访问。
启动Gitee上的Demo代码后,在浏览器上访问如下链接,获取授权码,备注:浏览器访问的时候会弹出alert框,让你输入前面设置的用户名和密码。http://localhost:8080/oauth/authorize?client_id=clientapp&redirect_uri=http://localhost:9001/callback&response_type=code&scope=read_userinfo
获取到授权码后,通过授权码获取token,获取token后,带上token访问/api/**,极客成功访问到相关的api请求。详细信息查看这里。
上面是授权码模式,接着来看看客户端模式,对于客户端模式,只需修改授权服务器配置类即可。
启动应用,通过curl命令即可获取到token。curl -X POST "http://localhost:8080/oauth/token" --user clientdevops:789 -d "grant_type=client_credentials&scope=devops"
可以看到和授权码模式相比较,client模式更简单,没有回掉地址,也没有授权码了。
接着再看看简化模式,Implicit。
访问如下链接,即可返回token。
http://localhost:8080/oauth/authorize?client_id=clientapp&redirect_uri=http://localhost:9001/callback&response_type=token&scope=read_userinfo&state=abc 最后再看看密码模式,因为密码模式会告知客户端用户名和密码信息,故大多数场景不推荐。
上面就是密码模式的配置类,密码模式在获取token时,需要告知客户端在application.properties中设置的用户名和密码,安全性较低。获取token的curl命令如下所示
curl -X POST --user clientapp:112233 http://localhost:8080/oauth/token -H "accept: application/json" -H "content-type: application/x-www-form-urlencoded" -d "grant_type=password&username=user&password=password&scope=read_userinfo"
前面通过简单的例子介绍了OAuth2的四种授权模式,例子中都是采用内存存放的模式,且资源服务器和授权服务器在一个repo上,后续的例子会演示如何进行服务拆分以及采用数据库方式来存放token等信息。