Лесно единично влизане с пролетна сигурност OAuth2

1. Общ преглед

В този урок ще обсъдим как да внедрим SSO - Single Sign On - с помощта на Spring Security OAuth и Spring Boot, като използваме Keycloak като сървър за оторизация.

Ще използваме 4 отделни приложения:

  • Сървър за оторизация - който е централният механизъм за удостоверяване
  • Ресурсен сървър - доставчикът на Foo s
  • Две клиентски приложения - приложения, използващи SSO

Много просто казано, когато потребителят се опита да осъществи достъп до ресурс чрез едно клиентско приложение, той ще бъде пренасочен първо към удостоверяване чрез сървъра за упълномощаване. Keycloak ще влезе в потребителя и докато все още е влязъл в първото приложение, ако до второто клиентско приложение се осъществява достъп със същия браузър, потребителят няма да има нужда да въвежда своите идентификационни данни отново.

Ще използваме типа на издаване на код за разрешаване извън OAuth2, за да стимулираме делегирането на удостоверяване.

Ще използваме стека OAuth в Spring Security 5. Ако искате да използвате наследствения стек Spring Security OAuth, разгледайте тази предишна статия: Просто еднократно влизане с Spring Security OAuth2 (наследствен стек)

Съгласно ръководството за миграция:

Spring Security се позовава на тази функция като OAuth 2.0 Login, докато Spring Security OAuth я нарича SSO

Добре, нека скочим направо.

2. Сървърът за оторизация

Преди това стекът Spring OAuth предлагаше възможност за настройка на сървър за оторизация като пролетно приложение.

Въпреки това стекът OAuth е остарял от Spring и сега ще използваме Keycloak като наш сървър за оторизация.

Така че този път ще настроим нашия Authorization Server като вграден сървър Keycloak в приложение Spring Spring .

В нашата предварителна конфигурация ще дефинираме два клиента, ssoClient-1 и ssoClient-2 , по един за всяко клиентско приложение.

3. Ресурсният сървър

След това се нуждаем от Resource Server или REST API, който ще ни предостави Foo, което ще консумира нашето клиентско приложение.

По същество е същото, което използвахме за нашите приложения за Angular Client по-рано.

4. Клиентските приложения

Сега нека разгледаме нашето приложение за клиент Thymeleaf; ние, разбира се, ще използваме Spring Boot, за да сведем до минимум конфигурацията.

Имайте предвид, че ще трябва да имаме 2 от тях, за да демонстрираме функционалността на единния вход .

4.1. Зависимости на Maven

Първо, ще се нуждаем от следните зависимости в нашия pom.xml :

 org.springframework.boot spring-boot-starter-web   org.springframework.boot spring-boot-starter-oauth2-client   org.springframework.boot spring-boot-starter-thymeleaf   org.thymeleaf.extras thymeleaf-extras-springsecurity5   org.springframework spring-webflux   io.projectreactor.netty reactor-netty 

За да включим цялата поддръжка на клиента, която ще ни е необходима, включително сигурност, трябва само да добавим spring-boot-starter-oauth2-client . Освен това, тъй като старата RestTemplate ще бъде остаряла, ще използваме WebClient и затова добавихме spring-webflux и reactor-netty .

4.2. Конфигурация на защитата

След това, най-важната част, конфигурацията за защита на първото ни клиентско приложение:

@EnableWebSecurity public class UiSecurityConfig extends WebSecurityConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.antMatcher("/**") .authorizeRequests() .antMatchers("/") .permitAll() .anyRequest() .authenticated() .and() .oauth2Login(); } @Bean WebClient webClient(ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository) { ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 = new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository, authorizedClientRepository); oauth2.setDefaultOAuth2AuthorizedClient(true); return WebClient.builder().apply(oauth2.oauth2Configuration()).build(); } }

Основната част на тази конфигурация е методът oauth2Login () , който се използва за активиране на поддръжката на OAuth 2.0 за влизане в Spring Security. Тъй като използваме Keycloak, което по подразбиране е решение за единичен вход за уеб приложения и RESTful уеб услуги, не е необходимо да добавяме допълнителни конфигурации за SSO.

И накрая, дефинирахме и WebClient боб, който да действа като прост HTTP клиент за обработка на заявки, които трябва да бъдат изпратени до нашия ресурсен сървър.

И ето приложението.yml :

