Custom Authentication Manager con Spring Security e Java Configuration

Sto usando Spring Security con SpringMVC per creare un’applicazione web (mi riferirò a questo come WebApp per chiarezza) che parla a un’applicazione esistente (mi riferirò a questo come BackendApp).

Voglio debind le responsabilità di autenticazione a BackendApp (in modo che non sia necessario sincronizzare le due applicazioni).

Per implementarlo, vorrei che la WebApp (che esegue la sicurezza di spring) comunichi a BackendApp via REST con il nome utente e la password forniti dall’utente in un modulo e autenticati in base al fatto che la risposta di BackendApp sia 200 OK o 401 Non autorizzato.

Capisco che avrò bisogno di scrivere un Authentication Manager personalizzato per farlo, ma sono molto nuovo per la spring e non riesco a trovare alcuna informazione su come implementarlo.

Credo che dovrò fare qualcosa del genere:

public class CustomAuthenticationManager implements AuthenticationManager{ @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String username = authentication.getName(); String pw = authentication.getCredentials().toString(); // Code to make rest call here and check for OK or Unauthorised. // What do I return? } } 

Devo impostare authentication.setAuthenticated (true) se ha successo e false se diversamente e questo è vero?

Una volta scritto questo, come posso configurare la sicurezza di spring per utilizzare questo gestore di autenticazione usando un file di configurazione java?

Grazie in anticipo per qualsiasi assistenza.

Dai un’occhiata al mio esempio qui sotto. Devi restituire un UsernamePasswordAuthenticationToken. Contiene il principal e le GrantedAuthorities. Spero di poter aiutare 🙂

 public Authentication authenticate(Authentication authentication) throws AuthenticationException { String username = authentication.getPrincipal() + ""; String password = authentication.getCredentials() + ""; User user = userRepo.findOne(username); if (user == null) { throw new BadCredentialsException("1000"); } if (user.isDisabled()) { throw new DisabledException("1001"); } if (!encoder.matches(password, user.getPassword())) { throw new BadCredentialsException("1000"); } List userRights = rightRepo.getUserRights(username); return new UsernamePasswordAuthenticationToken(username, password, userRights.stream().map(x -> new SimpleGrantedAuthority(x.getName())).collect(Collectors.toList())); } 

PS: userRepo e rightRepo sono archivi Spring-Data-JPA che accedono al mio User-DB personalizzato

SpringSecurity JavaConfig:

 @Configuration @EnableWebMvcSecurity public class MySecurityConfiguration extends WebSecurityConfigurerAdapter { public MySecurityConfiguration() { super(false); } @Override protected AuthenticationManager authenticationManager() throws Exception { return new ProviderManager(Arrays.asList((AuthenticationProvider) new AuthProvider())); } } 

Nel suo modo più semplice:

 @Override public Authentication authenticate(Authentication auth) throws AuthenticationException { String username = auth.getName(); String password = auth.getCredentials().toString(); // to add more logic List grantedAuths = new ArrayList<>(); grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER")); return new UsernamePasswordAuthenticationToken(username, password, grantedAuths); } 

Per prima cosa devi configurare la sicurezza Spring per utilizzare il tuo AuthenticationProvider personalizzato. Quindi, nel tuo spring-security.xml (o file di configurazione equivalente) devi definire quale class sta implementando questa funzione. Per esempio:

       

In secondo luogo è necessario implementare AuthenticationProvider come nell’esempio. Specialmente il metodo autentica (autenticazione di autenticazione) in cui deve essere la tua chiamata di rest. Per esempio:

 public Authentication authenticate(Authentication authentication) throws AuthenticationException { User user = null; try { //use a rest service to find the user. //Spring security provides user login name in authentication.getPrincipal() user = userRestService.loadUserByUsername(authentication.getPrincipal().toString()); } catch (Exception e) { log.error("Error loading user, not found: " + e.getMessage(), e); } if (user == null) { throw new UsernameNotFoundException(String.format("Invalid credentials", authentication.getPrincipal())); } else if (!user.isEnabled()) { throw new UsernameNotFoundException(String.format("Not found enabled user for username ", user.getUsername())); } //check user password stored in authentication.getCredentials() against stored password hash if (StringUtils.isBlank(authentication.getCredentials().toString()) || !passwordEncoder.isPasswordValid(user.getPasswordHash(), authentication.getCredentials().toString()) { throw new BadCredentialsException("Invalid credentials"); } //doLogin makes whatever is necesary when login is made (put info in session, load other data etc..) return doLogin(user); } 

La mia soluzione è quasi la stessa della prima risposta:

1) È necessaria una class che implementa il provider di autenticazione

 @Service @Configurable public class CustomAuthenticationProvider implements AuthenticationProvider { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { // Your code of custom Authentication } } 

2) Di fronte alla prima risposta non è necessario avere il seguente codice in WebSecurityConfiguration se si dispone solo di questo provider personalizzato.

 @Override protected AuthenticationManager authenticationManager() throws Exception { return new ProviderManager(Arrays.asList((AuthenticationProvider) new AuthProvider())); } 

Il problema è che Spring cerca i provider disponibili e usa l’impostazione predefinita se non viene trovato nient’altro. Ma poiché hai l’implementazione di AuthenticationProvider, la tua implementazione verrà utilizzata.