是否可以在 Spring Security 中仅使用刷新令牌请求访问令牌 oauth2?没有基本身份验证?

Posted

技术标签:

【中文标题】是否可以在 Spring Security 中仅使用刷新令牌请求访问令牌 oauth2?没有基本身份验证?【英文标题】:Is possible ask for an acces token oauth2 just with refresh token in spring security? without basic authentication? 【发布时间】:2020-11-07 07:55:34 【问题描述】:

我想知道在春季 oauth2 中是否有可能仅使用另一个刷新令牌获得一对新的令牌(访问令牌和刷新令牌),而无需基本身份验证(没有 clientId 和 clientSecret,有什么办法吗?

例如:

具有基本身份验证

curl -u clientId:clientSecret -X POST 'http://myapplication.oauth2/accounts/oauth/token?grant_type=refresh_token&client_id=&refresh_token=' -v

没有基本身份验证

curl -u -X POST 'http://myapplication.oauth2/accounts/oauth/token?grant_type=refresh_token&client_id=&refresh_token=' -v

我注意到 spring 中的 sprint BasicAuthenticationFilter 使用下面的验证,可能会覆盖此过滤器并仅使用刷新令牌进行身份验证。

String header = request.getHeader("Authorization");

if (header == null || !header.toLowerCase().startsWith("basic ")) 
  chain.doFilter(request, response);
  return;

【问题讨论】:

我对如何解决这个问题非常感兴趣。 @georgecro 为什么要在没有客户端身份验证(没有客户端 ID 和客户端密码)的情况下刷新访问令牌?在这种情况下,OAuth 2.0 规范需要客户端身份验证。 我已经用解决方法更新了我的答案 【参考方案1】:

简短的回答是否定的。用于管理 Spring Oauth 2 端点的类如下:

@FrameworkEndpoint
public class TokenEndpoint extends AbstractEndpoint

我的意思是,两个请求都获得access tokenrefresh 一个使用具有不同参数的相同端点。管理这些的方法是:

@RequestMapping(
  value = "/oauth/token",
  method = RequestMethod.POST
)
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException 
  if (!(principal instanceof Authentication)) 
    throw new InsufficientAuthenticationException("There is no client authentication. Try adding an appropriate authentication filter.");
   else 
    String clientId = this.getClientId(principal);
    ... 

如您所见,Principal 对象是必需的(在本例中由基本身份验证提供)。

即使,如果您将项目的安全性配置为允许该 url 而不检查身份验证,您将在上述方法中实现“输入”,但您将收到一个 InsufficientAuthenticationException,因为没有提供 Authentication 实例。


为什么自定义身份验证不起作用

1. 创建自定义AuthenticationProvider 将不起作用,因为之前调用了方法postAccessToken。所以你会收到一个InsufficientAuthenticationException

2. 创建一个OncePerRequestFilter 并将其配置为在处理当前请求之前执行:

    @Override
    protected void configure(HttpSecurity http) throws Exception 
      http...
        .anyRequest().authenticated()
        .and()
        .addFilterBefore(myCustomFilter, UsernamePasswordAuthenticationFilter.class);
    

    @Override
    public void configure(WebSecurity web) throws Exception 
      web.ignoring()
            .antMatchers(POST, "/accounts/oauth/**");
    

带有“类似”的代码:

@Component
public class CustomAuthenticationFilter extends OncePerRequestFilter 

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                FilterChain filterChain) throws ServletException, IOException 
    ...
    SecurityContextHolder.getContext().setAuthentication(
            new UsernamePasswordAuthenticationToken("existingUser",
                    "passwordOfExistingUser",
                    Collections.emptyList()));
    ...
    filterChain.doFilter(request, response);

这种方法的问题是TokenEndpoint 中的主体来自HttpServletRequest 而不是来自 Spring 上下文,正如您可以看到调试 BasicAuthenticationFilter 类。

在您的自定义过滤器中,您可以尝试使用反射在 userPrincipal 属性中设置一个值,但正如您可以验证的那样,request 有几个“内部 request 属性”,这可能是一个“太棘手的选项” ”。


总之,Oauth 标准需要用户/通行证来访问资源,如果您想在几乎所有提供的端点中解决问题,那么该项目可能不是您想要的。


在 Spring Principal 中包含您自己的对象的解决方法

我不建议这样做,但如果您仍想继续使用这种方法,有一种方法可以将您自己的值包含在 TokenEndpoint 类接收的 principal 参数中。

重要的是要考虑到 BasicAuthorizationFilter 仍将被执行,但是您可以自己覆盖 Spring 主体对象。

为此,我们可以重用以前的CustomAuthenticationFilter,但现在您必须包含您需要的过滤器,我的意思是,允许的 url、参数等您将“打开大门”,所以要小心你的允许和不允许。

在这种情况下的不同之处在于,我们将在扩展 WebSecurityConfigurerAdapter 的类中添加配置,而不是添加:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter 

  @Autowired
  private CustomAuthenticationFilter customAuthenticationFilter;
  ...

  @Override
  public void configure(AuthorizationServerSecurityConfigurer security) 
    security.checkTokenAccess("isAuthenticated()");
    security.addTokenEndpointAuthenticationFilter(customAuthenticationFilter);
  
  ...

【讨论】:

我有个想法:我看到可以设置http.csrf().disable().authenticationProvider(.....) 如果我实现自定义Authentication实例呢? 同样在这种情况下,您是如何设法实现此刷新令牌功能的? 我已经用更多信息更新了我的答案。 how did you managed... 因为在我的情况下,该微服务不是直接从“前端应用程序”调用的,我通过“后端 - 后端通信”来管理它。另一方面,如果您需要发送“应用程序用户/通行证”(我说的是“应用程序凭据”,而不是应用程序用户的特定凭据),您需要以同样的方式发送这些来自“前端应用程序”的请求,当需要获取访问令牌或刷新时,您需要在请求中继续发送这些数据。 好的,那么这个想法怎么样。我可以存储用户并将 access_token 和 refresh_token 一起传递到 Angular SPA 中。是不是太冒险了?我会检查是否可以加密它们。 Is it too risky? 是的,这是个坏主意,因为 JWT 令牌(实际上是 JWS 令牌)的有效负载是您包含在其上的信息的“base64 值”(更多信息见jwt.io)

以上是关于是否可以在 Spring Security 中仅使用刷新令牌请求访问令牌 oauth2?没有基本身份验证?的主要内容,如果未能解决你的问题,请参考以下文章

如何在响应式方形网格中仅使背景图像透明?

如何在 uwp 应用程序中仅使部分 gridview 项目可点击?

Spring Security 中仅使用自定义 UserDetailsS​​ervice 禁止 403 [重复]

如何允许用户在 Spring Boot / Spring Security 中仅访问自己的数据?

如何在我的 Spring Boot Security OAuth2 应用程序中仅为某些类启用 OAuth2?

是否可以禁用滑动手势并仅使平移手势在滚动视图中工作?