Introduksjon til Apache Shiro

1. Oversikt

I denne artikkelen ser vi på Apache Shiro, et allsidig Java-sikkerhetsrammeverk.

Rammeverket er svært tilpassbart og modulært, da det tilbyr autentisering, autorisasjon, kryptografi og øktadministrasjon.

2. Avhengighet

Apache Shiro har mange moduler. I denne opplæringen bruker vi imidlertid shiro-kjerne kun gjenstand.

La oss legge det til vårt pom.xml:

 org.apache.shiro shiro-core 1.4.0 

Den siste versjonen av Apache Shiro-modulene finner du på Maven Central.

3. Konfigurere Security Manager

De Sikkerhetssjef er midtpunktet i Apache Shiros rammeverk. Programmer vil vanligvis ha en enkelt forekomst av at den kjører.

I denne opplæringen utforsker vi rammeverket i et skrivebordsmiljø. For å konfigurere rammeverket, må vi lage en shiro.ini filen i ressursmappen med følgende innhold:

[brukere] bruker = passord, admin bruker2 = passord2, redaktør bruker3 = passord3, forfatter [roller] admin = * redaktør = artikler: * forfatter = artikler: komponere, artikler: lagre

De [brukere] delen av shiro.ini konfigurasjonsfil definerer brukerlegitimasjonen som gjenkjennes av Sikkerhetssjef. Formatet er: srektor (brukernavn) = passord, rolle1, rolle2,…, rolle.

Rollene og tilhørende tillatelser er deklarert i [roller] seksjon. De admin rollen får tillatelse og tilgang til alle deler av applikasjonen. Dette er angitt med jokertegnet (*) symbol.

De redaktør rolle har alle tillatelser knyttet til artikler mens forfatter rolle kan bare komponere og lagre en artikkel.

De Sikkerhetssjef brukes til å konfigurere SecurityUtils klasse. Fra SecurityUtils vi kan få den nåværende brukeren som samhandler med systemet og utføre autentiserings- og autorisasjonsoperasjoner.

La oss bruke IniRealm for å laste inn bruker- og rolledefinisjoner fra shiro.ini filen og deretter bruke den til å konfigurere DefaultSecurityManager gjenstand:

IniRealm iniRealm = ny IniRealm ("klassesti: shiro.ini"); SecurityManager securityManager = ny DefaultSecurityManager (iniRealm); SecurityUtils.setSecurityManager (securityManager); Subject currentUser = SecurityUtils.getSubject ();

Nå som vi har en Sikkerhetssjef som er klar over brukerlegitimasjon og roller definert i shiro.ini fil, la oss gå videre til brukerautentisering og autorisasjon.

4. Autentisering

I Apache Shiros terminologier, a Emne er en hvilken som helst enhet som kommuniserer med systemet. Det kan enten være et menneske, et skript eller en REST-klient.

Ringer SecurityUtils.getSubject () returnerer en forekomst av gjeldende Emne, det er det nåværende bruker.

Nå som vi har nåværende bruker Objekt, vi kan utføre autentisering på de medfølgende legitimasjonene:

if (! currentUser.isAuthenticated ()) {UsernamePasswordToken token = nytt brukernavnPasswordToken ("bruker", "passord"); token.setRememberMe (true); prøv {currentUser.login (token); } fange (UnknownAccountException uae) {log.error ("Brukernavn ikke funnet!", uae); } fange (IncorrectCredentialsException ice) {log.error ("Ugyldig legitimasjon!", ice); } catch (LockedAccountException lae) {log.error ("Kontoen din er låst!", lae); } fange (AuthenticationException ae) {log.error ("Uventet feil!", ae); }}

Først sjekker vi om den nåværende brukeren ikke allerede er godkjent. Deretter lager vi et godkjenningstoken med brukerens rektor (brukernavn) og legitimasjon (passord).

Deretter prøver vi å logge inn med tokenet. Hvis de oppgitte legitimasjonene er korrekte, bør alt gå bra.

Det er forskjellige unntak for forskjellige saker. Det er også mulig å kaste et tilpasset unntak som bedre passer applikasjonskravet. Dette kan gjøres ved å underklassere Konto unntak klasse.

5. Autorisasjon

