Uvod u Apache Shiro

1. Pregled

U ovom ćemo članku pogledati Apache Shiro, svestrani Java sigurnosni okvir.

Okvir je vrlo prilagodljiv i modularan, jer nudi provjeru autentičnosti, autorizaciju, kriptografiju i upravljanje sesijama.

2. Ovisnost

Apache Shiro ima mnogo modula. Međutim, u ovom uputstvu koristimo shiro-core samo artefakt.

Dodajmo ga našem pom.xml:

 org.apache.shiro shiro-core 1.4.0 

Najnovija verzija modula Apache Shiro može se naći na Maven Central.

3. Konfiguriranje upravitelja sigurnosti

The SecurityManager središnji je dio okvira Apachea Shiroa. Aplikacije će obično imati pokrenut jedan primjerak.

U ovom uputstvu istražujemo okvir u radnom okruženju. Da bismo konfigurirali okvir, moramo stvoriti shiro.ini datoteka u mapi resursa sa sljedećim sadržajem:

[korisnici] korisnik = lozinka, admin korisnik2 = lozinka2, urednik korisnik3 = lozinka3, autor [uloge] admin = * urednik = članci: * autor = članci: sastavi, članci: spremi

The [korisnici] odjeljak shiro.ini Datoteka config definira korisničke vjerodajnice koje prepoznaje SecurityManager. Format je: strrincipal (korisničko ime) = lozinka, uloga1, uloga2,…, uloga.

Uloge i njihova pridružena dopuštenja deklariraju se u [uloge] odjeljak. The admin uloga dobiva dopuštenje i pristup svakom dijelu aplikacije. To je naznačeno zamjenskim znakom (*) simbol.

The urednik uloga ima sva dopuštenja povezana s članaka dok Autor uloga može samo sastaviti i uštedjeti članak.

The SecurityManager koristi se za konfiguriranje SecurityUtils razred. Od SecurityUtils možemo dobiti trenutnog korisnika koji stupa u interakciju sa sustavom i izvoditi operacije provjere autentičnosti i autorizacije.

Iskoristimo IniRealm za učitavanje naših definicija korisnika i uloga iz shiro.ini datoteku, a zatim je koristite za konfiguriranje datoteke DefaultSecurityManager objekt:

IniRealm iniRealm = novi IniRealm ("put do klase: shiro.ini"); SecurityManager securityManager = novi DefaultSecurityManager (iniRealm); SecurityUtils.setSecurityManager (securityManager); Predmet currentUser = SecurityUtils.getSubject ();

Sad kad imamo SecurityManager koji je svjestan vjerodajnica i uloga definiranih u shiro.ini datoteku, prijeđimo na autentifikaciju i autorizaciju korisnika.

4. Autentifikacija

U terminologijama Apachea Shira, a Predmet je bilo koji entitet koji je u interakciji sa sustavom. To može biti čovjek, skripta ili REST klijent.

Pozivanje SecurityUtils.getSubject () vraća instancu trenutne Predmet, odnosno trenutni korisnik.

Sad kad imamo trenutni korisnik Objekt, možemo izvršiti provjeru autentičnosti na priloženim vjerodajnicama:

if (! currentUser.isAuthenticated ()) {UsernamePasswordToken token = novo UsernamePasswordToken ("user", "password"); token.setRememberMe (true); isprobajte {currentUser.login (token); } catch (UnknownAccountException uae) {log.error ("Korisničko ime nije pronađeno!", uae); } catch (IncorrectCredentialsException ice) {log.error ("Nevažeće vjerodajnice!", led); } catch (LockedAccountException lae) {log.error ("Vaš račun je zaključan!", lae); } catch (AuthenticationException ae) {log.error ("Neočekivana pogreška!", ae); }}

Prvo provjeravamo nije li trenutni korisnik već provjeren. Zatim stvaramo token za provjeru autentičnosti s korisnikovim nalogodavcem (Korisničko ime) i vjerodajnica (zaporka).

Zatim se pokušavamo prijaviti pomoću tokena. Ako su dostavljene vjerodajnice točne, sve bi trebalo ići u redu.

Postoje različite iznimke za različite slučajeve. Također je moguće izbaciti prilagođenu iznimku koja bolje odgovara zahtjevima aplikacije. To se može učiniti podklasiranjem AccountException razred.

5. Odobrenje

