Custom Evaluators
Custom evaluators laajentavat webforJ:n turvallisuusjärjestelmää erikoistuneella pääsynhallintalogiikalla, joka menee perusasiakirjojen ja roolien tarkistusten yli. Käytä niitä, kun sinun tarvitsee tarkistaa dynaamisia ehtoja, jotka riippuvat pyyntöyhteydestä, ei vain käyttäjäoikeuksista.
Tämä opas kattaa mukautetut arvioijat Spring Securitylle. Jos et käytä Spring Bootia, katso Evaluator Chain guide ymmärtääksesi, miten arvioijat toimivat ja Complete Implementation toimivan esimerkin saamiseksi.
Mitä ovat mukautetut arvioijat
Arvioija määrittää, voiko käyttäjä käyttää tiettyä reittiä mukautetun logiikan perusteella. Arvioijat tarkistetaan navigoinnin aikana ennen kuin mitään komponenttia renderöidään, mikä mahdollistaa pääsyn keskeyttämisen ja hallinnan dynaamisesti.
webforJ sisältää valmiit arvioijat standardeille Jakarta-anotaatioille:
AnonymousAccessEvaluator- Käsittelee@AnonymousAccessPermitAllEvaluator- Käsittelee@PermitAllRolesAllowedEvaluator- Käsittelee@RolesAllowedDenyAllEvaluator- Käsittelee@DenyAll
Mukautetut arvioijat seuraavat samaa kaavaa, mikä mahdollistaa omien anotaatioiden ja pääsynhallintalogiikan luomisen.
Tiedot @AnonymousAccess, @PermitAll, @RolesAllowed ja @DenyAll -anotaatiosta löytyy Security Annotations guide.
Käyttötapa: Omistajuuden vahvistaminen
Yksi yleinen vaatimus on sallia käyttäjille pääsy vain omiin resursseihinsa. Esimerkiksi käyttäjät voivat vain muokata omaa profiiliaan, eivätkä jonkun toisen profiilia.
Ongelma: @RolesAllowed("USER") myöntää pääsyn kaikille todennetuille käyttäjille, mutta ei tarkista, pääseekö käyttäjä omaan resurssiinsa. Sinun on vertailtava kirjautuneen käyttäjän ID:t resurssi-ID:hen URL-osoitteessa.
Esimerkkitapaus:
- Käyttäjä ID
123on kirjautunut - He siirtyvät osoitteeseen
/users/456/edit - Saavatko he käyttää tätä sivua? EI - he voivat muokata vain
/users/123/edit
Et voi ratkaista tätä roolien avulla, koska se riippuu reitti-parametrista :userId, joka muuttuu jokaiselle pyynnölle.
Mukautetun anotaatio luominen
Määrittele anotaatio merkitsemään reittejä, jotka vaativat omistajuuden vahvistamista:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequireOwnership {
/**
* Reitti-parametrin nimi, joka sisältää käyttäjän ID:n.
*/
String value() default "userId";
}
Käytä sitä reiteillä, jotka vaativat omistajuuden tarkistuksia:
@Route(value = "/users/:userId/edit", outlet = MainLayout.class)
@RequireOwnership("userId")
public class EditProfileView extends Composite<Div> {
private final Div self = getBoundComponent();
public EditProfileView() {
self.setText("Muokkaa profiilisivua");
}
}
Arvioijan toteuttaminen
Luo Springin hallinnoima arvioija, joka vertailee kirjautuneen käyttäjän ID:tä reitti-parametriin:
@RegisteredEvaluator(priority = 10)
public class OwnershipEvaluator implements RouteSecurityEvaluator {
@Override
public boolean supports(Class<?> routeClass) {
return routeClass.isAnnotationPresent(RequireOwnership.class);
}
@Override
public RouteAccessDecision evaluate(Class<?> routeClass, NavigationContext context,
RouteSecurityContext securityContext, SecurityEvaluatorChain chain) {
// Tarkista ensin todennus
if (!securityContext.isAuthenticated()) {
return RouteAccessDecision.denyAuthentication();
}
// Hae anotaatio
RequireOwnership annotation = routeClass.getAnnotation(RequireOwnership.class);
String paramName = annotation.value();
// Hae kirjautuneen käyttäjän ID turvallisuuskontekstista
String currentUserId = securityContext.getPrincipal()
.filter(p -> p instanceof UserDetails)
.map(p -> ((UserDetails) p).getUsername())
.orElse(null);
// Hae :userId reitti-parametreista
String requestedUserId = context.getRouteParameters()
.get(paramName)
.orElse(null);
// Tarkista, vastaavatko ne
if (currentUserId != null && currentUserId.equals(requestedUserId)) {
// Omistus vahvistettu - jatka ketjua sallimaan muiden arvioijien suorittaminen
return chain.evaluate(routeClass, context, securityContext);
}
return RouteAccessDecision.deny("Voit käyttää vain omia resurssejasi");
}
}
Spring löytää ja rekisteröi automaattisesti arvioijat, jotka on merkitty @RegisteredEvaluator.
Miten se toimii
Arvioijan toteutuksessa on kaksi avainmenetelmää:
supports(Class<?> routeClass)
- Palauttaa
true, jos tämä arvioija pitäisi käsitellä reittiä - Vain arvioijat, jotka palauttavat
true, kutsutaan reitille - Suodattaa reittejä tarkistamalla
@RequireOwnership-anotaation
evaluate(...)
- Tarkistaa, onko käyttäjä ensin todennettu
- Hakee kirjautuneen käyttäjän ID turvallisuuskontekstista käyttäen
securityContext.getPrincipal() - Hakee reitti-parametrin arvon käyttämällä
context.getRouteParameters().get(paramName) - Vertailee kahta ID:tä
- Jos ne vastaavat, delegoi
chain.evaluate()-menetelmälle, jotta muut arvioijat voivat suorittaa - Jos ne eivät vastaa, palauttaa
deny()syyn kanssa
Virtauksen esimerkki
Kun omistajuuden tarkistus epäonnistuu:
- Käyttäjä
123kirjautuu ja siirtyy/users/456/edit OwnershipEvaluator.supports()palauttaatrue(reitti sisältää@RequireOwnership)OwnershipEvaluator.evaluate()suoritetaan:currentUserId = "123"(turvallisuuskontekstista)requestedUserId = "456"(reitistä saadusta parametrista:userId)"123".equals("456")→false- Palauttaa
RouteAccessDecision.deny("Voit käyttää vain omia resurssejasi")
- Käyttäjä ohjataan pääsy kielletty -sivulle
Kun omistajuuden tarkistus onnistuu:
- Käyttäjä
123kirjautuu ja siirtyy/users/123/edit OwnershipEvaluator.evaluate()suoritetaan:currentUserId = "123",requestedUserId = "123"- ID:t vastaavat → kutsuu
chain.evaluate()jatkaakseen
- Jos ei ole muita arvioijia, jotka estävät pääsyn, käyttäjälle myönnetään pääsy
Arvioijaketjun ymmärtäminen
Turvallisuusjärjestelmä käyttää vastuu- ja ketju-käytäntöä, jossa arvioijat käsitellään prioriteettijärjestyksessä. Arvioijat voivat joko tehdä lopullisia päätöksiä tai delegoida ketjulle moninkertaisten tarkistusten yhdistämiseksi.
Miten ketju toimii
- Arvioijat lajitellaan prioriteettinsa mukaan (alhaiset numerot ensin)
- Jokaiselle arvioijalle kutsutaan
supports(routeClass)tarkistamaan, soveltuuko se - Jos
supports()palauttaatrue, arvioijanevaluate()-menetelmää kutsutaan - Arvioija voi joko:
- Palauttaa lopullisen päätöksen (
grant()taideny()) - keskeyttää ketjun - Delegoida ketjulle kutsumalla
chain.evaluate()- sallii muiden arvioijien suorittaa
- Palauttaa lopullisen päätöksen (
- Jos ketju päättyy ilman päätöstä ja oletus turvallisuus on käytössä, todennetut käyttäjät eivät saa pääsyä
Lopulliset päätökset
Keskeytä ketju heti:
RouteAccessDecision.grant()
- Myöntää pääsyn ja lopettaa edelleen arvioinnin
- Käytetään
@AnonymousAccessja@PermitAll-anotaatioilla - nämä ovat täydellisiä valtuutuksia, jotka eivät yhdisty muihin tarkistuksiin
RouteAccessDecision.deny(reason)
- Kieltää pääsyn ja lopettaa edelleen arvioinnin
- Käytetään
@DenyAllja kun mukautetut tarkistukset epäonnistuvat - Esimerkki:
RouteAccessDecision.deny("Voit käyttää vain omia resurssejasi")
RouteAccessDecision.denyAuthentication()
- Ohjaa kirjautumissivulle
- Käytetään, kun todennus on vaadittu mutta puuttuu
Ketjun delegointi
Mahdollistaa tarkistusten yhdistämisen:
chain.evaluate(routeClass, context, securityContext)
- Siirtää ohjausta seuraavalle arvioijalle ketjussa
- Mahdollistaa useiden valtuutustarkistusten yhdistämisen
- Käytetään
@RolesAllowedja@RouteAccessjälkeen, kun niiden tarkistukset onnistuvat - Mukautettujen arvioijien tulisi käyttää tätä kaavaa, kun tarkistukset onnistuvat, jotta sallitaan koostumus
Arvioijan prioriteetti
Arvioijat tarkistetaan prioriteettijärjestyksessä (alhaiset numerot ensin). Kehyksen arvioijat käyttävät prioriteettia 1-9, mukautettujen arvioijien tulisi käyttää 10 tai enemmän.
Valmiit arvioijat rekisteröidään tässä järjestyksessä:
// Prioriteetti 1: @DenyAll - estää kaiken
// Prioriteetti 2: @AnonymousAccess - sallii todennuksen
// Prioriteetti 3: AuthenticationRequiredEvaluator - varmistaa todennuksen @PermitAll/@RolesAllowed
// Prioriteetti 4: @PermitAll - vaatii vain todennuksen
// Prioriteetti 5: @RolesAllowed - vaatii erityisiä rooleja
// Prioriteetti 6: @RouteAccess - SpEL lausekkeet (vain Spring Security)
// Prioriteetti 10+: Mukautetut arvioijat (kuten @RequireOwnership)
Miten prioriteetti vaikuttaa arviointiin
- Alhaisemman prioriteetin arvioijat toimivat ensin ja voivat "lyödä ketjun"
@DenyAll(prioriteetti 1) toimii ensin - jos tämä on läsnä, pääsy kielletty@AnonymousAccess(prioriteetti 2) toimii seuraavaksi - jos tämä on läsnä, pääsy sallitaan aina (ilman todennusta)AuthenticationRequiredEvaluator(prioriteetti 3) tarkistaa, tarvitseeko reitti todennusta ja onko käyttäjä todennettu- Jos mikään arvioija ei käsittele reittiä, oletus turvallisuuslogiikka pätee
Prioriteetin asettaminen
Aseta prioriteetti @RegisteredEvaluator -anotaatiolla:
@RegisteredEvaluator(priority = 10) // Toimii valmiiden arvioijien jälkeen
public class OwnershipEvaluator implements RouteSecurityEvaluator {
// ...
}
Mukautettujen arvioijien tulisi käyttää prioriteettia 10 tai suurempi. Prioriteetti 1-9 on varattu kehysarvioijille. Jos käytät varattua aluetta, saat varoituksen lokissa.
Arvioijien yhdistäminen
Arvioijat, jotka delegoivat ketjulle, voidaan yhdistää monimutkaisten valtuutustarkistusten luomiseksi. Reiteillä voi olla useita tietoturvaan liittyviä anotaation:
Yhdistetään roolitarkistukset mukautetun logiikan kanssa
@Route("/users/:userId/settings")
@RolesAllowed("USER")
@RequireOwnership("userId")
public class UserSettingsView extends Composite<Div> {
// Täytyy olla USER-rooli JA käyttää itseään asetuksia
}
Miten se toimii:
RolesAllowedEvaluator(prioriteetti 5) tarkistaa, onko käyttäjällä "USER" -rooli- Jos yes, kutsuu
chain.evaluate()jatkaakseen OwnershipEvaluator(prioriteetti 10) tarkistaa, vastaakouserIdkirjautunutta käyttäjää- Jos yes, kutsuu
chain.evaluate()jatkaakseen - Ketju päättyy → pääsy myönnetään
Yhdistetään SpEL-lausekkeet mukautetun logiikan kanssa
@Route("/admin/users/:userId/edit")
@RouteAccess("hasRole('ADMIN')")
@RequireOwnership("userId")
public class AdminEditUserView extends Composite<Div> {
// Täytyy olla admin JA käyttää omaa tiliä
}
Mitä ei voi yhdistää
@AnonymousAccess ja @PermitAll tekevät lopullisia päätöksiä - ne myöntävät pääsyn välittömästi ilman, että ketjua kutsutaan. Et voi yhdistää niitä mukautettujen arvioijien kanssa:
// @PermitAll myöntää pääsyn välittömästi, @RequireOwnership ei koskaan toimi
@Route("/users/:userId/profile")
@PermitAll
@RequireOwnership("userId")
public class ProfileView extends Composite<Div> {
// ...
}
Resursseille, joihin kaikki todennetut käyttäjät voivat käyttää, käytä @RolesAllowed yhteisen roolin kanssa:
// @RolesAllowed delegoi ketjulle
@Route("/users/:userId/profile")
@RolesAllowed("USER")
@RequireOwnership("userId")
public class ProfileView extends Composite<Div> {
// Täytyy olla todennettu käyttäjä JA käyttää omaa profiiliaan
}