使用正确的访问令牌访问 OAuth2 保密资源时遇到问题
Posted
技术标签:
【中文标题】使用正确的访问令牌访问 OAuth2 保密资源时遇到问题【英文标题】:Trouble accessing OAuth2 secred resource using correct access token 【发布时间】:2015-09-21 13:25:05 【问题描述】:我正在使用 Spring 4.1.5.RELEASE、Spring Security 3.2.5.RELEASE 和 OAuth2 2.0.7.RELEASE。我正在使用 JdbcTokenStore 并将其保存在我的 oauth_client_details 表中……
client_id = dave
resource_ids = /dashboard
scope = read,write,delete
authorized_grant_types = implicit,password,client_credentials
web_server_redirect_uri = /redirect
authorities = ROLE1,ROLE2,ROLE3
access_token_validity = 3600
refresh_token_validity = 3600
但我无法访问我的安全资源 /dashboard。下面我提出获取访问令牌的请求……
Daves-MacBook-Pro:biv2 davea$ curl -v -d grant_type=password \
-d username=ROLE3 \
-d password=dave \
-u dave:davesecret \
http://localhost:8080/mycontext-path/oauth/token
* Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
* Server auth using Basic with user 'dave'
> POST /mycontext-path/oauth/token HTTP/1.1
> Authorization: Basic YmltOmJpbXNlY3JldA==
> User-Agent: curl/7.40.0
> Host: localhost:8080
> Accept: */*
> Content-Length: 50
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 50 out of 50 bytes
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Cache-Control: no-store
< Pragma: no-cache
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Fri, 03 Jul 2015 16:33:39 GMT
<
* Connection #0 to host localhost left intact
"access_token":"2648069d-607d-449d-af10-cb38d5fd7614","token_type":"bearer","expires_in":470,"scope":"delete read write"
然后我尝试使用该访问令牌来获取我的资源 /dashboard,但我正在重定向回我的登录页面……
Daves-MacBook-Pro:biv2 daveacurl -v -H 'Authorization: Bearer 2648069d-607d-449d-af10-cb38d5fd7614' http://localhost:8080/mycontext-path/dashboard
* Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET /mycontext-path/dashboard HTTP/1.1
> User-Agent: curl/7.40.0
> Host: localhost:8080
> Accept: */*
> Authorization: Bearer 2648069d-607d-449d-af10-cb38d5fd7614
>
< HTTP/1.1 302 Found
< Server: Apache-Coyote/1.1
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Set-Cookie: JSESSIONID=1EF45E8864F5A03CA0D2895E8E851E87; Path=/mycontext-path/; HttpOnly
< Location: http://localhost:8080/mycontext-path/login
< Content-Length: 0
< Date: Fri, 03 Jul 2015 16:34:06 GMT
<
* Connection #0 to host localhost left intact
我错过了什么?下面是我如何配置我的 OAuth2Config 以及我的 Spring 安全配置。
@Configuration
@ComponentScan
@EnableAutoConfiguration
@RestController
public class Application
public static void main(String[] args)
SpringApplication.run(Application.class, args);
@Configuration
@EnableAuthorizationServer
// [1]
protected static class OAuth2Config extends
AuthorizationServerConfigurerAdapter
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private DataSource dataSource;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private TokenStore tokenStore;
@Override
// [2]
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception
endpoints.tokenStore(tokenStore)
.userApprovalHandler(userApprovalHandler())
.authenticationManager(authenticationManager);
@Override
// [3]
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception
// @formatter:off
clients.jdbc(dataSource);
@Bean
public TokenStore tokenStore()
return new JdbcTokenStore(dataSource);
@Bean
public TokenApprovalStore tokenApprovalStore()
TokenApprovalStore tokenApprovalStore = new TokenApprovalStore();
tokenApprovalStore.setTokenStore(tokenStore);
return tokenApprovalStore;
@Bean
public OAuth2RequestFactory getOauth2RequestFactory()
return new DefaultOAuth2RequestFactory(clientDetailsService);
// getOauth2RequestFactory
@Bean
public UserApprovalHandler userApprovalHandler()
final TokenStoreUserApprovalHandler userApprovalHandler = new TokenStoreUserApprovalHandler();
userApprovalHandler.setRequestFactory(getOauth2RequestFactory());
userApprovalHandler.setTokenStore(tokenStore);
return userApprovalHandler;
// userApprovalHandler
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
oauthServer.realm("abcdefgh/client");
Spring 安全配置:
@Configuration
@EnableWebSecurity
@ComponentScan(basePackages="com.myproject", excludeFilters=@ComponentScan.Filter(Controller.class))
public class SecurityConfig extends WebSecurityConfigurerAdapter
private static final String ROLE1 = "ROLE1";
private static final String ROLE2 = "ROLE2";
private static final String ROLE3 = "ROLE3";
@Resource(name="userDetailsService")
private UserDetailsService userDetailsService;
@Resource(name="jsonAuthenticationSuccessHandler")
private daveAuthenticationSuccessHandler authSuccessHandler;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
@Override
public void configure(HttpSecurity http) throws Exception
http.addFilterBefore(authenticationFilter(), JSONUsernamePasswordAuthenticationFilter.class);
http
.authorizeRequests()
.antMatchers("/403").permitAll()
.antMatchers("/login/**").permitAll()
.antMatchers("/resources/**").permitAll()
// TODO: Add secured patterns
.antMatchers("/ROLE3/**").hasRole(ROLE3)
.antMatchers("/common/**").hasAnyRole(ROLE3, ROLE2, ROLE1)
.antMatchers("/ROLE1/**").hasAnyRole(ROLE3, ROLE2, ROLE1)
.antMatchers("/ROLE2/**").hasAnyRole(ROLE3, ROLE2)
// Allow access to all other resources only if authenticated
.antMatchers("/*/**").fullyAuthenticated()
.and().formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.usernameParameter("username")
.passwordParameter("password")
.successHandler(authSuccessHandler)
.and().logout().logoutSuccessUrl("/login?logout")
.and().exceptionHandling().accessDeniedPage("/403")
.and().csrf().disable();
@Bean(name="passwordEncoder")
public PasswordEncoder passwordEncoder()
return new StandardPasswordEncoder();
@Bean
public JSONUsernamePasswordAuthenticationFilter authenticationFilter() throws Exception
final JSONUsernamePasswordAuthenticationFilter authFilter = new JSONUsernamePasswordAuthenticationFilter();
//authFilter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login","POST"));
authFilter.setAuthenticationManager(authenticationManagerBean());
authFilter.setAuthenticationSuccessHandler(authSuccessHandler);
authFilter.setUsernameParameter("username");
authFilter.setPasswordParameter("password");
return authFilter;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception
return super.authenticationManagerBean();
【问题讨论】:
使用 .antMatchers("/*/**").fullyAuthenticated() 访问仪表板,并且您的令牌具有权限 ROLE1/2/3。如果添加 .antMatchers("/dashboard").hasAnyRole(ROLE3, ROLE2, ROLE1) 会发生什么(只是看看它是否真的是一个令牌问题)?另外我想知道您的 403 页面是做什么的:在配置的另一部分不会重定向到您的登录页面?是否记录了任何异常? 【参考方案1】:在 OAuth2 中受保护的资源是 ResourceServer 的一部分。
spring-security-oauth2
使用OAuth2AuthenticationProcessingFilter
处理 OAuth2 身份验证。
@EnableWebSecurity
在您的 SecurityConfig
类中还不够。
你需要用@EnableResourceServer
注释它,扩展ResourceServerConfigurerAdapter
会更容易配置相应的http安全。
见Reference documentation
【讨论】:
以上是关于使用正确的访问令牌访问 OAuth2 保密资源时遇到问题的主要内容,如果未能解决你的问题,请参考以下文章
如果我的用户会话持续 30 分钟,我的 Oauth2 访问令牌应该持续的正确时间是多少?