java—如何从authenticationmanager调用的多个AuthenticationProviders中选择特定的身份验证方法

beq87vna  于 2021-07-24  发布在  Java
关注(0)|答案(0)|浏览(246)

我有两个 AuthenticaionProvider 在我的应用程序中 AWS Cognito 对用户进行身份验证。
第一个 Admin ```
@Component
@RequiredArgsConstructor
public class CustomAuthenticationProvider implements AuthenticationProvider {

private final CognitoAuthenticationService cognitoService;
private final AuthenticationHelper authenticationHelper;

@SuppressWarnings("unchecked")
@Override
public Authentication authenticate(Authentication authentication) {
AuthenticationRequest authenticationRequest;

if (isNotNull(authentication)) {
  authenticationRequest = new AuthenticationRequest();
  Map<String, String> credentials = (Map<String, String>) authentication.getCredentials();
  authenticationRequest.setNewPassword(credentials.get(NEW_PASSWORD_KEY));
  authenticationRequest.setPassword(credentials.get(PASSWORD));
  authenticationRequest.setUsername(authentication.getName());

  SpringSecurityUser userAuthenticated = cognitoService.authenticate(authenticationRequest);
  if (isNotNull(userAuthenticated)) {

    Map<String, String> authenticatedCredentials =
        authenticationHelper.prepareAuthenticationResponse(userAuthenticated);
    return new UsernamePasswordAuthenticationToken(
        userAuthenticated.getUsername(),
        authenticatedCredentials,
        userAuthenticated.getAuthorities());
  } else {
    return null;
  }
} else {
  throw new UsernameNotFoundException("No application user for given username");
}

}

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

其次是传统的 `User` ```
@Component
@RequiredArgsConstructor
public class UserAuthenticationProvider implements AuthenticationProvider {

  private final CognitoAuthenticationService cognitoAuthenticationService;
  private final AuthenticationHelper authenticationHelper;

