Single Sign-On na przykładzie GitHub
Single Sign-On na przykładzie GitHub
Artykuł ten ma na celu zaprezentowanie podejścia Single Sign-On na przykładzie logowania z użyciem serwisu GitHub. Single Sign-On to podejście które zakłada jeden centralny punkt logowania za pomocą którego użytkownik ma dostęp do wszystkich chronionych zasobów. Nie ma zatem potrzeby pamiętania kolejnych haseł oraz nie trzeba tworzyć dodatkowych mechanizmów uwierzytelniania. Zanim przejdziemy do projektowania aplikacji omówimy czym jest standard OAuth 2.0 (z ang. Open Authorization) – specyfikacja dostępna tutaj: http://tools.ietf.org/html/rfc6749. OAuth 2.0 to „protokół” który daje możliwość budowania bezpiecznych mechanizmów autoryzacyjnych z użyciem różnych platform np. portali WWW czy aplikacji mobilnych. Specyfikacja nie narzuca sztywnych reguł implementowania tego standardu dlatego określenie protokół użyte jest raczej na wyrost, jednak można spotkać się z takim określeniem. W dobie serwisów społecznościowych niemal każdy posiada konto tj. google, facebook, linkedin, twitter. Użycie zaufanego portalu do logowania do innych platform to idea standardu OAuth. Mechanizm działania (flow) jest następujący:
- użytkownik klika w link i jest przekierowywany na stronę dostawcy np. może to być konto w serwisie google,
- użytkownik potwierdza udzielenie uprawnień,
- użytkownik przekierowywany jest na stronę aplikacji z unikalnym tokenem,
- przeglądarka (klient) zapamiętuje token np. w pliku cookie i dołącza go do każdego zapytania,
- aplikacja uzyskuje dostęp do danych użytkownika np. adresu email i w ten sposób identyfikuje użytkowników na swojej platformie.
[źródło] http://habr.com/en/post/449182/
Pamiętajmy jednak, że autoryzacja to nie to samo co uwierzytelnianie, standard OAuth używany jest do autoryzacji czyli uzyskania dostępu, uwierzytelnianie natomiast to potwierdzenie tożsamości. Projektowanie aplikacji zaczynamy od nowego projektu Spring Boota – niezbędne zależności – plik pom.xml:
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Edgware.RELEASE</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <artifactId>tomcat-embed-el</artifactId> <groupId>org.apache.tomcat.embed</groupId> </exclusion> <exclusion> <artifactId>tomcat-embed-core</artifactId> <groupId>org.apache.tomcat.embed</groupId> </exclusion> <exclusion> <artifactId>tomcat-embed-websocket</artifactId> <groupId>org.apache.tomcat.embed</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
Aplikacja będzie działać na zewnętrznym serwerze zatem klasa startowa przedstawia się następująco:
@SpringBootApplication public class Oauth2SsoApplication extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(Oauth2SsoApplication.class, args); } @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(Oauth2SsoApplication.class); } }
Włączamy funkcjonalność OAuth – klasa SecurityConfig. Konfigurujemy dostęp do aplikacji, jeden zasób będzie tylko dostępny publicznie \public, inne zasoby są chronione i wymagają autentykacji:
@Configuration @EnableOAuth2Sso class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(httpecurity http) throws Exception { http.authorizeRequests() .antMatchers("/login", "/public") .permitAll() .anyRequest() .authenticated(); } }
Plik application.properties:
security.oauth2.client.client-id = *** security.oauth2.client.client-secret = *** #access token security.oauth2.client.access-token-uri = http://github.com/login/oauth/access_token #authorization security.oauth2.client.user-authorization-uri = http://github.com/login/oauth/authorize #zcheme of authentication security.oauth2.client.client-authentication-scheme = form #user info endpoint security.oauth2.resource.user-info-uri = http://api.github.com/user #prefer user info security.oauth2.resource.prefer-token-info = false security.basic.enabled = false logging.level.org.springframework.security = DEBUG
parametry security.oauth2.client.client-id oraz security.oauth2.client.client-secret należy uzupełnić własnymi danymi które wygenerowane zostały przez GitHuba. Link gdzie opisany jest proces jak dokładnie utworzyć aplikację na GitHubie – http://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app/. Po utworzeniu aplikacji jako Authorization callback URL podajemy przykładowo – należy dostosować do własnej domeny:
http://oauth.javaleader.pl/login
Tworzymy RestController:
@RestController public class RestControllerApi { @GetMapping("/public") String getPublicContent(){ return "getPublicContent"; } @GetMapping("/private") String getPrivateContent(){ return "getPrivateContent"; } }
Aplikacja wdrożona jest pod adresem: http://oauth.javaleader.pl
Testy:
http://oauth.javaleader.pl/public -> treść dostępna publicznie
http://oauth.javaleader.pl/private -> treść dostępna wyłącznie po zalogowaniu się z użyciem GitHubApi.
P.S.
Wdrożenie aplikacji w kontenerze servletów – Apache Tomcat wymagało dodania w pliku ./conf/server.xml wpisu:
<Host name="oauth.javaleader.pl" appBase="javaleaderoauth" unpackWARs="true" autoDeploy="true"> <!-- SingleSignOn valve, share authentication between web applications Documentation at: /docs/config/valve.html --> <!-- <Valve className="org.apache.catalina.authenticator.SingleSignOn" /> --> <!-- Access log processes all example. Documentation at: /docs/config/valve.html Note: The pattern used is equivalent to using pattern="common" --> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host>
konfiguracja wirtualnych hostów:
<VirtualHost oauth.javaleader.pl:443> ServerName oauth.javaleader.pl ServerAlias oauth.javaleader.pl SSLEngine on ... ProxyPass / ajp://localhost:8009/ ProxyPassReverse / ajp://localhost:8009/ </VirtualHost>
W katalogu ./Tomcat/oauth.javaleader.pl zbudowany plik war powinien mieć nazwę ROOT. Pozwala to uniknąć doklejania zbędnej nazwy kontekstu aplikacji, w przeciwnym wypadku po wdrożeniu aplikacji na produkcje logowanie nie działa poprawnie właśnie przez doklejanie nazwy wynikowego pliku WAR do ścieżki URL aplikacji. Polecam dodatkowo wpis https://javaleader.pl/2019/06/12/konfiguracja-virtual-hosts-apache-tomcat-protokolu-ajp/ który przedstawia jak skonfigurować aplikację z życiem serwera VPS & Tomcata & protokołu AJP.
Leave a comment