我有两个 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()` 方法。如能就如何解决这个问题提出建议,我将不胜感激。
暂无答案!
目前还没有任何答案,快来回答吧!