  @SuppressWarnings("unchecked")
  @Override
  public Authentication authenticate(Authentication authentication) {

    if (isNotNull(authentication)) {
      AuthenticationRequest authenticationRequest = new AuthenticationRequest();
      Map<String, String> credentials = (Map<String, String>) authentication.getCredentials();
      authenticationRequest.setPassword(credentials.get(PASSWORD));
      authenticationRequest.setUsername(authentication.getName());

      SpringSecurityUser userAuthenticated =
          cognitoAuthenticationService.authenticateUser(authenticationRequest);

      if (isNotNull(userAuthenticated)) {
        Map<String, String> authenticatedCredentials =
            authenticationHelper.prepareAuthenticationResponse(userAuthenticated);
        return new UsernamePasswordAuthenticationToken(
            userAuthenticated.getUsername(),
            authenticatedCredentials,
            userAuthenticated.getAuthorities());
      } else {
        return null;
      }
    } else {
      throw new UsernameNotFoundException("No application user for given username");
    }
  }

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

以上提供者使用的方法 authenticate 以及 authenticateUser ```
public SpringSecurityUser authenticate(AuthenticationRequest authenticationRequest) {

AuthenticationResultType authenticationResult;

try {

  final Map<String, String> authParams = new HashMap<>();
  authParams.put(USERNAME, authenticationRequest.getUsername());
  authParams.put(PASSWORD, authenticationRequest.getPassword());

  final AdminInitiateAuthRequest authRequest =
      new AdminInitiateAuthRequest()
          .withAuthFlow(AuthFlowType.ADMIN_NO_SRP_AUTH)
          .withClientId(cognitoConfig.getClientId())
          .withUserPoolId(cognitoConfig.getPoolId())
          .withAuthParameters(authParams);

  AdminInitiateAuthResult result = awsCognitoIdentityProvider.adminInitiateAuth(authRequest);

  // Has a Challenge
  if (StringUtils.isNotBlank(result.getChallengeName())) {

    // If the challenge is required new Password validates if it has the new password variable.
    if (NEW_PASSWORD_REQUIRED.equals(result.getChallengeName())) {

      if (isNull(authenticationRequest.getNewPassword())) {
        throw new CognitoException(
            "User must provide a new password",
            CognitoException.USER_MUST_CHANGE_PASS_WORD_EXCEPTION_CODE,
            result.getChallengeName());
      } else {
        // we still need the username

        final Map<String, String> challengeResponses = new HashMap<>();
        challengeResponses.put(USERNAME, authenticationRequest.getUsername());
        challengeResponses.put(PASSWORD, authenticationRequest.getPassword());

        // add the new password to the params map
        challengeResponses.put(NEW_PASSWORD, authenticationRequest.getNewPassword());

        // populate the challenge response
        final AdminRespondToAuthChallengeRequest request =
            new AdminRespondToAuthChallengeRequest()
                .withChallengeName(ChallengeNameType.NEW_PASSWORD_REQUIRED)
                .withChallengeResponses(challengeResponses)
                .withClientId(cognitoConfig.getClientId())
                .withUserPoolId(cognitoConfig.getPoolId())
                .withSession(result.getSession());

        AdminRespondToAuthChallengeResult resultChallenge =
            awsCognitoIdentityProvider.adminRespondToAuthChallenge(request);
        authenticationResult = resultChallenge.getAuthenticationResult();
      }
    } else {
      // has another challenge
      throw new CognitoException(
          result.getChallengeName(), CognitoException.USER_MUST_DO_ANOTHER_CHALLENGE);
    }

  } else {
    // Doesn't have a challenge
    authenticationResult = result.getAuthenticationResult();
  }

  var userAuthenticated =
      SpringSecurityUser.builder()
          .username(authenticationRequest.getUsername())
          .password(authenticationRequest.getPassword())
          .accessToken(authenticationResult.getAccessToken())
          .refreshToken(authenticationResult.getRefreshToken())
          .expiresIn(authenticationResult.getExpiresIn())
          .tokenType(authenticationResult.getTokenType())
          .idToken(authenticationResult.getIdToken())
          .build();

  log.info("User with username: {} authenticated", authenticationRequest.getUsername());

  return userAuthenticated;
} catch (AWSCognitoIdentityProviderException e) {
  log.error(e.getMessage(), e);
  throw new CognitoException(
      e.getMessage(), e.getErrorCode(), e.getMessage() + e.getErrorCode());
} catch (CognitoException cognitoException) {
  throw cognitoException;
} catch (Exception e) {
  log.error(e.getMessage(), e);
  throw new CognitoException(
      e.getMessage(), CognitoException.GENERIC_EXCEPTION_CODE, e.getMessage());
}

}

public SpringSecurityUser authenticateUser(AuthenticationRequest request) {

try {

  final var authRequest =
      new InitiateAuthRequest()
          .withAuthFlow(AuthFlowType.USER_PASSWORD_AUTH)
          .withClientId(cognitoConfig.getClientId())
          .withAuthParameters(prepareAuthorizationParameters(request));

  var initiateAuthResult = awsCognitoIdentityProvider.initiateAuth(authRequest);
  var authenticationResult = initiateAuthResult.getAuthenticationResult();

  Set<GrantedAuthority> grantedAuthorities = prepareUserAuthorities(request.getUsername());

  var userAuthenticated =
      SpringSecurityUser.builder()
          .username(request.getUsername())
          .password(request.getPassword())
          .accessToken(authenticationResult.getAccessToken())
          .refreshToken(authenticationResult.getRefreshToken())
          .expiresIn(authenticationResult.getExpiresIn())
          .tokenType(authenticationResult.getTokenType())
          .idToken(authenticationResult.getIdToken())
          .authorities(grantedAuthorities)
          .build();

  log.info(USER_SUCCESSFULLY_AUTHENTICATED, request.getUsername(), grantedAuthorities);
  return userAuthenticated;
} catch (AWSCognitoIdentityProviderException e) {
  log.error(e.getMessage(), e);
  throw new CognitoException(
      e.getMessage(), e.getErrorCode(), e.getMessage() + e.getErrorCode());
} catch (CognitoException cognitoException) {
  throw cognitoException;
} catch (Exception e) {
  log.error(e.getMessage(), e);
  throw new CognitoException(
      e.getMessage(), CognitoException.GENERIC_EXCEPTION_CODE, e.getMessage());
}

}

我的配置如下:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableTransactionManagement
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

private CustomAuthenticationProvider authProvider;
private UserAuthenticationProvider userAuthProvider;
private AccountControllerExceptionHandler exceptionHandler;
private static final String LOGIN_URL = "/auth/login";
private static final String LOGOUT_URL = "/auth/signOut";

@Autowired
public WebSecurityConfiguration(
CustomAuthenticationProvider authProvider,
UserAuthenticationProvider userAuthProvider,
AccountControllerExceptionHandler exceptionHandler) {
this.authProvider = authProvider;
this.userAuthProvider = userAuthProvider;
this.exceptionHandler = exceptionHandler;
}

public WebSecurityConfiguration() {
super(true);
}

@Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authProvider).eraseCredentials(false);
auth.authenticationProvider(userAuthProvider).eraseCredentials(false);
}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

@Override
public void configure(WebSecurity web) {
// TokenAuthenticationFilter will ignore the below paths
web.ignoring().antMatchers("/auth");
web.ignoring().antMatchers("/auth/");
web.ignoring().antMatchers("/v2/api-docs");
web.ignoring().antMatchers(GET, "/nutrition/api/
");
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.addFilterAfter(corsFilter(), ExceptionTranslationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(new SecurityAuthenticationEntryPoint())
.accessDeniedHandler(new RestAccessDeniedHandler())
.and()
.anonymous()
.and()
.sessionManagement()
.sessionCreationPolicy(STATELESS)
.and()
.authorizeRequests()
.antMatchers("/auth")
.permitAll()
.anyRequest()
.authenticated()
.and()
// Instantiate a new instance of the filter
.addFilterBefore(
awsCognitoJwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.formLogin(
formLogin -> formLogin.loginProcessingUrl(LOGIN_URL).failureHandler(exceptionHandler))
.logout(logout -> logout.permitAll().logoutUrl(LOGOUT_URL))
.csrf(AbstractHttpConfigurer::disable);
}

private CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader(ORIGIN);
config.addAllowedHeader(CONTENT_TYPE);
config.addAllowedHeader(ACCEPT);
config.addAllowedHeader(AUTHORIZATION);
config.addAllowedMethod(GET);
config.addAllowedMethod(PUT);
config.addAllowedMethod(POST);
config.addAllowedMethod(OPTIONS);
config.addAllowedMethod(DELETE);
config.addAllowedMethod(PATCH);
config.setMaxAge(3600L);

source.registerCorsConfiguration("/v2/api-docs", config);
source.registerCorsConfiguration("/**", config);
return new CorsFilter();

}

private AwsCognitoJwtAuthenticationFilter awsCognitoJwtAuthenticationFilter() {
return new AwsCognitoJwtAuthenticationFilter(new AwsCognitoIdTokenProcessor(), exceptionHandler);
}
}

我的控制器看起来像:

@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthenticationController {

private final AuthenticationManager authenticationManager;
private final CognitoAuthenticationService authService;

@SuppressWarnings("unchecked")
@CrossOrigin
@PostMapping("/login")
public ResponseEntity authenticationRequest(
@RequestBody AuthenticationRequest authRequest) {

String accessToken;

Map<String, String> authorizationParameters =
    authService.prepareAuthorizationParameters(authRequest);

Authentication authentication =
    authenticationManager.authenticate(
        new UsernamePasswordAuthenticationToken(
            authRequest.getUsername(), authorizationParameters));

//todo verify if above can be simplified
Map<String, String> authenticatedCredentials =
    (Map<String, String>) authentication.getCredentials();

accessToken = authenticatedCredentials.get(ACCESS_TOKEN_KEY);

UserResponse userResponse = authService.getUserInfo(accessToken);

SecurityContextHolder.getContext().setAuthentication(authentication);

AuthenticationResponse authResponse =
    AuthenticationResponse.builder()
        .accessToken(authenticatedCredentials.get(ID_TOKEN_KEY))
        .expiresIn(authenticatedCredentials.get(EXPIRES_IN_KEY))
        .sessionToken(accessToken)
        .userData(userResponse)
        .build();

return ResponseEntity.ok(authResponse);

}

@SuppressWarnings("unchecked")
@CrossOrigin
@PostMapping("/loginUser")
public ResponseEntity authenticationUserRequest(
@RequestBody AuthenticationRequest authRequest) {

String accessToken;

Map<String, String> authorizationParameters =
    authService.prepareAuthorizationParameters(authRequest);

Authentication authentication =
    authenticationManager.authenticate(
        new UsernamePasswordAuthenticationToken(
            authRequest.getUsername(), authorizationParameters));

Map<String, String> authenticatedCredentials =
    (Map<String, String>) authentication.getCredentials();

accessToken = authenticatedCredentials.get(ACCESS_TOKEN_KEY);

UserResponse userResponse = authService.getUserInfo(accessToken);

SecurityContextHolder.getContext().setAuthentication(authentication);

AuthenticationResponse authResponse =
    AuthenticationResponse.builder()
        .accessToken(authenticatedCredentials.get(ID_TOKEN_KEY))
        .expiresIn(authenticatedCredentials.get(EXPIRES_IN_KEY))
        .sessionToken(accessToken)
        .userData(userResponse)
        .build();

return ResponseEntity.ok(authResponse);

}
}

还有一个问题,即我无法选择引擎盖下的哪个方法将由 `AuthenticationManager` 在 `Controller` 生产线: `authenticationManager.authenticate()` . 我知道将身份验证分为两种方式,比如admin/user,并不是最好的选择,在将来,我会去掉这一种,但这不会改变我想为social的身份验证创建另一个提供者的事实,也会有同样的问题,因为到目前为止,我没有选择正确的身份验证的选项 `authentication()` 方法。如能就如何解决这个问题提出建议,我将不胜感激。

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题