写在开篇
本改编课程基于《OAuth 2.0 Cookbook_Protect Your Web Applications using Spring Security-Packt Publishing(2017)》。这本书侧重通过一个个精简的小例子来学习,如何使用spring security和oauth2.0来保护你的资源。
课程从第二章开始,在Chaptor2,我们将学习以下内容:
- 使用授权码模式(Authorization Code grant)保护资源
- 支持隐式授权模式(Implicit grant)
- 使用密码模式(Resource Owner Password Credentials grant type )
- 配置客户端证书授权模式(Client Credentials grant)
- 支持refresh tokens
- 使用一个关系数据库来保存tokens和客户信息
- 使用redis保存token
- 实现客户端注册过程
- 中途破坏Oauth 2.0 Provider
- 使用Gatling,通过共享数据库测试token的验证过程
本例将讲解如何配置客户端模式,当一个应用自己本身需要获取资源(无需用户授权),而不是获取资源所有者的资源时,客户端模式十分有用。
Getting Ready
Java8 + maven
本文源码可点击下载
How to do it…
下面的步骤将指导你如何使用spring security OAuth2.0 配置一个授权中心和一个资源服务器:
- 使用Spring Initializr 新建一个Springboot工程,加入
web
,security
依赖。 - 打开
pom.xml
,加入以下依赖:
org.springframework.security.oauth
spring-security-oauth2
1.打开application.properties文件,输入:
security.user.name=adolfosecurity.user.password=123
2.尽管本例不关注于用户行为,我们依然需要创建一个API来获取用户的profile。为了保证用户可以通过应用程序以一种安全的方式获取profile,新建UserProfile
和UserController
类,内容和《学习3》一样。
3.打开UserController
,将@RequestMapping
的注解由“/api/profile”
改为"/user"
。
4.由于我们在使用客户端模式,我们将不允许客户端获取用户的profile。我们可以创建一个API,来让客户端可以获取在本服务器注册的用户信息。可能这种模式对你而言没啥意义,现在我们将目光移到如何使用客户端模式,而不是它对你有没有用。好了,我们来创建AdminController
,一下是这个类的具体实现:
@Controller
@RequestMapping("/api")
public class AdminController {
@RequestMapping("/users")
public ResponseEntity> getAllUsers() {
return ResponseEntity.ok(getUsers());
}
private List getUsers() {
List users = new ArrayList<>();
users.add(new UserProfile("adolfo", "[email protected]"));
users.add(new UserProfile("demigreite", "[email protected]"));
users.add(new UserProfile("jujuba", "[email protected]"));
return users;
}
}
4.为了保护这个API,我们来创建一个资源服务配置类:
@Configuration
@EnableResourceServer
public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.requestMatchers().antMatchers("/api/**");
}
}
5.为了颁发access token,我们需要创建一个授权中心,注意现在我们定义了一个不同的client ID和密码,还有不同的authorizedGrantTypes:
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("clientadmin")
.secret("123")
.authorizedGrantTypes("client_credentials")
.scopes("admin");
}
}
6.现在我们要保护/users
API,也就是一批没有在客户端注册的用户信息:
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.antMatcher("/user/**").httpBasic()
.and()
.csrf().disable();
}
}
现在全部配好了
How it works…
本例展示了OAuth2.0一种特殊模式,让注册的客户端直接访问资源。之前的几种模式均是通过依赖用户的批准,第三方客户端才能访问资源。因为本例的特殊性,我们专门新建了一个api,可以让客户端拿到所有注册在OAuth2.0 Provider上的用户。
授权中心和资源服务中心的配置和前几种模式几乎一致,最大的不同就是我们不需要重定向地址,此外,我们也要将验证模式改一下。就像这样的
clients.inMemory()
.withClient("clientadmin")
.secret("123")
.authorizedGrantTypes("client_credentials")
.accessTokenValiditySeconds(120)
.scopes("admin");
现在我们来启动服务,看看程序是如何与Oauth 2.0 Provider进行交互。当我们使用客户端模式的时候,我们并没有任何用户的交互,所以也没有授权的模块。但是我们还是需要申请一个授权码:
我们可以通过curl
curl -X POST " http://localhost:8080/oauth/t..." --user clientadmin:123 -d "grant_type=client_credentials&scope=admin"或者PostMan
发送请求后,你会得到如下的回复:
{
"access_token": "22924299-e715-4d7d-a30b-4d315aa932a3",
"token_type": "bearer",
"expires_in": 56,
"scope": "admin"
}
需要注意的是,使用客户端模式的时候,根据OAUTH2.0规范,不应该颁发refreshed token。因为使用客户端模式,不用考虑用户体检,客户端自身可以在access token过期的时候重新申请新的access token。
现在我们可以携带access token去拿去资源了,可能你已经意识到,当我们拿到access token后,我们就可以获取资源,而无关我们使用什么途径拿到这个资源的。
curl " http://localhost:8080/api/users" -H "Authorization: Bearer f6f81a52-7920-4f95-a83b-72fbfb5188c5"或者:
本例向你展示了如何使用客户端模式来保护资源,同时你还可以选择其他的模式(例如HTTP Basic Authentication)保护资源。我们之前在第9步时,配置了WebSecurityConfiguration,其中
.antMatcher("/user/**").httpBasic()
说明匹配的endpoint将使用http 基础认证。因此,我们无法通过之前的方法拿到/user
对应的资源,但是,我们可以通过以下办法获取:
curl "" --user adolfo:123
或者:
There's more…
客户端模式是客户端本身请求验证的行为,所以无需用户“认证”,不牵涉任何用户的行为。但是现在的微服务体系不怎么使用这种模式,因为这种模式可以轻易地通过refresh token来不断获取access token,并且也可以使用动态的注册流程,在集成服务时自动执行所有流程。
考虑到很多微服务都是基于这种模式来沟通,因此存在大量的access token验证过程。基于这种情况,我们需要考虑验证策略,不同的策略会不同程度地影响服务器的性能。在本周的最后章节,我们将学到,如何创建负载测试应用程序,和如何使用Gatling测量验证访问令牌时的响应时间。