spring security oauth2实践

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"
}

你可能感兴趣的:(spring security oauth2实践)