Jakarta EE 8 sikkerhets-API

1. Oversikt

Jakarta EE 8 Security API er den nye standarden og en bærbar måte å håndtere sikkerhetsproblemer i Java-containere.

I denne artikkelen, vi ser på de tre kjernefunksjonene i API:

  1. HTTP-autentiseringsmekanisme
  2. Identitetsbutikk
  3. Sikkerhetskontekst

Vi forstår først hvordan du konfigurerer de medfølgende implementeringene og deretter hvordan vi implementerer en tilpasset implementering.

2. Maven-avhengigheter

For å sette opp Jakarta EE 8 Security API, trenger vi enten en server-gitt implementering eller en eksplisitt en.

2.1. Bruke serverimplementeringen

Jakarta EE 8-kompatible servere gir allerede en implementering av Jakarta EE 8 Security API, og derfor trenger vi bare Jakarta EE Web Profile API Maven artefakt:

  javax javaee-web-api 8.0 gitt 

2.2. Bruke en eksplisitt implementering

Først spesifiserer vi Maven-gjenstanden for Jakarta EE 8 Security API:

  javax.security.enterprise javax.security.enterprise-api 1.0 

Og så legger vi til en implementering, for eksempel Soteria - referanseimplementeringen:

  org.glassfish.soteria javax.security.enterprise 1.0 

3. HTTP-autentiseringsmekanisme

Før Jakarta EE 8 har vi konfigurert autentiseringsmekanismer erklærende gjennom web.xml fil.

I denne versjonen har Jakarta EE 8 Security API designet den nye HttpAuthenticationMechanism grensesnitt som erstatning. Derfor kan nettapplikasjoner nå konfigurere autentiseringsmekanismer ved å tilby implementeringer av dette grensesnittet.

Heldigvis gir containeren allerede en implementering for hver av de tre autentiseringsmetodene som er definert av Servlet-spesifikasjonen: Grunnleggende HTTP-autentisering, formbasert autentisering og tilpasset formbasert autentisering.

Det gir også en kommentar som utløser hver implementering:

  1. @BasicAuthenticationMechanismDefinition
  2. @FormAuthenticationMechanismDefinition
  3. @CustomFormAuthenrticationMechanismDefinition

3.1. Grunnleggende HTTP-godkjenning

Som nevnt ovenfor kan et webapplikasjon konfigurere Basic HTTP Authentication bare ved å bruke @BasicAuthenticationMechanismDefinisjon kommentar på en CDI bønne:

@BasicAuthenticationMechanismDefinition (realmName = "userRealm") @ApplicationScoped offentlig klasse AppConfig {}

På dette tidspunktet søker Servlet-containeren og etablerer den angitte implementeringen av HttpAuthenticationMechanism grensesnitt.

Ved mottak av en uautorisert forespørsel utfordrer containeren klienten for å gi passende godkjenningsinformasjon via WWW-godkjenning svarhode.

WWW-Authenticate: Basic realm = "userRealm"

Klienten sender deretter brukernavnet og passordet, atskilt med et kolon ":" og kodet i Base64, via Autorisasjon forespørselstittel:

// bruker = baeldung, passord = baeldung Autorisasjon: Grunnleggende YmFlbGR1bmc6YmFlbGR1bmc = 

Merk at dialogboksen som presenteres for legitimasjon kommer fra nettleseren og ikke fra serveren.

3.2. Formbasert HTTP-autentisering

De @FormAuthenticationMechanismDefinition kommentar utløser en skjemabasert autentisering som definert av Servlet-spesifikasjonen.

Da har vi muligheten til å spesifisere påloggings- og feilsidene eller bruke standard rimelige /Logg Inn og /innloggingsfeil:

@FormAuthenticationMechanismDefinition (loginToContinue = @LoginToContinue (loginPage = "/login.html", errorPage = "/login-error.html")) @ApplicationScoped offentlig klasse AppConfig {}

Som et resultat av påkalling loginSide, serveren skal sende skjemaet til klienten:

Kunden skal deretter sende skjemaet til en forhåndsdefinert prosess for godkjenning av støtte, levert av containeren.

3.3. Tilpasset skjemabasert HTTP-autentisering

