Java >> Java tutorial >  >> Tag >> new

Hvad er nyt i Jakarta Security 3?

På trods af versionsnummer 3 er Jakarta Security 3 den første rigtige opdatering af Jakarta Security, siden den blev introduceret som Java EE Security i Java EE 8.

I denne artikel tager vi et kig på, hvilke nye ting der er blevet tilføjet. Vi vil først tage et kig på den brugervendte paraply API, som er selve Jakarta Security, og derefter tage et kig på de to underliggende SPI'er, det afhænger af; Jakarta-godkendelse og Jakarta-autorisation.

OpenID Connect

Signaturtilførslen til Jakarta Security 3 er den nye OpenID Connect-godkendelsesmekanisme, bidraget af Payaras Lead Developer Rudy De Busscher og Principal Engineer Gaurav Gupta.

OpenID Connect forbinder de eksisterende grundlæggende, formular og brugerdefinerede formular-godkendelsesmekanismer. Planen om også at opnå paritet med Servlet ved at tilføje Jakarta Security-versioner af Client-Cert og Digest-godkendelsesmekanismerne mislykkedes desværre, da der simpelthen ikke var nogen, der tog arbejdet for det. Da Jakarta Security nu for det meste er et frivilligt drevet OSS-projekt, er det selvfølgelig sådan, tingene går; vi kan planlægge, hvad vi vil, men er i sidste ende afhængige af, at frivillige henter ting.

OpenID Connect i sig selv er en mekanisme, hvor en tredjepartsserver tager sig af selve autentificeringen af ​​en slutbruger, og resultatet af denne autentificering kommunikeres tilbage til vores server. "Vores server" her er den, der kører Jakarta Security for at sikre vores webapplikation. Da vi er afhængige af en tredjepartsserver, kaldes den eksterne OpenID Connect-server en "relying part" i OpenID Connect-terminologien.

Dette er afbildet i følgende let justerede diagram fra OpenID Connect-webstedet:

    +--------+                                                       +--------+
    |        |                                                       |        |
    |        |---------------(1) Authentication Request------------->|        |
    |        |                                                       |        |
    |        |       +--------+                                      |        |
    |        |       |  End-  |<--(2) Authenticates the End-User---->|        |
    |   RP   |       |  User  |                                      |   OP   |
    |        |       +--------+                                      |        |
    |        |                                                       |        |
    |        |<---------(3) Returns Authorization code---------------|        |
    |        |                                                       |        |
    |        |---------(3b)                                          |        |
    |        |           | Redirect to original resource (if any)    |        |
    |        |<----------+                                           |        |
    |        |                                                       |        |
    |        |------------------------------------------------------>|        |
    |        |   (4) Request to TokenEndpoint for Access / Id Token  |        |
    | OpenID |<------------------------------------------------------| OpenID |
    | Connect|                                                       | Connect|
    | Client | ----------------------------------------------------->|Provider|
    |        |   (5) Fetch JWKS to validate ID Token                 |        |
    |        |<------------------------------------------------------|        |
    |        |                                                       |        |
    |        |------------------------------------------------------>|        |
    |        |   (6) Request to UserInfoEndpoint for End-User Claims |        |
    |        |<------------------------------------------------------|        |
    |        |                                                       |        |
    +--------+                                                       +--------+  
Se openid.net/specs/openid-connect-core-1_0

RP, eller Relying Party, er Jakarta EE-serveren, der kører vores webapplikation. OP, eller OpenID Connect Provider, er den eksterne server, der udfører godkendelse. Dette kan være en anden server, som vi selv kører, eller mere almindeligt, det er en offentlig tjeneste såsom Google, Facebook, Twitter osv.

Vi kan bruge denne godkendelsesmekanisme i vores egne applikationer via den nye "@OpenIdAuthenticationMechanismDefinition" annotation. Det følgende giver et eksempel:

@OpenIdAuthenticationMechanismDefinition(
  
    providerURI =  "http://localhost:8081/openid-connect-server-webapp",

    clientId =     "client",

    clientSecret = "secret",
   
    redirectURI =  "${baseURL}/Callback",

    redirectToOriginalResource = true
)

Der er mange flere attributter, der kan konfigureres, men ovenstående er en typisk minimal konfiguration. Lad os hurtigt tage et kig på, hvad de forskellige egenskaber gør.

