1 概述
1.1 oauth2 根据使用场景不同,分成了4种模式
授权码模式(authorization code 即先登录获取code,再获取token)
简化模式(implicit 在redirect_uri 的Hash传递token; Auth客户端运行在浏览器中,如JS,Flash)
密码模式( password 将用户名,密码传过去,直接获取token)
客户端模式(client credentials 无用户,用户向客户端注册,然后客户端以自己的名义向’服务端’获取资源)
1.2 security oauth2 整合的3个核心配置类
资源服务配置 ResourceServerConfiguration
授权认证服务配置 AuthorizationServerConfiguration
security 配置 SecurityConfiguration
2 实例
2.1 主要代码
- 认证服务器类
@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Autowired
UserDetailsService userDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//配置两个客户端,一个用于password认证一个用于client认证
clients.inMemory().withClient("client_1")
.resourceIds(DEMO_RESOURCE_ID)
.authorizedGrantTypes("client_credentials")
.scopes("select")
.authorities("oauth2")
.secret("123456")
.and().withClient("client_2")
.resourceIds(DEMO_RESOURCE_ID)
.authorizedGrantTypes("password", "refresh_token")
.scopes("select")
.authorities("oauth2")
.secret("123456");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(new RedisTokenStore(redisConnectionFactory))
.tokenStore(new InMemoryTokenStore())
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
// 2018-4-3 增加配置,允许 GET、POST 请求获取 token,即访问端点:oauth/token
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
endpoints.reuseRefreshTokens(true);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
//允许表单认证
oauthServer.allowFormAuthenticationForClients();
}
}
- 资源服务器认证配置类
@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(DEMO_RESOURCE_ID).stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
// Since we want the protected resources to be accessible in the UI as well we need
// session creation to be allowed (it's disabled by default in 2.0.6)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.anonymous()
.and()
.authorizeRequests()
.antMatchers("/product/**").access("#oauth2.hasScope('select') and hasPermission('delete')")
.antMatchers("/order/**").authenticated();//配置order访问控制,必须认证过后才可以访问
// @formatter:on
}
}
- security 配置
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
@Override
protected UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user_1").password("123456").authorities("USER").build());
manager.createUser(User.withUsername("user_2").password("123456").authorities("USER").build());
return manager;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers().anyRequest()
.and()
.authorizeRequests()
.antMatchers("/oauth/*").permitAll();
// @formatter:on
}
}
- 测试 controller
@RestController
public class TestEndpoints {
@GetMapping("/product/{id}")
public String getProduct(@PathVariable String id) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return "product id : " + id;
}
@GetMapping("/order/{id}")
public String getOrder(@PathVariable String id) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return "order id : " + id;
}
}
2.2 测试验证
2.2.1 请求方式说明
1. /oauth/authorize:授权端点。
2. /oauth/token:获取token。
3. /oauth/confirm_access:用户确认授权提交端点。
4. /oauth/error:授权服务错误信息端点。
5. /oauth/check_token:用于资源服务访问的令牌解析端点。
6. /oauth/token_key:提供公有密匙的端点,如果你使用JWT令牌话。
7. /oauth/logout: 退出
2.2.2 未授权访问资源
curl -X GET http://localhost:8080/order/1 -H 'cache-control: no-cache'
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
2.2.3 密码模式获取token
curl -X GET \
'http://127.0.0.1:8080/oauth/token?username=user_1&password=123456&grant_type=password&client_id=client_2&client_secret=123456' \
-H 'Postman-Token: fa9a639f-4401-4d47-9de2-11e010913905' \
-H 'cache-control: no-cache'
{
"access_token": "4be14cf1-2049-4bec-b7e6-27805500e8d8",
"token_type": "bearer",
"refresh_token": "86f583ae-7f0c-4cf7-934c-47435a1f0b9c",
"expires_in": 37729,
"scope": "select"
}
2.2.4 客户端模式获取token
curl -X GET \
'http://127.0.0.1:8080/oauth/token?grant_type=client_credentials\
&client_id=client_1&client_secret=123456'
-H 'cache-control: no-cache'
{
"access_token": "655e2f3c-7b58-45d7-b331-8d9784eabb21",
"token_type": "bearer",
"expires_in": 43098,
"scope": "select"
}
2.2.4 授权码模式获取token
2.2.7 带错误token 访问资源
curl -X GET \
'http://localhost:8080/order/1?access_token=9363651c-d354-41d3-89dc-89607665b351' \
-H 'cache-control: no-cache'
{
"error": "invalid_token",
"error_description": "Invalid access token: 9363651c-d354-41d3-89dc-89607665b351"
}
2.2.6 带正确token 访问资源
curl -X GET \
'http://localhost:8080/order/1?access_token=c363651c-d354-41d3-89dc-89607665b351' \
-H 'cache-control: no-cache'
order id : 1
2.2.7 密码模式刷新token
curl -X GET \
'http://localhost:8080/oauth/token?grant_type=refresh_token&refresh_token=2c24fdba-7c7a-409d-ac1d-3af411aa3dc5&client_id=client_2&client_secret=123456' \
-H 'cache-control: no-cache'
{
"access_token": "eaf7dad3-789e-4bd5-9e3c-0a8c3801a285",
"token_type": "bearer",
"refresh_token": "2c24fdba-7c7a-409d-ac1d-3af411aa3dc5",
"expires_in": 43199,
"scope": "select"
}
网友评论