【问题标题】:Spring Boot 2 Oauth how to implement Implicit Code FlowSpring Boot 2 Oauth 如何实现隐式代码流
【发布时间】: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


    【解决方案1】:

    将您的 configure() 实现从 WebSecurityConfig 移动到资源服务器 configure() 方法,如下所示

    http.authorizeRequests()
          .antMatchers("/api/v1/**")
          .hasAnyRole("ADMIN", "USER")
    

    configure() 方法中的其余配置不是必需的

    【讨论】:

      猜你喜欢
      • 2019-06-12
      • 2018-08-18
      • 2019-02-18
      • 2019-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-17
      • 1970-01-01
      相关资源
      最近更新 更多