spring: security: oauth2: client: registration: custom: client-id: ssoClient-1 client-secret: ssoClientSecret-1 scope: read,write authorization-grant-type: authorization_code redirect-uri: //localhost:8082/ui-one/login/oauth2/code/custom provider: custom: authorization-uri: //localhost:8083/auth/realms/baeldung/protocol/openid-connect/auth token-uri: //localhost:8083/auth/realms/baeldung/protocol/openid-connect/token user-info-uri: //localhost:8083/auth/realms/baeldung/protocol/openid-connect/userinfo user-name-attribute: preferred_username thymeleaf: cache: false server: port: 8082 servlet: context-path: /ui-one resourceserver: api: project: url: //localhost:8081/sso-resource-server/api/foos/ 

Тук spring.security.oauth2.client.registration е основното пространство от имена за регистриране на клиент. Дефинирахме клиент с регистрационен идентификатор по поръчка . След това дефинирахме неговия клиентски идентификатор , клиентска тайна , обхват , тип разрешение за предоставяне и пренасочване-uri , които, разбира се, трябва да бъдат същите като дефинираните за нашия сървър за оторизация

След това дефинирахме нашия доставчик на услуги или сървъра за оторизация, отново със същия идентификатор по поръчка , и изброихме различните му URI за Spring Security, които да използваме. Това е всичко, което трябва да дефинираме, и рамката прави целия процес на влизане, включително пренасочване към Keycloak, безпроблемно за нас .

Също така имайте предвид, че в нашия пример тук пуснахме нашия сървър за оторизация, но разбира се можем да използваме и други доставчици на трети страни като Facebook или GitHub.

4.3. Контролерът

Нека сега внедрим нашия контролер в клиентското приложение, за да поискаме Foo s от нашия ресурсен сървър:

@Controller public class FooClientController { @Value("${resourceserver.api.url}") private String fooApiUrl; @Autowired private WebClient webClient; @GetMapping("/foos") public String getFoos(Model model) { List foos = this.webClient.get() .uri(fooApiUrl) .retrieve() .bodyToMono(new ParameterizedTypeReference
    
     () { }) .block(); model.addAttribute("foos", foos); return "foos"; } }
    

As we can see, we have only one method here that'll dish out the resources to the foos template. We did not have to add any code for login.

4.4. Front End

Now, let's take a look at the front-end configuration of our client application. We're not going to focus on that here, mainly because we already covered in on the site.

Our client application here has a very simple front-end; here's the index.html:

Spring OAuth Client Thymeleaf - 1 Welcome !

Login

And the foos.html:

Spring OAuth Client Thymeleaf -1 Hi, preferred_username   
    
ID Name
No foos
ID Name

The foos.html page needs the users to be authenticated. If a non-authenticated user tries to access foos.html, they'll be redirected to Keycloak's login page first.

4.5. The Second Client Application

We'll configure a second application, Spring OAuth Client Thymeleaf -2 using another client_idssoClient-2.

It'll mostly be the same as the first application we just described.

The application.yml will differ to include a different client_id, client_secret and redirect_uri in its spring.security.oauth2.client.registration:

spring: security: oauth2: client: registration: custom: client-id: ssoClient-2 client-secret: ssoClientSecret-2 scope: read,write authorization-grant-type: authorization_code redirect-uri: //localhost:8084/ui-two/login/oauth2/code/custom

And, of course, we need to have a different server port for it as well, so that we can run them in parallel:

server: port: 8084 servlet: context-path: /ui-two

Finally, we'll tweak the front end HTMLs to have a title as Spring OAuth Client Thymeleaf – 2 instead of – 1 so that we can distinguish between the two.

5. Testing SSO Behavior

To test SSO behavior, let's run our Applications.

We'll need all our 4 Boot Apps – the Authorization Server, the Resource Server and both Client Applications – to be up and running for this.

Now let's open up a browser, say Chrome, and log in to Client-1 using the credentials [email protected]/123. Next, in another window or tab, hit the URL for Client-2. On clicking the login button, we'll be redirected to the Foos page straightaway, bypassing the authentication step.

Similarly, if the user logs in to Client-2 first, they need not enter their username/password for Client-1.

6. Conclusion

В този урок се фокусирахме върху прилагането на Single Sign-On с помощта на Spring Security OAuth2 и Spring Boot, използвайки Keycloak като доставчик на самоличност.

Както винаги, пълният изходен код може да бъде намерен в GitHub.