【发布时间】:2019-06-07 01:44:51
【问题描述】:
目前我正在将 OAuth 引入我的 Spring 应用程序,但我没有正确集成它的运气。我正在使用 Spring Boot 2。
我的要求是:
- 授权和资源服务器是同一个应用程序在同一个服务器上运行
- 需要支持隐式、授权代码授予和资源所有者凭据授予流
- 我想保护的 API 位于“/api/v1/”下
没有一个流程正常工作。到目前为止,我所取得的成就是基于https://www.devglan.com/spring-security/spring-oauth2-role-based-authorization 的教程和这个答案:https://stackoverflow.com/a/52386009/4454752
所以我的 AuthorizationServer 看起来像这样:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
@Autowired
public AuthorizationServerConfig(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("as466gf");
return converter;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("my-client-id")
.authorizedGrantTypes("authorization_code", "implicit", "refresh_token", "password")
.authorities("ADMIN")
.scopes("all")
.resourceIds("product_api")
.secret("$2a$10$jfAHmk4szDU/t1qLGlFTLukuBZL0ZHZGUJQICePjjyq6IrLOS934.")
.redirectUris("https://example.com")
.accessTokenValiditySeconds(7200)
.refreshTokenValiditySeconds(7200);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.authenticationManager(authenticationManager)
.accessTokenConverter(accessTokenConverter());
}
}
然后是 ResourceServer
@Configuration
@EnableResourceServer
@Order(2)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId("product_api");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/**")
.and().authorizeRequests()
.antMatchers("/**").permitAll()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
最后是 WebSecurityConfig
@Configuration
@Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource(name = "userDetailService")
private UserDetailService userDetailsService;
@Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(encoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/v1/**")
.hasAnyRole("ADMIN", "USER").and()
.httpBasic().and().formLogin().and().authorizeRequests().anyRequest().authenticated();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(encoder());
}
}
我的 UserDetails 和完整的用户管理已经设置好了,所以这里不需要更改。
现在到用例。
如果访问“/oauth/token”:
curl --request POST \
--url http://localhost:8080/oauth/token \
--header 'authorization: Basic bXktY2xpZW50Om15LXNlY3JldA==' \
--header 'content-type: application/x-www-form-urlencoded' \
--data 'grant_type=password&username=admin&password=test'
因此,使用 my-client:my-secret 我会收到带有访问令牌和刷新令牌的响应。但如果我想在我的 API 上使用访问令牌,我会得到 Access denied。如果我使用/oauth/check_token 检查令牌,则表示令牌有效。
如果我使用“/oauth/authorize”(隐式流程),也会出现同样的问题。我知道我需要 spring 的登录页面才能使其正常工作,这就是我在WebSecurityConfig 中添加formLogin() 的原因。但是如果我查询 http://localhost:8080/oauth/authorize?response_type=token&client_id=my-client&redirect_uri=https://example.com 我会重定向到 /login 我登录,然后从重定向 url 获取访问令牌,但是如果我再次使用这个令牌,我会得到 401 错误。
我要访问的端点由以下控制器处理:
@RestController
@RequestMapping(value = "/api/v1/user")
@CrossOrigin(origins = "*")
public class UserController {
private static final Logger LOGGER = LogManager.getLogger(UserController.class);
public static final String ROLE_USER = "ROLE_USER";
private final AuthenticationFacade authenticationFacade;
private final UserService userService;
@Autowired
public UserController(AuthenticationFacade authenticationFacade,
UserService userService) {
this.authenticationFacade = authenticationFacade;
this.userService = userService;
}
@RequestMapping(value = "/me", method = RequestMethod.GET)
public Optional<User> getCurrentUser() {
LOGGER.info("Requesting /api/v1/user/me");
return userService.findByUsername((String) authenticationFacade.getAuthentication().getPrincipal());
}
}
我很确定安全配置有问题,但我不知道它可能是什么。我在网上查看了很多指南,但我没有找到一个解释所有授权码组合的指南。我认为这可能是验证 URL 的一个小错误,但我不知道它可能是什么。
如果有人知道这个问题的答案,我会很高兴。
【问题讨论】:
标签: spring spring-boot oauth oauth-2.0