"ProviderURI" angiver placeringen af ​​OpenID-udbyderen. Det er her, slutbrugeren bliver omdirigeret til, når han logger på vores applikation. ClientId og clientSecret er i det væsentlige brugernavnet/adgangskoden til at identificere os selv over for OpenID-udbyderen. Bemærk, at når brugeren omdirigeres, er det kun clientId, der lægges ind i omdirigerings-URL'en. ClientSecret bruges, når vi laver vores sikre server til server kommunikation, som ikke involverer slutbrugeren. Bemærk også, at i Jakarta Security kan annoteringsattributter indeholde Jakarta Expression Language, og i praksis ville clientId og clientSecret ikke blive sat i konstanter i koden på denne måde.

RedirectURI er det sted, slutbrugeren omdirigeres tilbage til efter at være blevet godkendt. En speciel pladsholder ${baseURL} bruges her, som løses til den faktiske URL, som vores applikation er implementeret til.

Endelig har vi redirectToOriginalResource, som sørger for, at slutbrugeren omdirigeres tilbage til den oprindelige ressource (side, sti) for den situation, hvor godkendelse automatisk blev udløst, når en beskyttet ressource blev tilgået. Dette fungerer på samme måde, som den velkendte FORM-godkendelsesmekanisme fungerer. Når den er indstillet til false, vil slutbrugeren forblive ved ressourcen bag redirectURI, som naturligvis skal eksistere dengang. Hvis den er indstillet til sand, overvåger godkendelsesmekanismen den, og der behøver ikke at være en egentlig Servlet eller Filter knyttet til den.

Det følgende viser denne annotation i kontekst på en Servlet:

@OpenIdAuthenticationMechanismDefinition(
    providerURI =  "http://localhost:8081/openid-connect-server-webapp",
    clientId =     "client",
    clientSecret = "secret",
    redirectURI =  "${baseURL}/Callback",
    redirectToOriginalResource = true
)
@WebServlet("/protectedServlet")
@DeclareRoles({ "foo", "bar", "kaz" })
@ServletSecurity(@HttpConstraint(rolesAllowed = "foo"))
public class ProtectedServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @Override
    public void doGet(
        HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {
        response.getWriter().write("This is a protected servlet \n");
    }

}
I Jakarta Security kombinerer vi typisk en godkendelsesmekanisme med et identitetslager, som er den enhed, der validerer de legitimationsoplysninger, som slutbrugeren leverer. For OpenID Connect valideres denne slutbrugerlegitimation naturligvis af den eksterne OpenID Connect-udbyder. Vi er nødt til at validere et token, der kommer tilbage fra udbyderen, men det gøres internt af OpenID Connect-godkendelsesmekanismen (den bruger et identitetslager til dette, men et internt).

En offentlig OpenID-udbyder har dog typisk ikke kendskab til grupper, som en slutbruger har i vores applikation, så vi er nødt til at levere et identitetslager til netop det formål. Dette er grundlæggende det samme, som vi ofte skal gøre for klientcertificering, da certifikaterne heller ikke indeholder nogen grupper. Det følgende giver et eksempel på en sådan butik:

@ApplicationScoped
public class AuthorizationIdentityStore implements IdentityStore {

    private Map<String, Set<String>> authorization = 
        Map.of("user", Set.of("foo", "bar"));

    @Override
    public Set<ValidationType> validationTypes() {
        return EnumSet.of(PROVIDE_GROUPS);
    }

    @Override
    public Set<String> getCallerGroups(CredentialValidationResult result) {
        return authorization.get(result.getCallerPrincipal().getName());
    }

}
I eksempelbutikken ovenfor kortlægger vi en slutbruger kaldet "bruger" til grupperne "foo" og "bar". Denne identitetsbutik vil blive kaldt sammen med den interne OpenID Connect identitetsbutik, og vores opgave her er netop at stille grupperne til rådighed.

Disse to klasser kan sammen pakkes sammen og udgøre en komplet applikation, som vi kan bruge til at teste. Det er tilgængeligt som et Maven-projekt her:app-openid3

Små API-forbedringer

Ved siden af ​​billetfunktionen OpenID Connect er der tilføjet en række små API-forbedringer:

OpkaldsPrincipal kan serialiseres

Den oprindelige Principal-type, som Jakarta Security bruger til at betegne den opkaldende principal, kunne ikke serialiseres i de første versioner. Dette forårsagede forskellige problemer, når denne principal blev gemt i en HTTP-session, og der blev brugt en form for fail-over eller clustering. Det kan nu serialiseres:

/**
 * Principal that represents the caller principal associated with the invocation 
 * being processed by the container (e.g. the current HTTP request).
 */
public class CallerPrincipal implements Principal, Serializable {

Dynamisk tilføjelse af interceptor til en indbygget CDI-bønne

Jakarta Security leverer en række interceptorer, der tilføjer funktionalitet til en bønne, for det meste bønner, der er autentificeringsmekanismer. De er nemme at tilføje til ens egne bønner, men det kræver noget mere arbejde at anvende på en af ​​de godkendelsesmekanismer, der er indbygget i Jakarta Security.

To af de artefakter, der skulle oprettes, for at dette kunne virke, var en indpakning til typen HttpAuthenticationMechanism og en annotation for interceptoren, som vi ønskede at tilføje dynamisk.

Denne opgave er blevet gjort en smule lettere i Jakarta Security 3, hvor alle Interceptorer nu har standard annotations-literaler, og HttpAuthenticationMechanismWrapper-typen leveres af API'et nu.

For eksempel:

@Inherited
@InterceptorBinding
@Retention(RUNTIME)
@Target(TYPE)
public @interface AutoApplySession {

    /**
     * Supports inline instantiation of the AutoApplySession annotation.
     *
     * @since 3.0
     */
    public static final class Literal extends AnnotationLiteral<AutoApplySession>
        implements AutoApplySession {
        private static final long serialVersionUID = 1L;

        /**
         * Instance of the {@link AutoApplySession} Interceptor Binding.
         */
        public static final Literal INSTANCE = new Literal();
    }
}

Jakarta-godkendelse

Jakarta Authentication er den underliggende SPI, som Jakarta Security afhænger af. Forbedringer her gavner for det meste biblioteksleverandører, selvom nogle avancerede brugere også kan vælge at bruge det direkte.

Registrer ServerAuthModule

Slutbrugeren af ​​Jakarta Authentication såvel som integratorer som Jakarta Security-implementeringer er næsten altid ligeglade med at registrere et ServerAuthModule. Alligevel accepterer AuthConfigFactory kun en AuthConfigProvider, som i det væsentlige er en "wrapper-wrapper-wrapper-wrapper" af et ServerAuthModule til slutbrugeren. En ny metode er blevet tilføjet til AuthConfigFactory til kun at registrere et ServerAuthModule.

Et ServerAuthModule er typisk installeret i en servlet-lytter. Følgende er et eksempel:

@WebListener
public class SamAutoRegistrationListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        AuthConfigFactory
            .getFactory()
            .registerServerAuthModule(
                new TestServerAuthModule(),
                sce.getServletContext());
    }

}

Tilføj manglende generiske artikler til API

Jakarta Authentication har mærkeligt nok været på Java SE 1.4 selv i Jakarta EE 9.1, som officielt er rettet mod Java SE 8 og 11. Dette betød specifikt, at der manglede en masse generiske stoffer overalt i API'en. Disse er nu tilføjet. For eksempel:

public interface ServerAuthModule extends ServerAuth {

    void initialize(
        MessagePolicy requestPolicy, MessagePolicy responsePolicy, 
        CallbackHandler handler, Map<String, Object> options) 
        throws AuthException;

    Class<?>[] getSupportedMessageTypes();
}

Tilføj standardmetoder

Et ServerAuthModule kræver metoder for at "secureResponse" og "cleanSubject" kan implementeres, men det er langt fra alle ServerAuthModules, der behøver at gøre noget der. For disse metoder er standarder blevet tilføjet, så implementeringer, der ikke har brug for dem, kan være lidt mindre omfattende. Grænsefladen ser nu ud som følger:
public interface ServerAuth {

    AuthStatus validateRequest(
        MessageInfo messageInfo, Subject clientSubject, 
        Subject serviceSubject) throws AuthException;

    default AuthStatus secureResponse(
        MessageInfo messageInfo, Subject serviceSubject) 
        throws AuthException {
        return AuthStatus.SEND_SUCCESS;
    }

    default void cleanSubject(
        MessageInfo messageInfo, Subject subject) 
        throws AuthException {
    }
}

Tilføj konstruktør, der tager årsag til AuthException

Jakarta-godkendelse var på Java SE 1.4-niveau betød, at dens AuthException ikke gjorde brug af at indstille undtagelsesårsagen, der blev tilføjet i Java SE 5.