Autentisering prøver å validere brukerens identitet mens autorisasjon prøver å kontrollere tilgang til visse ressurser i systemet.

Husk at vi tildeler en eller flere roller til hver bruker vi har opprettet i shiro.ini fil. Videre definerer vi i rolleseksjonen forskjellige tillatelser eller tilgangsnivåer for hver rolle.

La oss nå se hvordan vi kan bruke det i applikasjonen vår til å håndheve brukeradgangskontroll.

I shiro.ini fil, gir vi administratoren total tilgang til alle deler av systemet.

Redaktøren har total tilgang til alle ressurser / operasjoner angående artikler, og en forfatter er begrenset til bare å komponere og lagre artikler kun.

La oss ønske den nåværende brukeren velkommen på grunnlag av rollen:

hvis (currentUser.hasRole ("admin")) {log.info ("Welcome Admin"); } annet hvis (currentUser.hasRole ("editor")) {log.info ("Welcome, Editor!"); } annet hvis (currentUser.hasRole ("forfatter")) {log.info ("Velkommen, forfatter"); } annet {log.info ("Velkommen, gjest"); }

La oss nå se hva den nåværende brukeren har lov til å gjøre i systemet:

if (currentUser.isPermitted ("articles: compose")) {log.info ("Du kan komponere en artikkel"); } annet {log.info ("Du har ikke lov til å komponere en artikkel!"); } if (currentUser.isPermitted ("articles: save")) {log.info ("Du kan lagre artikler"); } annet {log.info ("Du kan ikke lagre artikler"); } if (currentUser.isPermitted ("articles: publish")) {log.info ("Du kan publisere artikler"); } annet {log.info ("Du kan ikke publisere artikler"); }

6. Realm Configuration

I ekte applikasjoner trenger vi en måte å få brukerlegitimasjon fra en database i stedet for fra shiro.ini fil. Det er her konseptet Realm spiller inn.

I Apache Shiros terminologi er et rike en DAO som peker på en butikk med brukerlegitimasjon som trengs for autentisering og autorisasjon.

For å skape et rike trenger vi bare å implementere Rike grensesnitt. Det kan være kjedelig; rammeverket kommer imidlertid med standardimplementeringer som vi kan underklasse fra. En av disse implementeringene er JdbcRealm.

Vi lager en tilpasset realm implementering som utvides JdbcRealm klasse og overstyrer følgende metoder: doGetAuthenticationInfo (), doGetAuthorizationInfo (), getRoleNamesForUser () og getPermissions ().

La oss lage et rike ved å subklassere JdbcRealm klasse:

offentlig klasse MyCustomRealm utvider JdbcRealm {// ...}

For enkelhets skyld bruker vi java.util.Kart å simulere en database:

privat kartlegitimasjon = ny HashMap (); privat kart roller = ny HashMap (); privat kart perm = ny HashMap (); {credentials.put ("bruker", "passord"); credentials.put ("bruker2", "passord2"); credentials.put ("bruker3", "passord3"); roller.put ("bruker", ny HashSet (Arrays.asList ("admin"))); roller.put ("bruker2", ny HashSet (Arrays.asList ("redaktør"))); roller.put ("user3", ny HashSet (Arrays.asList ("forfatter"))); perm.put ("admin", ny HashSet (Arrays.asList ("*"))); perm.put ("editor", ny HashSet (Arrays.asList ("articles: *"))); perm.put ("forfatter", ny HashSet (Arrays.asList ("articles: compose", "articles: save"))); }

La oss fortsette og overstyre doGetAuthenticationInfo ():

beskyttet AuthenticationInfo doGetAuthenticationInfo (AuthenticationToken token) kaster AuthenticationException {UsernamePasswordToken uToken = (UsernamePasswordToken) token; hvis (uToken.getUsername () == null || uToken.getUsername (). isEmpty () ||! credentials.containsKey (uToken.getUsername ())) {kast ny UnknownAccountException ("brukernavn ikke funnet!"); } returner nye SimpleAuthenticationInfo (uToken.getUsername (), credentials.get (uToken.getUsername ()), getName ()); }

Vi kastet først AuthenticationToken gitt til BrukernavnPassordToken. Fra uToken, trekker vi ut brukernavnet (uToken.getUsername ()) og bruk den for å hente brukerlegitimasjonen (passordet) fra databasen.

Hvis ingen poster blir funnet - kaster vi en UnknownAccountException, ellers bruker vi legitimasjonen og brukernavnet til å konstruere en SimpleAuthenticatioInfo objekt som er returnert fra metoden.

Hvis brukerlegitimasjonen er hasjet med et salt, må vi returnere en SimpleAuthenticationInfo med tilhørende salt:

returnere nye SimpleAuthenticationInfo (uToken.getUsername (), credentials.get (uToken.getUsername ()), ByteSource.Util.bytes ("salt"), getName ());

Vi må også overstyre doGetAuthorizationInfo (), i tillegg til getRoleNamesForUser () og getPermissions ().

Til slutt, la oss koble det tilpassede riket til sikkerhetssjef. Alt vi trenger å gjøre er å erstatte IniRealm ovenfor med vårt tilpassede rike, og overføre det til DefaultSecurityManager’S konstruktør:

Realm realm = ny MyCustomRealm (); SecurityManager securityManager = ny DefaultSecurityManager (realm);

Hver annen del av koden er den samme som før. Dette er alt vi trenger for å konfigurere sikkerhetssjef med et tilpasset rike ordentlig.

Nå er spørsmålet - hvordan samsvarer rammeverket med legitimasjonen?

Som standard er JdbcRealm bruker SimpleCredentialsMatcher, som bare sjekker for likhet ved å sammenligne legitimasjonen i AuthenticationToken og AuthenticationInfo.

Hvis vi hash passordene våre, må vi informere rammeverket for å bruke en HashedCredentialsMatcher i stedet. INI-konfigurasjonene for riker med hashed-passord finner du her.

7. Logge av

Nå som vi har autentisert brukeren, er det på tide å implementere avlogging. Det gjøres ganske enkelt ved å ringe til en enkelt metode - som ugyldiggjør brukersesjonen og logger ut brukeren:

currentUser.logout ();

8. Øktledelse

Rammeverket kommer naturlig med sitt øktstyringssystem. Hvis det brukes i et webmiljø, er det som standard HttpSession gjennomføring.

For et frittstående program bruker det sitt enterprise session management system. Fordelen er at selv i et skrivebordsmiljø kan du bruke et øktobjekt som du ville gjort i et vanlig webmiljø.

La oss ta en titt på et raskt eksempel og samhandle med økten til den nåværende brukeren:

Sessionsøkt = currentUser.getSession (); session.setAttribute ("nøkkel", "verdi"); Strengverdi = (String) session.getAttribute ("nøkkel"); if (value.equals ("value")) {log.info ("Hentet riktig verdi! [" + verdi + "]"); }

9. Shiro for en webapplikasjon med våren

Så langt har vi skissert den grunnleggende strukturen til Apache Shiro, og vi har implementert den i et skrivebordsmiljø. La oss fortsette med å integrere rammeverket i en Spring Boot-applikasjon.

Merk at hovedfokuset her er Shiro, ikke Spring-applikasjonen - vi skal bare bruke det til å drive et enkelt eksempel på app.

9.1. Avhengigheter

For det første må vi legge til våravhengighetsavhengigheten til vår pom.xml:

 org.springframework.boot spring-boot-starter-parent 2.2.6.RELEASE 

Deretter må vi legge til følgende avhengigheter til det samme pom.xml fil:

 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-freemarker org.apache.shiro shiro-spring-boot-web-starter $ {apache-shiro-core-version} 

9.2. Konfigurasjon

Legge til shiro-spring-boot-web-starter avhengighet til vår pom.xml vil som standard konfigurere noen funksjoner i Apache Shiro-applikasjonen, for eksempel Sikkerhetssjef.

Imidlertid trenger vi fortsatt å konfigurere Rike og Shiro sikkerhetsfiltre. Vi bruker samme tilpassede rike som definert ovenfor.

Og så, i hovedklassen der Spring Boot-applikasjonen kjøres, la oss legge til følgende Bønne definisjoner:

@Bean public Realm realm () {return new MyCustomRealm (); } @Bean offentlig ShiroFilterChainDefinition shiroFilterChainDefinition () {DefaultShiroFilterChainDefinition filter = nytt DefaultShiroFilterChainDefinition (); filter.addPathDefinition ("/ secure", "authc"); filter.addPathDefinition ("/ **", "anon"); returfilter; }

I ShiroFilterChainDefinition, brukte vi godkjenning filter til /sikre banen og brukte anon filtrer på andre baner ved hjelp av Ant-mønsteret.

Både godkjenning og anon filtre følger som standard for webapplikasjoner. Andre standardfiltre finner du her.

Hvis vi ikke definerte Rike bønne, ShiroAutoConfiguration vil som standard gi et IniRealm implementering som forventer å finne en shiro.ini fil i src / main / resources eller src / main / resources / META-INF.

Hvis vi ikke definerer en ShiroFilterChainDefinition bønne, rammeverket sikrer alle stier og setter innloggings-URL som innlogging.jsp.

Vi kan endre denne standard innloggings-URL-en og andre standardinnstillinger ved å legge til følgende oppføringer i vår application.properties:

shiro.loginUrl = / logg inn shiro.successUrl = / sikker shiro.unauthorizedUrl = / login

Nå som godkjenning filteret er brukt på /sikre, vil alle forespørsler til den ruten kreve skjemaautentisering.

9.3. Autentisering og autorisasjon

La oss lage en ShiroSpringController med følgende banekartlegginger: / indeks, / login, / logout og /sikre.

De Logg Inn() metoden er der vi implementerer faktisk brukerautentisering som beskrevet ovenfor. Hvis autentisering er vellykket, blir brukeren omdirigert til den sikre siden:

Emneemne = SecurityUtils.getSubject (); if (! subject.isAuthenticated ()) {UsernamePasswordToken token = new UsernamePasswordToken (cred.getUsername (), cred.getPassword (), cred.isRememberMe ()); prøv {subject.login (token); } fange (AuthenticationException ae) {ae.printStackTrace (); attr.addFlashAttribute ("feil", "Ugyldig legitimasjon"); returner "redirect: / login"; }} returner "redirect: / secure";

Og nå i sikre() implementering, nåværende bruker ble oppnådd ved å påkalle SecurityUtils.getSubject (). Brukerens rolle og tillatelser videreføres til den sikre siden, samt brukerens rektor:

Subject currentUser = SecurityUtils.getSubject (); String rolle = "", tillatelse = ""; hvis (currentUser.hasRole ("admin")) {role = role + "You are an Admin"; } annet hvis (currentUser.hasRole ("editor")) {role = role + "Du er redaktør"; } annet hvis (currentUser.hasRole ("forfatter")) {role = role + "Du er en forfatter"; } if (currentUser.isPermitted ("articles: compose")) {permission = permission + "Du kan komponere en artikkel,"; } annet {tillatelse = tillatelse + "Du har ikke lov til å komponere en artikkel !,"; } hvis (currentUser.isPermitted ("artikler: lagre")) {tillatelse = tillatelse + "Du kan lagre artikler,"; } annet {tillatelse = tillatelse + "\ nDu kan ikke lagre artikler,"; } if (currentUser.isPermitted ("articles: publish")) {permission = permission + "\ nDu kan publisere artikler"; } annet {tillatelse = tillatelse + "\ nDu kan ikke publisere artikler"; } modelMap.addAttribute ("brukernavn", currentUser.getPrincipal ()); modelMap.addAttribute ("tillatelse", tillatelse); modelMap.addAttribute ("rolle", rolle); returner "sikker";

Og vi er ferdige. Slik kan vi integrere Apache Shiro i en Spring Boot-applikasjon.

Vær også oppmerksom på at rammeverket tilbyr flere kommentarer som kan brukes sammen med definisjoner av filterkjeder for å sikre applikasjonen vår.

10. JEE-integrasjon

Integrering av Apache Shiro i en JEE-applikasjon er bare et spørsmål om å konfigurere web.xml fil. Som vanlig forventer konfigurasjonen shiro.ini å være i klassestien. En detaljert eksempelkonfigurasjon er tilgjengelig her. JSP-kodene finner du også her.

11. Konklusjon

I denne opplæringen så vi på Apache Shiros autentiserings- og autorisasjonsmekanismer. Vi fokuserte også på hvordan du definerer et tilpasset rike og kobler det til Sikkerhetssjef.

Som alltid er den komplette kildekoden tilgjengelig på GitHub.


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