Обратите внимание, что это содержимое устарело и использует устаревший стек OAuth. Взгляните на Последняя поддержка OAuth от Spring Security .
1. Обзор
В этом быстром учебнике мы сосредоточимся на настройке OpenID Connect с помощью реализации Spring Security OAuth2.
OpenID Подключение это простой слой идентификации, построенный поверх протокола OAuth 2.0.
И, более конкретно, мы узнаем, как проверить подлинность пользователей с помощью Реализация OpenID Connect с Google .
2. Конфигурация Maven
Во-первых, мы должны добавить следующие зависимости в наше приложение Spring Boot:
org.springframework.boot spring-boot-starter-security org.springframework.security.oauth spring-security-oauth2
3. Токен Id
Прежде чем углубиться в детали реализации, давайте посмотрим, как работает OpenID и как мы будем с ним взаимодействовать.
На данный момент, это, конечно, важно уже иметь понимание OAuth2, так как OpenID построен на вершине OAuth.
Во-первых, для того, чтобы использовать функциональность идентификации, мы будем использовать новую область OAuth2 под названием openid . Это приведет к дополнительному полю в нашем токене Access Token – “ id_token “.
id_token является JWT (JSON Web Token), который содержит идентификационную информацию о пользователе, подписанную поставщиком идентификационных данных (в нашем случае Google).
Наконец, оба сервер (Код авторизации) и неявное потоки являются наиболее часто используемыми способами получения id_token , в нашем примере, мы будем использовать серверный поток .
3. Конфигурация клиента OAuth2
Далее, давайте настраивать наш клиент OAuth2 – следующим образом:
@Configuration @EnableOAuth2Client public class GoogleOpenIdConnectConfig { @Value("${google.clientId}") private String clientId; @Value("${google.clientSecret}") private String clientSecret; @Value("${google.accessTokenUri}") private String accessTokenUri; @Value("${google.userAuthorizationUri}") private String userAuthorizationUri; @Value("${google.redirectUri}") private String redirectUri; @Bean public OAuth2ProtectedResourceDetails googleOpenId() { AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails(); details.setClientId(clientId); details.setClientSecret(clientSecret); details.setAccessTokenUri(accessTokenUri); details.setUserAuthorizationUri(userAuthorizationUri); details.setScope(Arrays.asList("openid", "email")); details.setPreEstablishedRedirectUri(redirectUri); details.setUseCurrentUri(false); return details; } @Bean public OAuth2RestTemplate googleOpenIdTemplate(OAuth2ClientContext clientContext) { return new OAuth2RestTemplate(googleOpenId(), clientContext); } }
А вот и application.properts :
google.clientId=google.clientSecret= google.accessTokenUri=https://www.googleapis.com/oauth2/v3/token google.userAuthorizationUri=https://accounts.google.com/o/oauth2/auth google.redirectUri=http://localhost:8081/google-login
Обратите внимание, что:
- Сначала вам нужно получить учетные данные OAuth 2.0 для вашего веб-приложения Google от Google Разработчики Консоли .
- Мы использовали область openid для получения id_token .
- мы также использовали дополнительную область электронной включить электронную почту пользователя в id_token идентификационной информации.
- Перенаправление URI http://localhost:8081/google-login это тот же, который используется в нашем веб-приложении Google.
4. Пользовательский фильтр подключения OpenID
Теперь нам нужно создать свой собственный пользовательский OpenIdConnectFilter извлечь аутентификацию из id_token – следующим образом:
public class OpenIdConnectFilter extends AbstractAuthenticationProcessingFilter { public OpenIdConnectFilter(String defaultFilterProcessesUrl) { super(defaultFilterProcessesUrl); setAuthenticationManager(new NoopAuthenticationManager()); } @Override public Authentication attemptAuthentication( HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { OAuth2AccessToken accessToken; try { accessToken = restTemplate.getAccessToken(); } catch (OAuth2Exception e) { throw new BadCredentialsException("Could not obtain access token", e); } try { String idToken = accessToken.getAdditionalInformation().get("id_token").toString(); String kid = JwtHelper.headers(idToken).get("kid"); Jwt tokenDecoded = JwtHelper.decodeAndVerify(idToken, verifier(kid)); MapauthInfo = new ObjectMapper() .readValue(tokenDecoded.getClaims(), Map.class); verifyClaims(authInfo); OpenIdConnectUserDetails user = new OpenIdConnectUserDetails(authInfo, accessToken); return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); } catch (InvalidTokenException e) { throw new BadCredentialsException("Could not obtain user details from token", e); } } }
И вот наш простой OpenIdConnectUserDetails :
public class OpenIdConnectUserDetails implements UserDetails { private String userId; private String username; private OAuth2AccessToken token; public OpenIdConnectUserDetails(MapuserInfo, OAuth2AccessToken token) { this.userId = userInfo.get("sub"); this.username = userInfo.get("email"); this.token = token; } }
Обратите внимание, что:
- Весенние меры JwtHelper расшифровать id_token .
- id_token всегда содержит ” суб” поле, которое является уникальным идентификатором для пользователя.
- id_token также будет содержать ” электронной ” поле, как мы добавили электронной сферы по нашей просьбе.
4.1. Проверка токена ID
В приведенной выше примере мы использовали декодироватьИверифицировать () метод JwtHelper для извлечения информации из id_token, но и для проверки его.
Первым шагом для этого является проверка того, что он был подписан одним из сертификатов, указанных в Google Discovery документ.
Они меняются примерно один раз в день, поэтому мы будем использовать утилиту библиотеки под названием jwks-rsa читать их:
com.auth0 jwks-rsa 0.3.0
Давайте добавим URL-адрес, содержащий сертификаты, в application.properts файл:
google.jwkUrl=https://www.googleapis.com/oauth2/v2/certs
Теперь мы можем прочитать это свойство и построить RSAВерификатор объект:
@Value("${google.jwkUrl}") private String jwkUrl; private RsaVerifier verifier(String kid) throws Exception { JwkProvider provider = new UrlJwkProvider(new URL(jwkUrl)); Jwk jwk = provider.get(kid); return new RsaVerifier((RSAPublicKey) jwk.getPublicKey()); }
Наконец, мы также проверим утверждения в расшифровав маркере id:
public void verifyClaims(Map claims) { int exp = (int) claims.get("exp"); Date expireDate = new Date(exp * 1000L); Date now = new Date(); if (expireDate.before(now) || !claims.get("iss").equals(issuer) || !claims.get("aud").equals(clientId)) { throw new RuntimeException("Invalid claims"); } }
проверитьClaims () метод проверяет, что токен id был выпущен Google и что он не истек.
Более подробную информацию об этом можно найти в Документация Google .
5. Конфигурация безопасности
Далее давайте обсудим нашу конфигурацию безопасности:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private OAuth2RestTemplate restTemplate; @Bean public OpenIdConnectFilter openIdConnectFilter() { OpenIdConnectFilter filter = new OpenIdConnectFilter("/google-login"); filter.setRestTemplate(restTemplate); return filter; } @Override protected void configure(HttpSecurity http) throws Exception { http .addFilterAfter(new OAuth2ClientContextFilter(), AbstractPreAuthenticatedProcessingFilter.class) .addFilterAfter(OpenIdConnectFilter(), OAuth2ClientContextFilter.class) .httpBasic() .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/google-login")) .and() .authorizeRequests() .anyRequest().authenticated(); } }
Обратите внимание, что:
- Мы добавили наши пользовательские OpenIdConnectFilter после OAuth2ClientContextFilter
- Мы использовали простую конфигурацию безопасности, чтобы перенаправить пользователей на ” /Google-логин ” для проверки подлинности Google
6. Контроллер пользователя
Далее, вот простой контроллер для тестирования нашего приложения:
@Controller public class HomeController { @RequestMapping("/") @ResponseBody public String home() { String username = SecurityContextHolder.getContext().getAuthentication().getName(); return "Welcome, " + username; } }
Пример ответа (после перенаправления в Google для утверждения органов по работе с приложениями):
Welcome, [email protected]
7. Образец процесса подключения OpenID
Наконец, давайте рассмотрим пример процесса проверки подлинности OpenID Connect.
Во-первых, мы собираемся послать Запрос на аутентификацию :
https://accounts.google.com/o/oauth2/auth? client_id=sampleClientID response_type=code& scope=openid%20email& redirect_uri=http://localhost:8081/google-login& state=abc
Ответ ( после утверждения пользователем ) является перенаправлением на:
http://localhost:8081/google-login?state=abc&code=xyz
Далее мы обменяемся код для токена доступа и id_token :
POST https://www.googleapis.com/oauth2/v3/token code=xyz& client_id= sampleClientID& client_secret= sampleClientSecret& redirect_uri=http://localhost:8081/google-login& grant_type=authorization_code
Вот пример ответа:
{ "access_token": "SampleAccessToken", "id_token": "SampleIdToken", "token_type": "bearer", "expires_in": 3600, "refresh_token": "SampleRefreshToken" }
Наконец, вот что информация о фактической id_token Похоже:
{ "iss":"accounts.google.com", "at_hash":"AccessTokenHash", "sub":"12345678", "email_verified":true, "email":"[email protected]", ... }
Таким образом, вы можете сразу увидеть, насколько полезна информация о пользователе внутри токена для предоставления идентификационной информации нашему собственному приложению.
8. Заключение
В этом быстром учебнике по интро мы узнали, как проверить подлинность пользователей с помощью реализации OpenID Connect от Google.
И, как всегда, вы можете найти исходный код более на GitHub .