Autentifikacija pokušava provjeriti identitet korisnika dok autorizacija pokušava kontrolirati pristup određenim resursima u sustavu.

Podsjetimo da svakom korisniku kojeg smo stvorili u programu dodijelimo jednu ili više uloga shiro.ini datoteka. Nadalje, u odjeljku uloga definiramo različita dopuštenja ili razine pristupa za svaku ulogu.

Sada da vidimo kako to možemo koristiti u našoj aplikaciji za provođenje kontrole pristupa korisnika.

U shiro.ini datoteku, dajemo administratoru ukupan pristup svakom dijelu sustava.

Urednik ima ukupan pristup svim resursima / operacijama u vezi s članaka, a autor je ograničen na samo skladanje i spremanje članaka samo.

Pozdravimo trenutnog korisnika na temelju uloge:

if (currentUser.hasRole ("admin")) {log.info ("Administrator dobrodošlice"); } else if (currentUser.hasRole ("editor")) {log.info ("Dobrodošli, uredniče!"); } else if (currentUser.hasRole ("author")) {log.info ("Dobrodošli, autore"); } else {log.info ("Dobrodošli, gost"); }

Sada, da vidimo što trenutni korisnik smije raditi u sustavu:

if (currentUser.isPermitted ("articles: compose")) {log.info ("Možete sastaviti članak"); } else {log.info ("Ne smijete sastaviti članak!"); } if (currentUser.isPermit ("articles: save")) {log.info ("Možete spremiti članke"); } else {log.info ("Ne možete spremiti članke"); } if (currentUser.isPermit ("članci: objaviti")) {log.info ("Članke možete objaviti"); } else {log.info ("Ne možete objavljivati ​​članke"); }

6. Konfiguracija područja

U stvarnim aplikacijama trebat će nam način za dobivanje vjerodajnica korisnika iz baze podataka, a ne iz shiro.ini datoteka. Tu na scenu stupa koncept Carstva.

U terminologiji Apache Shiro, Realm je DAO koji ukazuje na pohranu vjerodajnica korisnika potrebnih za autentifikaciju i autorizaciju.

Da bismo stvorili područje, trebamo samo implementirati Carstvo sučelje. To može biti zamorno; međutim, okvir dolazi sa zadanim implementacijama iz kojih možemo podrazrediti. Jedna od tih implementacija je JdbcRealm.

Izrađujemo prilagođenu implementaciju područja koja se proširuje JdbcRealm klase i poništava sljedeće metode: doGetAuthenticationInfo (), doGetAuthorizationInfo (), getRoleNamesForUser () i getPermissions ().

Stvorimo carstvo podklasiranjem JdbcRealm razred:

