CAS SSO med forårssikkerhed

1. Oversigt

I denne vejledning ser vi på Apereo Central Authentication Service (CAS), og vi ser, hvordan en Spring Boot-tjeneste kan bruge den til godkendelse. CAS er en enterprise Single Sign-On (SSO) løsning, der også er open source.

Hvad er SSO? Når du logger ind på YouTube, Gmail og Maps med de samme legitimationsoplysninger, er det Single Sign-On. Vi demonstrerer dette ved at oprette en CAS-server og en Spring Boot-app. Spring Boot-appen bruger CAS til godkendelse.

2. Opsætning af CAS-server

2.1. CAS-installation og afhængigheder

Serveren bruger Maven (Gradle) War Overlay-stil for at lette opsætning og implementering:

git klon //github.com/apereo/cas-overlay-template.git cas-server

Denne kommando vil klone cas-overlay-skabelon ind i cas-server vejviser.

Nogle af de aspekter, vi vil dække, inkluderer JSON-serviceregistrering og JDBC-databaseforbindelse. Så vi tilføjer deres moduler til afhængigheder sektion af build.gradle fil:

kompilér "org.apereo.cas: cas-server-support-json-service-registry: $ {casServerVersion}" kompilér "org.apereo.cas: cas-server-support-jdbc: $ {casServerVersion}"

Lad os sørge for at kontrollere den nyeste version af casServer.

2.2. CAS-serverkonfiguration

Før vi kan starte CAS-serveren, skal vi tilføje nogle grundlæggende konfigurationer. Lad os starte med at oprette en cas-server / src / main / resources mappe og i denne mappe. Dette vil blive fulgt af oprettelsen af application.properties i mappen også:

server.port = 8443 spring.main.allow-bean-definition-overriding = true server.ssl.key-store = classpath: / etc / cas / thekeystore server.ssl.key-store-password = changeit

Lad os fortsætte med oprettelsen af ​​nøglelagerfilen, der henvises til i konfigurationen ovenfor. Først skal vi oprette mapperne / etc / cas og / etc / cas / config i cas-server / src / main / resources.

Derefter skal vi ændre biblioteket til cas-server / src / main / resources / etc / cas og kør kommandoen for at generere nøglebutikken:

keytool -genkey -keyalg RSA -alias thekeystore -keystore thekeystore -storepass changeit -validity 360 -keysize 2048

For at vi ikke skal have en SSL-håndtryksfejl, skal vi bruge lokal vært som værdien af ​​for- og efternavn. Vi skal også bruge det samme til organisationens navn og enhed. Desuden er vi nødt til at importere nøglebutikken ind i JDK / JRE, som vi bruger til at køre vores klientapp:

keytool -importkeystore -srckeystore thekeystore -destkeystore $ JAVA11_HOME / jre / lib / sikkerhed / cacerts

Adgangskoden til kilde- og destinationsnøglelageret er Ændre det. På Unix-systemer skal vi muligvis køre denne kommando med admin (sudo) privilegium. Efter importen skal vi genstarte alle forekomster af Java, der kører, eller genstart systemet.

Vi bruger JDK11, fordi det kræves i CAS version 6.1.x. Vi definerede også miljøvariablen $ JAVA11_HOME, der peger på dens hjemmekatalog. Vi kan nu starte CAS-serveren:

./gradlew køre -Dorg.gradle.java.home = $ JAVA11_HOME

Når applikationen starter, ser vi “KLAR” trykt på terminalen og serveren vil være tilgængelig på // localhost: 8443.

2.3. CAS-serverbrugerkonfiguration

Vi kan ikke logge ind endnu, da vi ikke har konfigureret nogen bruger. CAS har forskellige metoder til styring af konfiguration, herunder standalone-tilstand. Lad os oprette en konfigurationsmappe cas-server / src / main / resources / etc / cas / config hvor vi opretter en egenskabsfil cas. ejendomme. Nu kan vi definere en statisk bruger i egenskabsfilen:

cas.authn.accept.users = casuser :: Mellon

Vi er nødt til at kommunikere placeringen af ​​konfigurationsmappen til CAS-serveren, før indstillingerne træder i kraft. Lad os opdatere opgaver.gradle så vi kan videregive placeringen som et JVM-argument fra kommandolinjen:

opgavekørsel (gruppe: "build", beskrivelse: "Kør CAS-webapplikationen i indlejret containertilstand") {afhænger af 'build' doLast {def casRunArgs = new ArrayList (Arrays.asList ("-server -noverify -Xmx2048M -XX: + TieredCompilation -XX: TieredStopAtLevel = 1 ".split (" "))) if (project.hasProperty ('args')) {casRunArgs.addAll (project.args.split ('\ s +'))} javaexec {main = "-jar" jvmArgs = casRunArgs args = ["build / libs / $ {casWebApplicationBinaryName}"] logger.info "Startet $ {commandLine}"}}}

Vi gemmer derefter filen og kører:

./gradlew run -Dorg.gradle.java.home = $ JAVA11_HOME -Pargs = "- Dcas.standalone.configurationDirectory = / cas-server / src / main / resources / etc / cas / config"

Vær opmærksom på, at værdien af cas.standalone.configurationDirectory er en absolut sti. Vi kan nu gå til // localhost: 8443 og log ind med brugernavn casuser og adgangskode Mellon.

3. Opsætning af CAS-klient

Vi bruger Spring Initializr til at generere en Spring Boot-klient-app. Det får det Internettet, Sikkerhed, Freemarker og DevTools afhængigheder. Desuden tilføjer vi også afhængigheden af ​​Spring Security CAS-modul til dets pom.xml:

 org.springframework.security spring-security-cas 5.3.0.RELEASE 

Lad os endelig tilføje følgende Spring Boot-egenskaber for at konfigurere appen:

server.port = 8900 spring.freemarker.suffix = .ftl

4. Registrering af CAS-server service

Klientapplikationer skal registrere sig hos CAS-serveren inden enhver godkendelse. CAS-server understøtter brugen af ​​YAML-, JSON-, MongoDB- og LDAP-klientregistre.

I denne vejledning bruger vi JSON Service Registry-metoden. Lad os oprette endnu en mappe cas-server / src / main / resources / etc / cas / services. Det er denne mappe, der huser JSON-filerne i servicebasen.

Vi opretter en JSON-fil, der indeholder definitionen af ​​vores klientapplikation. Filens navn, casSecuredApp-8900.json, følger mønsteret serviceName-Id.json:

{"@class": "org.apereo.cas.services.RegexRegisteredService", "serviceId": "// localhost: 8900 / login / cas", "name": "casSecuredApp", "id": 8900, "logoutType ":" BACK_CHANNEL "," logoutUrl ":" // localhost: 8900 / exit / cas "}

Det serviceId attribut definerer et regex URL-mønster til klientapplikationen. Mønsteret skal matche URL'en til klientapplikationen.

Det id attribut skal være unik. Med andre ord bør der ikke være to eller flere tjenester med det samme id registreret på den samme CAS-server. At have duplikat id vil føre til konflikter og tilsidesættelse af konfigurationer.

Vi konfigurerer også logout-typen til at være BACK_CHANNEL og den URL, der skal være // localhost: 8900 / exit / cas så vi kan foretage en enkelt logout senere. Inden CAS-serveren kan bruge vores JSON-konfigurationsfil, skal vi aktivere JSON-registreringsdatabasen i vores cas. ejendomme:
cas.serviceRegistry.initFromJson = sand cas.serviceRegistry.json.location = classpath: / etc / cas / services

5. Konfiguration af CAS-klient til enkelt login

Det næste trin for os er at konfigurere Spring Security til at arbejde med CAS-serveren. Vi bør også kontrollere den fulde strøm af interaktioner, kaldet en CAS-sekvens.

Lad os tilføje følgende bønnekonfigurationer til CasSecuredApplication klasse i vores Spring Boot-app:

@Bean offentlig CasAuthenticationFilter casAuthenticationFilter (AuthenticationManager authenticationManager, ServiceProperties serviceProperties) kaster undtagelse {CasAuthenticationFilter filter = new CasAuthenticationFilter (); filter.setAuthenticationManager (authenticationManager); filter.setServiceProperties (serviceProperties); returfilter; } @Bean public ServiceProperties serviceProperties () {logger.info ("serviceegenskaber"); ServiceProperties serviceProperties = nye ServiceProperties (); serviceProperties.setService ("// cas-client: 8900 / login / cas"); serviceProperties.setSendRenew (false); retur serviceEjendomme; } @Bean offentlig TicketValidator ticketValidator () {returner ny Cas30ServiceTicketValidator ("// localhost: 8443"); } @Bean public CasAuthenticationProvider casAuthenticationProvider (TicketValidator ticketValidator, ServiceProperties serviceProperties) {CasAuthenticationProvider provider = new CasAuthenticationProvider (); provider.setServiceProperties (serviceProperties); provider.setTicketValidator (ticketValidator); provider.setUserDetailsService (s -> ny bruger ("[email protected]", "Mellon", true, true, true, true, AuthorityUtils.createAuthorityList ("ROLE_ADMIN"))); provider.setKey ("CAS_PROVIDER_LOCALHOST_8900"); returudbyder }

Det Serviceegenskaber bønne har samme URL som serviceId i casSecuredApp-8900.json. Dette er vigtigt, fordi det identificerer denne klient til CAS-serveren.

Det sendRenew ejendom af Serviceegenskaber er indstillet til falsk. Dette betyder, at en bruger kun behøver at præsentere loginoplysninger til serveren en gang.

Det GodkendelseEntryPoint bean håndterer godkendelsesundtagelser. Således omdirigerer brugeren til login-URL'en på CAS-serveren til godkendelse.

Sammenfattende går godkendelsesflowet:

  1. En bruger forsøger at få adgang til en sikker side, der udløser en godkendelsesundtagelse
  2. Undtagelsen udløser GodkendelseEntryPoint. Som svar, den GodkendelseEntryPoint fører brugeren til CAS-serverens login-side - // localhost: 8443 / login
  3. Ved vellykket godkendelse omdirigerer serveren tilbage til klienten med en billet
  4. CasAuthenticationFilter vil hente omdirigering og ringe CasAuthenticationProvider
  5. CasAuthenticationProvider vil bruge TicketValidator for at bekræfte den præsenterede billet på CAS-serveren
  6. Hvis billetten er gyldig, får brugeren en omdirigering til den anmodede sikre URL

Lad os endelig konfigurere HttpSikkerhed for at sikre nogle ruter i WebSecurityConfig. I processen tilføjer vi også godkendelsesindgangspunktet til håndtering af undtagelser:

@ Override beskyttet ugyldig konfiguration (HttpSecurity http) kaster undtagelse {http.authorizeRequests (). AntMatchers ("/ secure", "/ login"). Godkendt () .and (). ExceptionHandling () .authenticationEntryPoint (authenticationEntryPoint ()); }

6. CAS-klient-enkeltlogoutkonfiguration

Indtil videre har vi behandlet single sign-on; lad os nu overveje CAS single logout (SLO).

Applikationer, der bruger CAS til styring af brugergodkendelse, kan logge ud af en bruger fra to steder:

  • Klientapplikationen kan logge en bruger fra sig selv lokalt - dette påvirker ikke brugerens loginstatus i andre applikationer, der bruger den samme CAS-server
  • Klientapplikationen kan også logge ud af brugeren fra CAS-serveren - dette får brugeren til at blive logget ud fra alle andre klientapps, der er tilsluttet den samme CAS-server.

Vi sætter først logout på klientapplikationen og udvider det derefter til enkeltlogout på CAS-serveren.

For at gøre det klart, hvad der foregår bag scenen, opretter vi en Log ud() metode til at håndtere den lokale logout. Efter succes omdirigerer vi os til en side med et link til enkelt logout:

@GetMapping ("/ logout") offentlig Strenglogout (HttpServletRequest anmodning, HttpServletResponse svar, SecurityContextLogoutHandler logoutHandler) {Authentication auth = SecurityContextHolder .getContext (). GetAuthentication (); logoutHandler.logout (anmodning, svar, godkendelse); ny CookieClearingLogoutHandler (AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY) .logout (anmodning, svar, godkendelse); returner "auth / logout"; }

I den enkelte logout-proces udløber CAS-serveren først brugerens billet og sender derefter en async-anmodning til alle registrerede klientapps. Hver klientapp, der modtager dette signal, udfører en lokal logout. Derved opnås målet om aflogning en gang, det vil medføre en aflogning overalt.

Når det er sagt, lad os tilføje nogle bønnekonfigurationer til vores klientapp. Specifikt i CasSecuredApplicaiton:

@Bean offentlig SecurityContextLogoutHandler securityContextLogoutHandler () {returner ny SecurityContextLogoutHandler (); } @Bean public LogoutFilter logoutFilter () {LogoutFilter logoutFilter = new LogoutFilter ("// localhost: 8443 / logout", securityContextLogoutHandler ()); logoutFilter.setFilterProcessesUrl ("/ logout / cas"); retur logoutFilter; } @Bean offentlig SingleSignOutFilter singleSignOutFilter () {SingleSignOutFilter singleSignOutFilter = ny SingleSignOutFilter (); singleSignOutFilter.setCasServerUrlPrefix ("// localhost: 8443"); singleSignOutFilter.setLogoutCallbackPath ("/ exit / cas"); singleSignOutFilter.setIgnoreInitConfiguration (sand); returner singleSignOutFilter; }

Det logoutFilter vil opfange anmodninger til / logout / cas og omdirigere applikationen til CAS-serveren. Det SingleSignOutFilter vil opfange anmodninger fra CAS-serveren og udføre den lokale logout.

7. Tilslutning af CAS-server til en database

Vi kan konfigurere CAS-serveren til at læse legitimationsoplysninger fra en MySQL-database. Vi bruger prøve database over en MySQL-server, der kører på en lokal maskine. Lad os opdatere cas-server / src / main / resources / etc / cas / config / cas.properties:

cas.authn.accept.users = cas.authn.jdbc.query [0] .sql = VÆLG * FRA brugere HVOR e-mail =? cas.authn.jdbc.query [0] .url = jdbc: mysql: //127.0.0.1: 3306 / test? useUnicode = true & useJDBCCompliantTimezoneShift = true & useLegacyDatetimeCode = false & serverTimezone = UTC cas.authn.jdbc.query [0] .dialect = org.hibernate.dialect.MySQLDialect cas.authn.jdbc.query [0] .bruger = root cas.auth. [0]. Kodeord = rod cas.authn.jdbc.query [0] .ddlAuto = ingen cas.authn.jdbc.query [0] .driverClass = com.mysql.cj.jdbc.Driver cas.authn.jdbc.query [0] .fieldPassword = adgangskode cas.authn.jdbc.query [0] .passwordEncoder.type = INGEN

Vi indstiller cas.authn.accept.users til blank. Dette deaktiverer CAS-serverens brug af statiske brugeropbevaringssteder.

I henhold til SQL ovenfor er brugernes legitimationsoplysninger gemt i brugere bord. Det e-mail kolonne er det, der repræsenterer brugernes hovedstol (brugernavn).

Sørg for at kontrollere listen over understøttede databaser, tilgængelige drivere og dialekter. Vi indstiller også kodeordstypen til INGEN. Andre krypteringsmekanismer og deres ejendommelige egenskaber er også tilgængelige.

Bemærk, at hovedmanden i CAS-serverens database skal være den samme som klientapplikationen.

Lad os opdatere CasAuthenticationProvider at have det samme brugernavn som CAS-serveren:

@Bean offentlig CasAuthenticationProvider casAuthenticationProvider () {CasAuthenticationProvider provider = ny CasAuthenticationProvider (); provider.setServiceProperties (serviceProperties ()); provider.setTicketValidator (ticketValidator ()); provider.setUserDetailsService (s -> ny bruger ("[email protected]", "Mellon", true, true, true, true, AuthorityUtils.createAuthorityList ("ROLE_ADMIN"))); provider.setKey ("CAS_PROVIDER_LOCALHOST_8900"); returudbyder }

CasAuthenticationProvider bruger ikke adgangskoden til godkendelse. Ikke desto mindre skal dets brugernavn matche CAS-serverens, for at godkendelse skal lykkes. CAS-server kræver, at der kører en MySQL-server lokal vært i havn 3306. Brugernavnet og adgangskoden skal være rod.

Genstart CAS-serveren og Spring Boot-appen igen. Brug derefter de nye legitimationsoplysninger til godkendelse.

8. Konklusion

Vi har set på, hvordan man bruger CAS SSO med Spring Security og mange af de involverede konfigurationsfiler. Der er mange andre aspekter af CAS SSO, der kan konfigureres. Fra temaer og protokoltyper til godkendelsespolitikker.

Disse og andre findes i dokumenterne. Kildekoden til CAS-serveren og Spring Boot-appen er tilgængelig på GitHub.