Brugerstyring med Okta SDK og Spring Boot
I dette indlæg vil jeg vise, hvordan vi kan bygge brugeradministration og godkendelse med Okta SDK og Spring Boot.
Introduktion
Som en del af enhver applikation skal udviklere være forsigtige med, hvordan de opbygger godkendelse. På trods af at vi har brugt formularbaseret godkendelse i lang tid, er det ikke den mest sikre. I dette indlæg planlægger jeg at vise formularbaseret godkendelse, hvor brugere ikke nødvendigvis bliver godkendt ved at validere deres krypterede adgangskode mod adgangskoden, der er gemt i en database. Hvis du vil lære mere om Spring Security med forskellige autentificeringsflows, har jeg for nylig udgivet en bog Simplifying Spring Security. Du kan købe bogen her.
Okta er en identitetsudbyder. Det er et program, der giver brugeradministration og godkendelse med forskellige protokoller.
Okta SDK API'er
Okta tilbyder to biblioteker okta-sdk-java
og okta-auth-java
til brugerstyrings-API'er og godkendelse.
Er disse biblioteker rigtige for dig? Dette afhænger af din use case. Okta tilbyder også okta-spring-boot-starter
bibliotek til at bruge okta til forskellige OAuth-flows i din Spring Boot-applikation. Vi vil ikke bruge dette bibliotek i denne demo.
Du kan finde flere detaljer om disse biblioteker her og her.
Inkluder disse biblioteker i dit projekt som følger:
implementation 'com.okta.authn.sdk:okta-authn-sdk-api:2.0.1'
runtimeOnly 'com.okta.authn.sdk:okta-authn-sdk-impl:2.0.1'
runtimeOnly 'com.okta.sdk:okta-sdk-httpclient:3.0.1'
Brugeradministration med Okta SDK i Spring Boot Application
I denne demo har jeg en prøveanvendelse af To-Do List. Når en bruger starter programmet, vil brugeren se en login-skærm. Den har mulighed for tilmelding. Hvis brugeren ikke findes i applikationen, skal brugeren oprette en konto.
På tilmeldingssiden, når en bruger indtaster knappen "Send", gemmer vi brugeren i vores database og kalder derefter Okta SDK API for at oprette brugeren på Okta-siden.
For at opnå dette har vi brug for Okta Client.
@Bean
public Client client()
{
Client clientConfig =
Clients.builder().setOrgUrl("https://oktadomainurl").setClientCredentials(new TokenClientCredentials(secret))
.build();
return clientConfig;
}
Som du kan se ovenfor, opretter vi en klient, som vi vil bruge til at kalde Okta API. `Hemmeligheden` er API-tokenet, du vil kunne finde i Okta admin UI. Hvis du ikke finder det, har du enten ikke administratorrettigheder, eller også har du ikke oprettet tokenet endnu. Der er en anden måde at oprette denne klient med et adgangstoken.
@Bean
public Client client()
{
Client clientConfig =
Clients.builder().setOrgUrl("https://oktadomainurl")
.setAuthorizationMode(AuthorizationMode.PRIVATE_KEY).setClientId("{clientId}")
.setScopes(new HashSet<>(Arrays.asList("okta.users.read", "okta.apps.read")))
.setPrivateKey("/path/to/yourPrivateKey.pem")
return clientConfig;
}
Fordelen ved denne klientkonfiguration er, at du ikke behøver at kende API-adgangstoken, der er oprettet baseret på administratorrettigheder.
Nu på min controller-side vil jeg bruge denne klient til at oprette bruger i Okta som nedenfor:
UserDto userDto = new UserDto();
userDto.setEmail(email);
userDto.setFirstName(firstname);
userDto.setLastName(lastname);
userDto.setPassword(encodedPassword);
userDto.setRole("ADMIN");
userDto.setEnabled(true);
UserDto returnedUser = usersManager.createUser(userDto);
LOGGER.info("Create the user in Okta");
User oktaUser = UserBuilder.instance().setEmail(returnedUser.getEmail())
.setFirstName(returnedUser.getFirstName())
.setLastName(returnedUser.getLastName())
.buildAndCreate(client);
Det dækker brugeradministrationsdelen. Du kan på samme måde ringe til GET
eller DELETE
API til at administrere brugere.
Brugergodkendelse
Nu kommer den kritiske del af autentificering. I mange virksomhedsapplikationer, når du bruger tredjeparts identitet, kommer problemet altid med synkronisering af brugerdata. Begge applikationer skal gemme brugerdata.
Til godkendelse skal vi bruge authenticationClient
bønne. Denne klient giver os mulighed for at kalde Okta API til godkendelse.
@Bean
public AuthenticationClient authenticationClient()
{
AuthenticationClient authenticationClient =
AuthenticationClients.builder()
.setOrgUrl("https://oktadomainurl")
.build();
return authenticationClient;
}
I vores sikkerhedskonfiguration vil jeg tilsidesætte det formularbaserede login med en tilpasset loginside.
@Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
@Bean(BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception
{
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception
{
httpSecurity.authorizeRequests()
.antMatchers("/js/**","/css/**","/img/**").permitAll()
.antMatchers("/signup","/forgotpassword").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
}
Som du ser i ovenstående kode, bruger jeg customAuthenticationProvider
, vil denne udbyder bruge authenticationClient
at godkende med Okta. Denne AuthenticationProvider vil se ud som nedenfor:
package com.betterjavacode.sss.todolist.clients;
import com.betterjavacode.sss.todolist.security.AuthenticationStateHandler;
import com.okta.authn.sdk.client.AuthenticationClient;
import com.okta.authn.sdk.resource.AuthenticationResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider
{
private static final Logger LOGGER = LoggerFactory.getLogger(CustomAuthenticationProvider.class);
@Autowired
private AuthenticationClient authenticationClient;
@Autowired
private AuthenticationStateHandler authenticationStateHandler;
@Override
public Authentication authenticate (Authentication authentication) throws AuthenticationException
{
String username = authentication.getName();
String password = authentication.getCredentials().toString();
String relayState = "/index";
AuthenticationResponse authnResponse = null;
try
{
LOGGER.info("Going to connect to Okta");
authnResponse = authenticationClient.authenticate(username, password.toCharArray(),
relayState,
authenticationStateHandler);
}
catch(com.okta.authn.sdk.AuthenticationException e)
{
LOGGER.error("Unable to authentcate the user", e);
}
if(authnResponse != null)
{
final List grantedAuths = new ArrayList<>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
final UserDetails principal = new User(username, password, grantedAuths);
final Authentication authen = new UsernamePasswordAuthenticationToken(principal,
password, grantedAuths);
return authen;
}
else
{
LOGGER.info("Unable to authenticate");
return null;
}
}
@Override
public boolean supports (Class<?> authentication)
{
return true;
}
}
Vi bruger authenticationClient
at kalde godkendelsesmetoden. AuthenticationStateHandler
håndterer grundlæggende statusgodkendelsen. Implementeringen af dette håndtag er som nedenfor:
package com.betterjavacode.sss.todolist.security;
import com.okta.authn.sdk.AuthenticationStateHandlerAdapter;
import com.okta.authn.sdk.resource.AuthenticationResponse;
import com.okta.commons.lang.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class AuthenticationStateHandler extends AuthenticationStateHandlerAdapter
{
private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationStateHandler.class);
@Override
public void handleUnknown (AuthenticationResponse unknownResponse)
{
// TO DO
}
@Override
public void handleSuccess (AuthenticationResponse successResponse)
{
if (Strings.hasLength(successResponse.getSessionToken()))
{
LOGGER.info("Login successful");
String relayState = successResponse.getRelayState();
String dest = relayState != null ? relayState : "/";
}
}
}
Det er alt. Dette dækker brugergodkendelse. Husk, at dette stadig er formularbaseret godkendelse, hvor du indtaster brugeroplysninger på din brugerdefinerede login-side og bag skærmen kalder Okta API for at godkende.
I min bog, Simplifying Spring Security, har jeg også tilføjet demoen til login med Okta OAuth.
Konklusion
I dette indlæg viste jeg, hvordan man bruger Okta SDK til brugeradministration og godkendelse med Spring Boot-applikationen. Hvis du har spørgsmål, er du velkommen til at sende mig en e-mail ved at abonnere på min blog her.