首页 > 解决方案 > 使用额外回调处理自定义登录

问题描述

我正在使用 Spring Security 5 和 Spring Boot 2.1 构建 OAuth2 提供程序服务器。

就我而言,我的服务器必须与某个外部服务器通信以验证用户身份。这个外部服务器生活在恐龙时代,因此没有使用像 OAuth 这样的通用身份验证机制。所以我必须劫持登录请求,重定向到恐龙服务器,手动处理该身份验证(不幸的是,其中包括回调),然后返回 Spring Security 以批准登录请求并确保用户获得访问令牌。

劫持登录请求如下:

@Override
  protected void configure ( HttpSecurity http ) throws Exception {
    http
        .requestMatchers()
        .antMatchers( "/login", "/oauth/authorize", "/manuallogin" )
        .and()
        .authorizeRequests()
        .anyRequest()
        .authenticated()
        .and()
        .formLogin()
        .loginPage( "/manuallogin" )
        .permitAll()
        .and().csrf().disable();
  }

@RestController
public class MainLoginController {

  @RequestMapping("/manuallogin")
  ResponseEntity<Object> interceptLoginRequest ( ){
    return ResponseEntity.status( HttpStatus.FOUND )
                         .location( URI.create( dinosaurServer.getLoginUrl() ) )
                         .build();
  }

  @RequestMapping("/handshakeWithDinosaur")
  @Override
  public ResponseEntity<Object> handshakeWithDinosaur ( String dinosaursToken ) {

    Principal principal = getUserPrincipalFromDinosaur();

    // somehow continue login, here is the problem
  }

如您所见,我需要接受另一个回调,因此我丢失了原始登录请求,我无法对此发送响应。

我想出了以下解决方案,通过调用 OAuth2 客户端的回调 URL 来缩短。

    Principal principal = getUserPrincipalFromDinosaur();
    Credential credential = new Credential();
    credential.setUsername( "john"  );
    credential.setPassword( "123" );


    PreAuthenticatedAuthenticationToken preAuthenticatedAuthenticationToken = new PreAuthenticatedAuthenticationToken( principal, credential);
    preAuthenticatedAuthenticationToken.setAuthenticated( true );

    SecurityContextHolder.getContext().setAuthentication( preAuthenticatedAuthenticationToken);



    return ResponseEntity.status( HttpStatus.FOUND )
                         .location( URI.create( String.format( oauth2client.getCallbackUrl(), dinosaursToken ) ))
                         .build();

但是,这不起作用,因为 spring 不接受身份验证。

我必须以某种方式继续原始登录请求并对用户进行身份验证。即使您阅读了所有这些,也非常感谢您:)

标签: javaspring-bootspring-securityoauth-2.0spring-security-oauth2

解决方案


对于遇到这个问题的任何绝望的灵魂,这里是解决方案:

@RestController
public class MainLoginController {

  @RequestMapping("/manuallogin")
  ResponseEntity<Object> interceptLoginRequest ( ){
    ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    DefaultSavedRequest springSecuritySavedRequest = (DefaultSavedRequest) requestAttributes.getRequest()
                                                                                            .getSession()
                                                                                            .getAttribute( "SPRING_SECURITY_SAVED_REQUEST" );
    queryString = springSecuritySavedRequest.getQueryString();
    request.getSession().setAttribute( "queryString", queryString );

    return ResponseEntity.status( HttpStatus.FOUND )
                         .location( URI.create( dinosaurServer.getLoginUrl() ) )
                         .build();
  }

  @RequestMapping("/handshakeWithDinosaur")
  public ResponseEntity<Object> handshakeWithDinosaur ( String dinosaursToken ) {


    Authentication authentication = this.authenticationManager.authenticate(
        new UsernamePasswordAuthenticationToken(
            dino.getUser(), dino.getPass()
        )
    );
    SecurityContext sc = SecurityContextHolder.getContext();
    sc.setAuthentication( authentication );
    request.getSession().setAttribute( SPRING_SECURITY_CONTEXT_KEY, sc );


    String queryString = String.valueOf( request.getSession().getAttribute( "queryString" ) );

    return ResponseEntity.status( HttpStatus.FOUND )
                         .location( URI.create( String.format( "%s?%s",SPRING_AUTH_ENDPOINT, queryString ) ) )
                         .build();
  }


@Component
public class AuthProviderForDinosaur implements AuthenticationProvider {


  @Override
  public Authentication authenticate ( Authentication authentication ) throws AuthenticationException {
    List<GrantedAuthority> grantedAuths = new ArrayList<>();
    grantedAuths.add( new SimpleGrantedAuthority( "ROLE_USER" ) );
    return new UsernamePasswordAuthenticationToken( authentication.getName(), authentication.getCredentials(), grantedAuths );
  }

  @Override
  public boolean supports ( Class<? extends Object> authentication ) {
    return ( UsernamePasswordAuthenticationToken.class.isAssignableFrom( authentication ) );
  }
}

基本上,我启用了会话并让 Spring 在会话中为我保留请求,同时服务器与恐龙服务器对话并完成握手。完成后,向 Spring 询问先前请求的参数以通过 Spring Security 继续授权。


推荐阅读