Jakarta EE 8 Sikkerheds-API

1. Oversigt

Jakarta EE 8 Security API er den nye standard og en bærbar måde at håndtere sikkerhedsproblemer i Java-containere på.

I denne artikel vi ser på de tre kerneegenskaber i API:

  1. HTTP-godkendelsesmekanisme
  2. Identitetsbutik
  3. Sikkerhedskontekst

Vi forstår først, hvordan man konfigurerer de leverede implementeringer, og derefter hvordan man implementerer en brugerdefineret.

2. Maven-afhængigheder

For at opsætte Jakarta EE 8 Security API har vi brug for enten en server-forudsat implementering eller en eksplicit.

2.1. Brug af serverimplementeringen

Jakarta EE 8-kompatible servere leverer allerede en implementering af Jakarta EE 8 Security API, og derfor har vi kun brug for Jakarta EE Web Profile API Maven artefakt:

  javax javaee-web-api 8.0 leveres 

2.2. Brug af en eksplicit implementering

Først angiver vi Maven-artefakten til Jakarta EE 8 Security API:

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

Og så tilføjer vi en implementering, for eksempel Soteria - referenceimplementeringen:

  org.glassfish.soteria javax.security.enterprise 1.0 

3. HTTP-godkendelsesmekanisme

Forud for Jakarta EE 8 har vi konfigureret godkendelsesmekanismer erklærende gennem web.xml fil.

I denne version har Jakarta EE 8 Security API designet det nye HttpAuthenticationMechanism interface som erstatning. Derfor kan webapplikationer nu konfigurere godkendelsesmekanismer ved at levere implementeringer af denne grænseflade.

Heldigvis leverer containeren allerede en implementering for hver af de tre godkendelsesmetoder, der er defineret af Servlet-specifikationen: Grundlæggende HTTP-godkendelse, formbaseret godkendelse og brugerdefineret formbaseret godkendelse.

Det giver også en kommentar, der udløser hver implementering:

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

3.1. Grundlæggende HTTP-godkendelse

Som nævnt ovenfor kan en webapplikation konfigurere Basic HTTP-godkendelse bare ved hjælp af @BasicAuthenticationMechanismDefinition annotation on a CDI bean:

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

På dette tidspunkt søger og instantierer Servlet-containeren den leverede implementering af HttpAuthenticationMechanism interface.

Efter modtagelse af en uautoriseret anmodning udfordrer containeren klienten til at give passende godkendelsesoplysninger via WWW-godkendelse svarhoved.

WWW-Authenticate: Basic realm = "userRealm"

Klienten sender derefter brugernavnet og adgangskoden adskilt af et kolon ":" og kodet i Base64 via Bemyndigelse anmodningsoverskrift:

// bruger = baeldung, adgangskode = baeldung Autorisation: Grundlæggende YmFlbGR1bmc6YmFlbGR1bmc = 

Bemærk, at dialogen, der præsenteres for at give legitimationsoplysninger, kommer fra browseren og ikke fra serveren.

3.2. Formbaseret HTTP-godkendelse

Det @FormAuthenticationMechanismDefinition annotering udløser en formbaseret godkendelse som defineret af Servlet-specifikationen.

Derefter har vi muligheden for at specificere login- og fejlsiderne eller bruge standard rimelige sider /Log på og /login fejl:

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

Som et resultat af påberåbelse loginSide, serveren skal sende formularen til klienten:

Klienten skal derefter sende formularen til en foruddefineret backing-godkendelsesproces leveret af containeren.

3.3. Brugerdefineret formbaseret HTTP-godkendelse

En webapplikation kan udløse den brugerdefinerede formbaserede godkendelsesimplementering ved hjælp af kommentaren @CustomFormAuthenticationMechanismDefinition:

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

Men i modsætning til den standardformularbaserede godkendelse konfigurerer vi en brugerdefineret login-side og påkalder SecurityContext.authenticate () metode som en backing-godkendelsesproces.

Lad os se på bagsiden LoginBean som også indeholder login-logikken:

@Named @RequestScoped offentlig klasse LoginBean {@Inject private SecurityContext securityContext; @NotNull privat streng brugernavn; @NotNull privat strengadgangskode; offentligt ugyldigt login () {Credential credential = new UsernamePasswordCredential (username, new Password (password)); AuthenticationStatus status = securityContext .authenticate (getHttpRequestFromFacesContext (), getHttpResponseFromFacesContext (), withParams (). Credential (credential)); // ...} // ...}

Som et resultat af påberåbelse af skikken login.xhtml side, sender klienten den modtagne formular til LoginBønne 's Log på() metode:

//... 

3.4. Brugerdefineret godkendelsesmekanisme

Det HttpAuthenticationMechanism interface definerer tre metoder. Det vigtigste er validateRequest () som vi skal give en implementering.

Standardadfærden for de to andre metoder, secureResponse () og cleanSubject (), er tilstrækkelig i de fleste tilfælde.

Lad os se på et eksempel på implementering:

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

Her giver implementeringen valideringsprocessens forretningslogik, men i praksis anbefales det at delegere til IdentityStore gennem IdentityStoreHandler by påberåber sig valider.

Vi har også kommenteret implementeringen med @ApplicationScoped kommentar, da vi har brug for at gøre det CDI-aktiveret.

Efter en gyldig verifikation af legitimationsoplysningerne og en eventuel hentning af brugerroller, implementeringen skal underrette containeren derefter:

HttpMessageContext.notifyContainerAboutLogin (Principal principal, Set groups)

3.5. Håndhæve Servlet-sikkerhed

En webapplikation kan håndhæve sikkerhedsbegrænsninger ved hjælp af @ServletSikkerhed kommentar til en Servlet-implementering:

@WebServlet ("/ sikret") @ServletSecurity (værdi = @HttpConstraint (rollerAllowed = {"admin_role"}), httpMethodConstraints = {@HttpMethodConstraint (værdi = "GET" ,rollerAllowed = {"user_role"}), @HttpMethodCon = "POST" ,rollerAllowed = {"admin_role"})}) offentlig klasse SecuredServlet udvider HttpServlet {}

Denne kommentar har to attributter - httpMethodConstraints og værdi; httpMethodConstraints bruges til at specificere en eller flere begrænsninger, hvor hver repræsenterer en adgangskontrol til en HTTP-metode ved en liste over tilladte roller.

Containeren kontrollerer derefter for hver url-mønster og HTTP-metode, hvis den tilsluttede bruger har den passende rolle til at få adgang til ressourcen.

4. Identitetsbutik

Denne funktion er abstraheret af det IdentityStore interface, og det bruges til at validere legitimationsoplysninger og til sidst hente gruppemedlemskab. Med andre ord kan det give muligheder for godkendelse, godkendelse eller begge dele.

IdentityStore er beregnet og opmuntret til at blive brugt af HttpAuthenticationMecanism gennem en kaldet IdentityStoreHandler interface. En standardimplementering af IdentityStoreHandler leveres af Servletbeholder.

En applikation kan give sin implementering af programmet IdentityStore eller bruger en af ​​de to indbyggede implementeringer leveret af containeren til database og LDAP.

4.1. Indbyggede identitetsbutikker

Den Jakarta EE-kompatible server skal levere implementeringer til de to identitetsbutikker: Database og LDAP.

Databasen IdentityStore implementering initialiseres ved at sende en konfigurationsdata til @DataBaseIdentityStoreDefinition kommentar:

@DatabaseIdentityStoreDefinition (dataSourceLookup = "java: comp / env / jdbc / securityDS", callerQuery = "vælg adgangskode fra brugere hvor brugernavn =?", GroupsQuery = "vælg GROUPNAME fra grupper hvor brugernavn =?", Prioritet = 30) @ApplicationScoped offentlig klasse AppConfig {}

Som konfigurationsdata vi har brug for en JNDI-datakilde til en ekstern database, er to JDBC-udsagn til kontrol af den, der ringer op og hans grupper, og endelig en prioritetsparameter, der bruges i tilfælde af multipelbutik, konfigureret.

IdentityStore med høj prioritet behandles senere af IdentityStoreHandler.

Ligesom databasen, LDAP IdentityStore-implementering initialiseres gennem @LdapIdentityStoreDefinition ved at videregive konfigurationsdata:

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

Her har vi brug for URL'en til en ekstern LDAP-server, hvordan man søger på den, der ringer op i LDAP-biblioteket, og hvordan man henter sine grupper.

4.2. Implementering af en brugerdefineret IdentityStore

Det IdentityStore interface definerer fire standardmetoder:

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

Det prioritet() metoden returnerer en værdi for rækkefølgen af ​​iteration, som denne implementering behandles af IdentityStoreHandler. En IdentityStore med lavere prioritet behandles først.

Som standard er en IdentityStore behandler begge legitimationsoplysninger validering (ValidationType.VALIDATE) og gruppehentning (ValidationType.PROVIDE_GROUPS). Vi kan tilsidesætte denne adfærd, så den kun kan give en mulighed.

Således kan vi konfigurere IdentityStore skal kun bruges til validering af legitimationsoplysninger:

@ Override public Sæt validationTypes () {return EnumSet.of (ValidationType.VALIDATE); }

I dette tilfælde skal vi give en implementering af valider () metode:

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

Eller vi kan vælge at konfigurere IdentityStore så det kun kan bruges til gruppehentning:

@ Override public Sæt validationTypes () {return EnumSet.of (ValidationType.PROVIDE_GROUPS); }

Vi skal derefter give en implementering af getCallerGroups () metoder:

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

Fordi IdentityStoreHandler forventer, at implementeringen er en CDI-bønne, vi dekorerer den med ApplicationScoped kommentar.

5. Security Context API

Jakarta EE 8 Security API giver et adgangspunkt til programmatisk sikkerhed gennem Sikkerhedskontekst interface. Det er et alternativ, når den deklarative sikkerhedsmodel, der håndhæves af containeren, ikke er tilstrækkelig.

En standardimplementering af Sikkerhedskontekst interface skal leveres ved kørsel som en CDI-bønne, og derfor er vi nødt til at indsprøjte den:

@Inject SecurityContext securityContext;

På dette tidspunkt kan vi godkende brugeren, hente en godkendt, kontrollere hans rolle medlemskab og give eller nægte adgang til webressourcen gennem de fem tilgængelige metoder.

5.1. Henter opkaldsdata

I tidligere versioner af Jakarta EE ville vi hente Rektor eller tjek rollemedlemskabet forskelligt i hver container.

Mens vi bruger getUserPrincipal () og isUserInRole () metoder til HttpServletRequest i en servletbeholder, lignende metoder getCallerPrincipal () og isCallerInRole () -metoder af EJBContext bruges i EJB Container.

Den nye Jakarta EE 8 Security API har standardiseret dette ved giver en lignende metode gennem Sikkerhedskontekst grænseflade:

Principal getCallerPrincipal (); boolsk isCallerInRole (strengrolle); Indstil getPrincipalsByType (klassetype);

Det getCallerPrincipal () metoden returnerer en containerspecifik repræsentation af den godkendte opkald, mens getPrincipalsByType () metode henter alle hovedpersoner af en given type.

Det kan være nyttigt, hvis den applikationsspecifikke opkald er forskellig fra beholderen.

5.2. Test for adgang til webressourcer

Først skal vi konfigurere en beskyttet ressource:

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

Og så skal vi påberåbe os for at kontrollere adgangen til denne beskyttede ressource hasAccessToWebResource () metode:

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

I dette tilfælde returnerer metoden sand, hvis brugeren er i rolle USER_ROLE.

5.3. Godkendelse af opkalderen programmatisk

En applikation kan programmatisk udløse godkendelsesprocessen ved at påberåbe sig godkende ():

AuthenticationStatus-godkendelse (HttpServletRequest anmodning, HttpServletResponse svar, AuthenticationParameters parametre);

Containeren får derefter besked og vil igen påkalde den godkendelsesmekanisme, der er konfigureret til applikationen. AuthenticationParameters parameter giver legitimationsoplysninger til HttpGodkendelseMekanisme:

withParams (). legitimationsoplysninger (legitimationsoplysninger)

Det SUCCES og SEND_FAILURE værdier af AuthenticationStatus designe en vellykket og mislykket godkendelse, mens SEND_CONTINUE signalerer en igangværende status for godkendelsesprocessen.

6. Kørsel af eksemplerne

For at fremhæve disse eksempler har vi brugt den nyeste udviklingsversion af Open Liberty Server, der understøtter Jakarta EE 8. Dette downloades og installeres takket være liberty-maven-plugin, som også kan implementere applikationen og starte serveren.

For at køre eksemplerne skal du bare få adgang til det tilsvarende modul og påkalde denne kommando:

mvn ren pakke frihed: køre

Som et resultat vil Maven downloade serveren, opbygge, implementere og køre applikationen.

7. Konklusion

I denne artikel dækkede vi konfigurationen og implementeringen af ​​hovedfunktionerne i den nye Jakarta EE 8 Security API.

Først startede vi med at vise, hvordan man konfigurerer standardindbyggede godkendelsesmekanismer, og hvordan man implementerer en brugerdefineret. Senere så vi, hvordan man konfigurerer den indbyggede Identity Store, og hvordan man implementerer en brugerdefineret. Og endelig så vi, hvordan man kalder metoder til Sikkerhedskontekst.

Som altid er kodeeksemplerne til denne artikel tilgængelige på GitHub.


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