javna klasa MyCustomRealm proširuje JdbcRealm {// ...}

Radi jednostavnosti koristimo java.util.Map za simulaciju baze podataka:

vjerodajnice privatne karte = novi HashMap (); privatna karta uloge = nova HashMap (); privatna karta trajna traka = nova HashMap (); {vjerodajnice.put ("korisnik", "lozinka"); credentials.put ("user2", "password2"); credentials.put ("user3", "password3"); role.put ("korisnik", novi HashSet (Arrays.asList ("admin"))); role.put ("user2", novi HashSet (Arrays.asList ("editor"))); role.put ("user3", novi HashSet (Arrays.asList ("autor"))); perm.put ("admin", novi HashSet (Arrays.asList ("*"))); perm.put ("editor", novi HashSet (Arrays.asList ("articles: *"))); perm.put ("autor", novi HashSet (Arrays.asList ("članci: sastavi", "članci: spremi"))); }

Nastavimo i poništimo doGetAuthenticationInfo ():

zaštićena AuthenticationInfo doGetAuthenticationInfo (žeton AuthenticationToken) baca žeton AuthenticationException {UsernamePasswordToken uToken = (UsernamePasswordToken); if (uToken.getUsername () == null || uToken.getUsername (). isEmpty () ||! credentials.containsKey (uToken.getUsername ())) {throw new UnknownAccountException ("korisničko ime nije pronađeno!"); } vrati novu SimpleAuthenticationInfo (uToken.getUsername (), vjerodajnice.get (uToken.getUsername ()), getName ()); }

Prvo smo bacili AuthenticationToken pod uvjetom da UsernamePasswordToken. Od uToken, izdvajamo korisničko ime (uToken.getUsername ()) i upotrijebite ga za dobivanje vjerodajnica korisnika (lozinke) iz baze podataka.

Ako nije pronađen nijedan zapis - bacamo znak UnknownAccountException, inače koristimo vjerodajnicu i korisničko ime za izradu a SimpleAuthenticatioInfo objekt koji se vraća iz metode.

Ako se vjerodajnica korisnika rasprši s soli, moramo vratiti a SimpleAuthenticationInfo s pripadajućom soli:

vrati novu SimpleAuthenticationInfo (uToken.getUsername (), credentials.get (uToken.getUsername ()), ByteSource.Util.bytes ("sol"), getName ());

Također moramo nadjačati doGetAuthorizationInfo (), kao i getRoleNamesForUser () i getPermissions ().

Napokon, uključimo prilagođeno područje u securityManager. Sve što trebamo je zamijeniti IniRealm gore s našim prilagođenim carstvom i prenesite ga na DefaultSecurityManagerKonstruktor:

Područje carstvo = novo MyCustomRealm (); SecurityManager securityManager = novi DefaultSecurityManager (carstvo);

Svaki drugi dio koda isti je kao i prije. To je sve što nam treba za konfiguriranje securityManager s prilagođenim carstvom pravilno.

Sada je pitanje - kako se okvir podudara s vjerodajnicama?

Prema zadanim postavkama JdbcRealm koristi SimpleCredentialsMatcher, koji samo provjerava jednakost uspoređujući vjerodajnice u AuthenticationToken i AutentifikacijaInfo.

Ako hashiramo svoje lozinke, moramo obavijestiti okvir za korištenje a HashedCredentialsMatcher umjesto toga. Konfiguracije INI za područja s raspršenim lozinkama možete pronaći ovdje.

7. Odjava

Sad kad smo provjerili identitet korisnika, vrijeme je da implementiramo odjavu. To se postiže jednostavnim pozivanjem jedne metode - koja onesposobljava korisničku sesiju i odjavljuje korisnika:

currentUser.logout ();

8. Upravljanje sjednicama

Okvir se prirodno isporučuje sa sustavom upravljanja sjednicama. Ako se koristi u web okruženju, po defaultu je HttpSession provedba.

Za samostalnu aplikaciju koristi svoj sustav upravljanja poslovnim sesijama. Prednost je u tome što čak i u radnom okruženju možete koristiti objekt sesije kao što biste to radili u tipičnom web okruženju.

Pogledajmo kratki primjer i stupimo u interakciju sa sesijom trenutnog korisnika:

Sjednica sesije = currentUser.getSession (); session.setAttribute ("ključ", "vrijednost"); Vrijednost niza = (Niz) session.getAttribute ("ključ"); if (value.equals ("value")) {log.info ("Dohvaćena točna vrijednost! [" + vrijednost + "]"); }

9. Shiro za web aplikaciju s proljećem

Do sada smo iznijeli osnovnu strukturu Apache Shiro-a i implementirali smo je u radno okruženje. Krenimo integracijom okvira u aplikaciju Spring Boot.

Imajte na umu da je ovdje glavni fokus Shiro, a ne aplikacija Spring - to ćemo koristiti samo za pokretanje jednostavnog primjera aplikacije.

9.1. Ovisnosti

Prvo, moramo dodati roditeljsku ovisnost Spring Boot u našu pom.xml:

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

Dalje, moramo dodati sljedeće ovisnosti na iste pom.xml datoteka:

 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. Konfiguracija

Dodavanje shiro-spring-boot-web-starter ovisnost o našoj pom.xml će prema zadanim postavkama konfigurirati neke značajke aplikacije Apache Shiro, poput SecurityManager.

Međutim, još uvijek moramo konfigurirati Carstvo i Shiro sigurnosni filtri. Koristit ćemo isto prethodno definirano prilagođeno područje.

Dakle, u glavnu klasu u kojoj se izvodi aplikacija Spring Boot, dodajmo sljedeće Grah definicije:

@Bean public Realm carstvo () {return new MyCustomRealm (); } @Bean public ShiroFilterChainDefinition shiroFilterChainDefinition () {DefaultShiroFilterChainDefinition filter = new DefaultShiroFilterChainDefinition (); filter.addPathDefinition ("/ sigurno", "authc"); filter.addPathDefinition ("/ **", "anonimno"); povratni filter; }

U Definicija ShiroFilterChain, primijenili smo authc filtrirati do /siguran put i primijenio anonimno filtrirajte na drugim stazama koristeći Ant obrazac.

Oba authc i anonimno filtri se standardno isporučuju za web aplikacije. Ostale zadane filtre možete pronaći ovdje.

Ako nismo definirali Carstvo grah, ShiroAutoConfiguration po defaultu će pružiti IniRealm implementacija koja očekuje pronalaženje a shiro.ini uloži u src / glavni / resursi ili src / main / resources / META-INF.

Ako ne definiramo a Definicija ShiroFilterChain bean, okvir osigurava sve staze i postavlja URL za prijavu kao login.jsp.

Ovaj zadani URL za prijavu i druge zadane postavke možemo promijeniti dodavanjem sljedećih unosa u naš primjena.svojstva:

shiro.loginUrl = / prijava shiro.successUrl = / sigurna shiro.unauthorizedUrl = / prijava

Sad kad je authc primijenjen je filtar /siguran, svi zahtjevi za tu rutu zahtijevat će provjeru autentičnosti obrasca.

9.3. Autentifikacija i autorizacija

Stvorimo a ShiroSpringController sa sljedećim preslikavanjima puta: /indeks, / prijava, / odjava i /siguran.

The prijaviti se() metoda je gdje implementiramo stvarnu autentifikaciju korisnika kako je gore opisano. Ako je provjera autentičnosti uspješna, korisnik se preusmjerava na sigurnu stranicu:

Predmet predmeta = SecurityUtils.getSubject (); if (! subject.isAuthenticated ()) {UsernamePasswordToken token = new UsernamePasswordToken (cred.getUsername (), cred.getPassword (), cred.isRememberMe ()); probajte {subject.login (token); } catch (AuthenticationException ae) {ae.printStackTrace (); attr.addFlashAttribute ("pogreška", "Nevažeće vjerodajnice"); return "preusmjeravanje: / prijava"; }} return "preusmjeravanje: / sigurno";

A sada u siguran() provedba, trenutni korisnik je dobiveno pozivanjem na SecurityUtils.getSubject (). Uloga i dozvole korisnika prosljeđuju se na sigurnu stranicu, kao i glavni korisnik:

Predmet currentUser = SecurityUtils.getSubject (); Niz uloge = "", dopuštenje = ""; if (currentUser.hasRole ("admin")) {role = role + "Vi ste administrator"; } else if (currentUser.hasRole ("editor")) {role = role + "Vi ste urednik"; } else if (currentUser.hasRole ("author")) {role = role + "Vi ste autor"; } if (currentUser.isPermitted ("articles: compose")) {dozvola = dopuštenje + "Možete sastaviti članak,"; } else {dozvola = dopuštenje + "Ne smijete sastavljati članak !,"; } if (currentUser.isPermitted ("articles: save")) {dozvola = dopuštenje + "Možete spremiti članke,"; } else {dozvola = dopuštenje + "\ nNe možete spremiti članke,"; } if (currentUser.isPermitted ("članci: objaviti")) {dozvola = dopuštenje + "\ nČlanove možete objaviti"; } else {dozvola = dopuštenje + "\ nNe možete objavljivati ​​članke"; } modelMap.addAttribute ("korisničko ime", currentUser.getPrincipal ()); modelMap.addAttribute ("dopuštenje", dopuštenje); modelMap.addAttribute ("uloga", uloga); povratak "siguran";

I gotovi smo. Tako možemo integrirati Apache Shiro u Spring Boot Application.

Također, imajte na umu da okvir nudi dodatne bilješke koje se mogu koristiti zajedno s definicijama lanca filtara kako bi se osigurala naša aplikacija.

10. JEE integracija

Integriranje Apache Shiro u JEE aplikaciju samo je pitanje konfiguriranja web.xml datoteka. Kao i obično, konfiguracija očekuje shiro.ini biti na razrednom putu. Detaljan primjer konfiguracije dostupan je ovdje. Također, JSP oznake možete pronaći ovdje.

11. Zaključak

U ovom smo tutorijalu pogledali mehanizme provjere autentičnosti i autorizacije Apache Shiro. Također smo se usredotočili na to kako definirati prilagođeno područje i uključiti ga u SecurityManager.

Kao i uvijek, cjeloviti izvorni kod dostupan je na GitHub-u.