At smide en undtagelse fra Jakarta Authentication Code var derfor mere end en lille detaljeret:

throw (AuthException) new AuthException().initCause(e);
Nye konstruktører er blevet tilføjet, som nu tager en årsag, så vi nu kan gøre:
throw new AuthException(e);

Skelne mellem påkaldelse ved start af anmodning og påkaldelse efter authenticate()

I Servlet Container Profile for Jakarta Authentication kan et ServerAuthModule kaldes af containeren ved starten af ​​en anmodning (før filtre og Servlets påkaldes) eller efter et kald til HttpServletRequest.authenticate(). For et ServerAuthModule er der ingen måde at skelne mellem disse to tilfælde, hvilket nogle gange er nødvendigt for mere avancerede interaktioner.

Et ServerAuthModule kan nu kontrollere dette ved at se på `jakarta.servlet.http.isAuthenticationRequest`-nøglen i meddelelsesinfokortet.

Jakarta-godkendelse

Jakarta-autorisation er en anden underliggende SPI, som Jakarta Security afhænger af. Forbedringer her også for det meste gavn for biblioteksleverandører, selvom nogle avancerede brugere også kan vælge at bruge det direkte.

Tilføj getPolicyConfiguration-metoder uden tilstandskrav

PolicyConfigurationFactory i Jakarta Authorization har metoder til at hente en politikkonfigurationsinstans, som har en samling tilladelser, som bruges til autorisationsbeslutninger. En Policy (autorisationsmodul) kan dog ikke uden videre bruge disse, da alle de eksisterende metoder har krævede bivirkninger. I praksis er en sådan politik derfor nødt til at ty til implementeringsspecifikke måder, der ofte i høj grad kobler PolicyConfigurationFactory og Policy. For de nye udgivelsesmetoder er blevet tilføjet for at få den PolicyConfiguration direkte uden nogen bivirkninger;
     public abstract PolicyConfiguration getPolicyConfiguration(String contextID);
     public abstract PolicyConfiguration getPolicyConfiguration();
Den første variant kan bruges, når politikken allerede har contextID'et (en identifikator for applikationen), mens den anden variant er en praktisk metode, der returnerer PolicyConfiguration for det contextID, der er indstillet på den kaldende tråd.

Tilføj metoder til PolicyConfiguation for at læse tilladelser

PolicyConfiguration som nævnt ovenfor gemmer tilladelserne, men det indeholdt mærkeligt nok ikke metoder før til at læse disse tilladelser tilbage. En politik er altid nødvendig for at ty til implementeringsspecifikke metoder for at opnå disse tilladelser. For eksempel, i gamle versioner af GlassFish ville PolicyConfiguration skrive sine tilladelser til en politikfil på disken først, og derefter ville Politiken læse filen tilbage. Nu er der endelig tilføjet nogle metoder til direkte at læse tilladelserne tilbage:
     Map<String, PermissionCollection> getPerRolePermissions();
     PermissionCollection getUncheckedPermissions();
     PermissionCollection getExcludedPermissions();

Generisk returværdi for getContext

Jakarta-autorisation har et PolicyContext-objekt, hvorfra instanser af forskellige typer kan hentes, vigtigst af alt emnet. Signaturen på denne metode returnerede et objekt før, så der altid var behov for en cast. I den nye version er dette blevet ændret til en generisk returværdi:public static T getContext(String key) throws PolicyContextExceptionSå for eksempel tidligere gjorde man:
     Subject subject = 
         (Subject) PolicyContext.getContext("javax.security.auth.Subject.container");
Hvilket nu kan være:
     Subject subject = PolicyContext.getContext(“javax.security.auth.Subject.container");

Sidste tanker

Mængden af ​​ændringer for Jakarta Security 3 er mindre end planlagt, men den store billetfunktion OpenID Connect er meget velkommen. Det var planlagt til den første udgivelse, og nogle implementeringer var startet, men i sidste ende nåede det ikke ind dengang. Ændringerne i SPI'er på lavere niveau er små, men nogle af dem ret vigtige. Ser vi fremad, bør den næste version af Jakarta Security fokusere mere på autorisationsemnet. Autorisationsmoduler er stadig noget af en obskur ting i den nuværende API, hvilket er en skam, da det er et meget kraftfuldt koncept. Denne opdatering har sat scenen for en mere tilgængelig fremtidig API der.
No
Java tag