Et webapplikasjon kan utløse den tilpassede skjemabaserte autentiseringsimplementeringen ved å bruke merknaden @CustomFormAuthenticationMechanismDefinition:

@CustomFormAuthenticationMechanismDefinition (loginToContinue = @LoginToContinue (loginPage = "/login.xhtml")) @ApplicationScoped offentlig klasse AppConfig {}

Men i motsetning til standard skjemabasert godkjenning, konfigurerer vi en tilpasset påloggingsside og påkaller SecurityContext.authenticate () metode som en godkjenningsprosess for sikkerhetskopiering.

La oss ta en titt på støtten LoginBean også som inneholder påloggingslogikken:

@Named @RequestScoped offentlig klasse LoginBean {@Inject private SecurityContext securityContext; @NotNull private String brukernavn; @NotNull privat strengpassord; public void login () {Credential credential = new UsernamePasswordCredential (username, new Password (password))); AuthenticationStatus status = securityContext .authenticate (getHttpRequestFromFacesContext (), getHttpResponseFromFacesContext (), withParams (). Credential (credential)); // ...} // ...}

Som et resultat av å påkalle skikken login.xhtml side, sender klienten det mottatte skjemaet til Logg innBønne 's Logg Inn() metode:

//... 

3.4. Tilpasset autentiseringsmekanisme

De HttpAuthenticationMechanism grensesnitt definerer tre metoder. Det viktigste er validateRequest () som vi må gi en implementering.

Standard oppførsel for de to andre metodene, secureResponse () og cleanSubject (), er tilstrekkelig i de fleste tilfeller.

La oss ta en titt på et eksempel på implementering:

@ApplicationScoped offentlig klasse CustomAuthentication implementerer HttpAuthenticationMechanism {@Override public AuthenticationStatus validateRequest (HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMsgContext) throws AuthenticationException = String. Strengpassord = respons.getParameter ("passord"); // mocking UserDetail, men i det virkelige liv kan vi få det fra en database UserDetail userDetail = findByUserNameAndPassword (brukernavn, passord); hvis (userDetail! = null) {return httpMsgContext.notifyContainerAboutLogin (ny CustomPrincipal (userDetail), ny HashSet (userDetail.getRoles ())); } returner httpMsgContext.responseUnauthorized (); } // ...}

Her gir implementeringen forretningslogikken til valideringsprosessen, men i praksis anbefales det å delegere til IdentityStore gjennom IdentityStoreHandler by påkaller validere.

Vi har også kommentert implementeringen med @ApplicationScoped kommentar ettersom vi trenger å gjøre den CDI-aktivert.

Etter en gyldig bekreftelse av legitimasjonen og en eventuell henting av brukerroller, implementeringen skal varsle containeren da:

HttpMessageContext.notifyContainerAboutLogin (rektor, sett grupper)

3.5. Håndhever Servlet Security

En webapplikasjon kan håndheve sikkerhetsbegrensninger ved å bruke @ServletSikkerhet kommentar om en Servlet-implementering:

@WebServlet ("/ secure") @ServletSecurity (value = @HttpConstraint (rollerAllowed = {"admin_role"}), httpMethodConstraints = {@HttpMethodConstraint (value = "GET" ,rollerAllowed = {"user_role"}), @HttpMethodCon = "POST" ,rollerAllowed = {"admin_role"})}) offentlig klasse SecuredServlet utvider HttpServlet {}

Denne kommentaren har to attributter - httpMethodConstraints og verdi; httpMethodConstraints brukes til å spesifisere en eller flere begrensninger, hver representerer en tilgangskontroll til en HTTP-metode ved en liste over tillatte roller.

Containeren vil da sjekke for hver url-mønster og HTTP-metoden, hvis den tilkoblede brukeren har den passende rollen for tilgang til ressursen.

4. Identitetsbutikk

Denne funksjonen er abstrahert av de IdentityStore grensesnitt, og det brukes til å validere legitimasjon og til slutt hente gruppemedlemskap. Med andre ord, det kan gi muligheter for autentisering, autorisasjon eller begge deler.

IdentityStore er ment og oppmuntret til å bli brukt av HttpAuthenticationMecanism gjennom en kalt IdentityStoreHandler grensesnitt. En standardimplementering av IdentityStoreHandler er levert av Servletcontainer.

En applikasjon kan gi implementeringen av programmet IdentityStore eller bruker en av de to innebygde implementeringene som leveres av containeren for Database og LDAP.

4.1. Innebygde identitetsbutikker

Den Jakarta EE-kompatible serveren skal gi implementeringer for de to identitetsbutikkene: Database og LDAP.

Databasen IdentityStore implementering initialiseres ved å sende konfigurasjonsdata til @DataBaseIdentityStoreDefinition kommentar:

@DatabaseIdentityStoreDefinition (dataSourceLookup = "java: comp / env / jdbc / securityDS", callerQuery = "velg passord fra brukere der brukernavn =?", GroupsQuery = "velg GROUPNAME fra grupper der brukernavn =?", Prioritet = 30) @ApplicationScoped offentlig klasse AppConfig {}

Som konfigurasjonsdata, vi trenger en JNDI-datakilde til en ekstern database, to JDBC-setninger for å sjekke innringer og gruppene hans, og til slutt er det konfigurert en prioritetsparameter som brukes i tilfeller med flere lagre.

IdentityStore med høy prioritet behandles senere av IdentityStoreHandler.

Som databasen, LDAP IdentityStore-implementering initialiseres gjennom @LdapIdentityStoreDefinition ved å overføre konfigurasjonsdata:

@LdapIdentityStoreDefinition (url = "ldap: // localhost: 10389", callerBaseDn = "ou = ringer, dc = baeldung, dc = com", groupSearchBase = "ou = group, dc = baeldung, dc = com", groupSearchFilter = " (& (medlem =% s) (objectClass = groupOfNames)) ") @ApplicationScoped offentlig klasse AppConfig {}

Her trenger vi URL-en til en ekstern LDAP-server, hvordan du søker i innringeren i LDAP-katalogen, og hvordan du henter gruppene hans.

4.2. Implementere en egendefinert IdentityStore

De IdentityStore grensesnitt definerer fire standardmetoder:

standard CredentialValidationResult validate (Credential credential) default Set getCallerGroups (CredentialValidationResult validationResult) default int prioritet () default Set validationTypes ()

De prioritet() metoden returnerer en verdi for rekkefølgen av iterasjon denne implementeringen blir behandlet av IdentityStoreHandler. An IdentityStore med lavere prioritet behandles først.

Som standard er en IdentityStore behandler begge legitimasjonene (ValidationType.VALIDATE) og gruppeinnhenting (ValidationType.PROVIDE_GROUPS). Vi kan overstyre denne oppførselen slik at den bare kan gi én mulighet.

Dermed kan vi konfigurere IdentityStore skal bare brukes til validering av legitimasjon:

@ Override public Sett validationTypes () {return EnumSet.of (ValidationType.VALIDATE); }

I dette tilfellet bør vi gi en implementering for validere() metode:

@ApplicationScoped offentlig klasse InMemoryIdentityStore implementerer IdentityStore {// init fra en fil eller kodede private Map-brukere = nye HashMap (); @ Override public int prioritet () {return 70; } @ Override public Sett validationTypes () {return EnumSet.of (ValidationType.VALIDATE); } offentlig CredentialValidationResult validere (UsernamePasswordCredential credential) {UserDetails user = users.get (credential.getCaller ()); hvis (credential.compareTo (user.getLogin (), user.getPassword ())) {return new CredentialValidationResult (user.getLogin ()); } returner INVALID_RESULT; }}

Eller vi kan velge å konfigurere IdentityStore slik at den bare kan brukes til gruppegjenfinning:

@Override public Sett validationTypes () {return EnumSet.of (ValidationType.PROVIDE_GROUPS); }

Vi bør da gi en implementering for getCallerGroups () metoder:

@ApplicationScoped offentlig klasse InMemoryIdentityStore implementerer IdentityStore {// init fra en fil eller kodede private Map-brukere = nye HashMap (); @ Override public int prioritet () {return 90; } @ Override public Sett validationTypes () {return EnumSet.of (ValidationType.PROVIDE_GROUPS); } @ Override public Sett getCallerGroups (CredentialValidationResult validationResult) {UserDetails user = users.get (validationResult.getCallerPrincipal (). GetName ()); returner nytt HashSet (user.getRoles ()); }}

Fordi IdentityStoreHandler forventer at implementeringen skal være en CDI-bønne, vi pynter den med ApplicationScoped kommentar.

5. Security Context API

Jakarta EE 8 Security API tilbyr et tilgangspunkt til programmatisk sikkerhet gjennom SecurityContext grensesnitt. Det er et alternativ når den deklarative sikkerhetsmodellen håndhevet av containeren ikke er tilstrekkelig.

En standardimplementering av SecurityContext grensesnittet bør leveres i løpet av en CDI-bønne, og derfor må vi injisere det:

@Inject SecurityContext securityContext;

På dette tidspunktet kan vi autentisere brukeren, hente en godkjent, sjekke hans rollemedlemskap og gi eller nekte tilgang til nettressurs gjennom de fem tilgjengelige metodene.

5.1. Henter anropsdata

I tidligere versjoner av Jakarta EE, vil vi hente Rektor eller sjekk rollemedlemskapet forskjellig i hver container.

Mens vi bruker getUserPrincipal () og isUserInRole () metoder for HttpServletRequest i en servletbeholder, lignende metoder getCallerPrincipal () og isCallerInRole () -metoder av EJBContext brukes i EJB Container.

Det nye Jakarta EE 8 Security API har standardisert dette av gir en lignende metode gjennom SecurityContext grensesnitt:

Rektor getCallerPrincipal (); boolsk isCallerInRole (strengrolle); Sett getPrincipalsByType (klassen type);

De getCallerPrincipal () metoden returnerer en containerspesifikk representasjon av den autentiserte innringer mens getPrincipalsByType () metoden henter alle prinsippene av en gitt type.

Det kan være nyttig hvis den applikasjonsspesifikke innringeren er forskjellig fra beholderen.

5.2. Testing for tilgang til nettressurser

Først må vi konfigurere en beskyttet ressurs:

@WebServlet ("/ protectedServlet") @ServletSecurity (@HttpConstraint (rollerAllowed = "USER_ROLE")) offentlig klasse ProtectedServlet utvider HttpServlet {// ...}

Og så, for å sjekke tilgang til denne beskyttede ressursen, bør vi påberope oss hasAccessToWebResource () -metoden:

securityContext.hasAccessToWebResource ("/ protectedServlet", "GET");

I dette tilfellet returnerer metoden sant hvis brukeren er i rolle USER_ROLE.

5.3. Godkjenne anroperen programmatisk

Et program kan programmatisk utløse godkjenningsprosessen ved å påkalle godkjenne ():

AuthenticationStatus authenticate (HttpServletRequest request, HttpServletResponse response, AuthenticationParameters parameters);

Containeren blir deretter varslet og vil igjen påkalle autentiseringsmekanismen konfigurert for applikasjonen. AuthenticationParameters parameter gir legitimasjon til HttpAuthenticationMechanism:

withParams (). legitimasjon (legitimasjon)

De SUKSESS og SEND_FAILURE verdiene til AuthenticationStatus designe en vellykket og mislykket godkjenning mens SEND_CONTINUE signaliserer en pågående status for autentiseringsprosessen.

6. Kjøre eksemplene

For å markere disse eksemplene har vi brukt den nyeste utviklingen av Open Liberty Server som støtter Jakarta EE 8. Denne lastes ned og installeres takket være Liberty-Maven-plugin-programmet som også kan distribuere applikasjonen og starte serveren.

For å kjøre eksemplene, er det bare å få tilgang til den tilsvarende modulen og påkalle denne kommandoen:

mvn ren pakkefrihet: kjør

Som et resultat vil Maven laste ned serveren, bygge, distribuere og kjøre applikasjonen.

7. Konklusjon

I denne artikkelen dekket vi konfigurasjonen og implementeringen av hovedfunksjonene i den nye Jakarta EE 8 Security API.

Først startet vi med å vise hvordan du konfigurerer standard innebygde autentiseringsmekanismer og hvordan du implementerer en tilpasset. Senere så vi hvordan vi skulle konfigurere den innebygde Identity Store og hvordan vi implementerte en tilpasset butikk. Og til slutt så vi hvordan man kaller metoder for SecurityContext.

Som alltid er kodeeksemplene for denne artikkelen tilgjengelig på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found