Im vorherigen Beispiel wurde eine HTTP-Sitzung (und Cookies) verwendet, um Anforderungen an den Server zu autorisieren. Für die Implementierung eines REST-Service ist diese Autorisierungsmethode jedoch nicht geeignet, da eine der Anforderungen der REST-Architektur das Fehlen eines Status ist. In diesem Artikel implementieren wir einen REST-Service, dessen Autorisierung von Anforderungen mithilfe eines Zugriffstokens ausgeführt wird.
Ein bisschen Theorie
Bei der Authentifizierung werden die Anmeldeinformationen des Benutzers (Login / Passwort) überprüft. Die Benutzerauthentifizierung erfolgt durch Vergleich des von ihm eingegebenen Logins / Passworts mit den gespeicherten Daten.
Die Autorisierung ist die Überprüfung der Rechte eines Benutzers auf bestimmte Ressourcen. Die Autorisierung erfolgt direkt, wenn der Benutzer auf die Ressource zugreift.
Betrachten wir die Arbeitsreihenfolge der beiden oben genannten Methoden zum Autorisieren von Anforderungen.
Autorisieren von Anforderungen über eine HTTP-Sitzung:
- Der Benutzer wird auf eine der folgenden Arten authentifiziert.
- Auf dem Server wird eine HTTP-Sitzung erstellt und ein JSESSIONID-Cookie, in dem die Sitzungskennung gespeichert ist.
- Das JSESSIONID-Cookie wird an den Client übertragen und im Browser gespeichert.
- Bei jeder nachfolgenden Anforderung wird ein JSESSIONID-Cookie an den Server gesendet.
- Der Server findet die entsprechende HTTP-Sitzung mit Informationen zum aktuellen Benutzer und bestimmt, ob der Benutzer das Recht hat, diesen Anruf zu tätigen.
- Um die Anwendung zu beenden, müssen Sie die HTTP-Sitzung vom Server löschen.
Autorisieren von Anforderungen mithilfe eines Zugriffstokens:
- Der Benutzer wird auf eine der folgenden Arten authentifiziert.
- Der Server generiert ein mit einem privaten Schlüssel signiertes Zugriffstoken und sendet es dann an den Client. Das Token enthält die Kennung des Benutzers und seine Rolle.
- Das Token wird auf dem Client gespeichert und bei jeder nachfolgenden Anforderung an den Server übertragen. In der Regel wird der HTTP-Header für die Autorisierung zum Übertragen des Tokens verwendet.
- Der Server überprüft die Signatur des Tokens, extrahiert daraus die Benutzer-ID und seine Rolle und bestimmt, ob der Benutzer das Recht hat, diesen Aufruf zu tätigen.
- Um die Anwendung zu beenden, entfernen Sie einfach das Token auf dem Client, ohne mit dem Server interagieren zu müssen.
Das JSON-Web-Token (JWT) ist derzeit das am häufigsten verwendete Zugriffstoken-Format. Das JWT-Token enthält drei durch Punkte getrennte Blöcke: Header, Payload und Signatur. Die ersten beiden Blöcke sind im JSON-Format und im base64-Format codiert. Der Satz von Feldern kann entweder aus reservierten Namen (iss, iat, exp) oder beliebigen Namen / Wert-Paaren bestehen. Die Signatur kann sowohl mit symmetrischen als auch mit asymmetrischen Verschlüsselungsalgorithmen generiert werden.
Implementierung
Wir werden einen REST-Service implementieren, der die folgende API bereitstellt:
- GET / auth / login - Startet den Benutzerauthentifizierungsprozess.
- POST / auth / token - Fordern Sie ein neues Paar von Zugriffs- / Aktualisierungstoken an.
- GET / api / repositories - Ruft eine Liste der Bitbucket-Repositorys des aktuellen Benutzers ab.
Übergeordnete Anwendungsarchitektur
Beachten Sie, dass Bitbucket Serveranforderungen an den Server autorisiert, da die Anwendung aus drei interagierenden Komponenten besteht und nicht nur Clientanforderungen an den Server autorisiert. Wir werden die Methodenautorisierung nicht nach Rollen konfigurieren, um das Beispiel nicht komplizierter zu machen. Wir haben nur eine API-Methode GET / api / repositories, die nur von authentifizierten Benutzern aufgerufen werden kann. Der Server kann alle Vorgänge an Bitbucket ausführen, die von der OAuth-Registrierung des Clients zugelassen werden.
Der Registrierungsprozess für den OAuth-Client ist im vorherigen Artikel beschrieben.
Für die Implementierung verwenden wir Spring Boot Version 2.2.2.RELEASE und Spring Security Version 5.2.1.RELEASE.
Überschreiben Sie AuthenticationEntryPoint
Wenn in einer Standard-Webanwendung auf eine sichere Ressource zugegriffen wird und im Sicherheitskontext kein Authentifizierungsobjekt vorhanden ist, leitet Spring Security den Benutzer zur Authentifizierungsseite weiter. Für einen REST-Service wäre es in diesem Fall jedoch angemessener, den HTTP-Status 401 (UNAUTHORIZED) zurückzugeben.
RestAuthenticationEntryPoint
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
}
}
Erstellen Sie einen Anmeldeendpunkt
Wir verwenden weiterhin OAuth2 mit dem Autorisierungstyp Autorisierungstyp für die Benutzerauthentifizierung. Im vorherigen Schritt haben wir jedoch den Standard-AuthenticationEntryPoint durch unsere eigene Implementierung ersetzt, sodass wir eine explizite Methode zum Starten des Authentifizierungsprozesses benötigen. Wenn wir eine GET-Anfrage an / auth / login senden, leiten wir den Benutzer zur Bitbucket-Authentifizierungsseite weiter. Der Parameter dieser Methode ist die Rückruf-URL, über die das Zugriffstoken nach erfolgreicher Authentifizierung zurückgegeben wird.
Anmeldeendpunkt
@Path("/auth")
public class AuthEndpoint extends EndpointBase {
...
@GET
@Path("/login")
public Response authorize(@QueryParam(REDIRECT_URI) String redirectUri) {
String authUri = "/oauth2/authorization/bitbucket";
UriComponentsBuilder builder = fromPath(authUri).queryParam(REDIRECT_URI, redirectUri);
return handle(() -> temporaryRedirect(builder.build().toUri()).build());
}
}
Überschreiben Sie AuthenticationSuccessHandler
AuthenticationSuccessHandler wird nach erfolgreicher Authentifizierung aufgerufen. Lassen Sie uns hier ein Zugriffstoken generieren, ein Aktualisierungstoken und eine Umleitung zu der Rückrufadresse, die zu Beginn des Authentifizierungsprozesses gesendet wurde. Wir geben das Zugriffstoken mit dem Parameter GET request und das Aktualisierungstoken im httpOnly-Cookie zurück. Wir werden später analysieren, was ein Aktualisierungstoken ist.
ExampleAuthenticationSuccessHandler
public class ExampleAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private final TokenService tokenService;
private final AuthProperties authProperties;
private final HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository;
public ExampleAuthenticationSuccessHandler(
TokenService tokenService,
AuthProperties authProperties,
HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository) {
this.tokenService = requireNonNull(tokenService);
this.authProperties = requireNonNull(authProperties);
this.authorizationRequestRepository = requireNonNull(authorizationRequestRepository);
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
log.info("Logged in user {}", authentication.getPrincipal());
super.onAuthenticationSuccess(request, response, authentication);
}
@Override
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
Optional<String> redirectUri = getCookie(request, REDIRECT_URI).map(Cookie::getValue);
if (redirectUri.isPresent() && !isAuthorizedRedirectUri(redirectUri.get())) {
throw new BadRequestException("Received unauthorized redirect URI.");
}
return UriComponentsBuilder.fromUriString(redirectUri.orElse(getDefaultTargetUrl()))
.queryParam("token", tokenService.newAccessToken(toUserContext(authentication)))
.build().toUriString();
}
@Override
protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
redirectToTargetUrl(request, response, authentication);
}
private boolean isAuthorizedRedirectUri(String uri) {
URI clientRedirectUri = URI.create(uri);
return authProperties.getAuthorizedRedirectUris()
.stream()
.anyMatch(authorizedRedirectUri -> {
// Only validate host and port. Let the clients use different paths if they want to.
URI authorizedURI = URI.create(authorizedRedirectUri);
return authorizedURI.getHost().equalsIgnoreCase(clientRedirectUri.getHost())
&& authorizedURI.getPort() == clientRedirectUri.getPort();
});
}
private TokenService.UserContext toUserContext(Authentication authentication) {
ExampleOAuth2User principal = (ExampleOAuth2User) authentication.getPrincipal();
return TokenService.UserContext.builder()
.login(principal.getName())
.name(principal.getFullName())
.build();
}
private void addRefreshTokenCookie(HttpServletResponse response, Authentication authentication) {
RefreshToken token = tokenService.newRefreshToken(toUserContext(authentication));
addCookie(response, REFRESH_TOKEN, token.getId(), (int) token.getValiditySeconds());
}
private void redirectToTargetUrl(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
String targetUrl = determineTargetUrl(request, response, authentication);
if (response.isCommitted()) {
logger.debug("Response has already been committed. Unable to redirect to " + targetUrl);
return;
}
addRefreshTokenCookie(response, authentication);
authorizationRequestRepository.removeAuthorizationRequestCookies(request, response);
getRedirectStrategy().sendRedirect(request, response, targetUrl);
}
}
Überschreiben Sie AuthenticationFailureHandler
Falls der Benutzer nicht authentifiziert ist, leiten wir ihn an die Rückrufadresse weiter, die zu Beginn des Authentifizierungsprozesses mit dem Fehlerparameter übergeben wurde, der den Fehlertext enthält.
ExampleAuthenticationFailureHandler
public class ExampleAuthenticationFailureHandler implements AuthenticationFailureHandler {
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
private final HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository;
public ExampleAuthenticationFailureHandler(
HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository) {
this.authorizationRequestRepository = requireNonNull(authorizationRequestRepository);
}
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
String targetUrl = getFailureUrl(request, exception);
authorizationRequestRepository.removeAuthorizationRequestCookies(request, response);
redirectStrategy.sendRedirect(request, response, targetUrl);
}
private String getFailureUrl(HttpServletRequest request, AuthenticationException exception) {
String targetUrl = getCookie(request, Cookies.REDIRECT_URI)
.map(Cookie::getValue)
.orElse(("/"));
return UriComponentsBuilder.fromUriString(targetUrl)
.queryParam("error", exception.getLocalizedMessage())
.build().toUriString();
}
}
Erstellen Sie den TokenAuthenticationFilter
Die Aufgabe dieses Filters besteht darin, das Zugriffstoken gegebenenfalls aus dem Autorisierungsheader zu extrahieren, zu validieren und den Sicherheitskontext zu initialisieren.
TokenAuthenticationFilter
public class TokenAuthenticationFilter extends OncePerRequestFilter {
private final UserService userService;
private final TokenService tokenService;
public TokenAuthenticationFilter(
UserService userService, TokenService tokenService) {
this.userService = requireNonNull(userService);
this.tokenService = requireNonNull(tokenService);
}
@Override
protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain chain) throws ServletException, IOException {
try {
Optional<String> jwtOpt = getJwtFromRequest(request);
if (jwtOpt.isPresent()) {
String jwt = jwtOpt.get();
if (isNotEmpty(jwt) && tokenService.isValidAccessToken(jwt)) {
String login = tokenService.getUsername(jwt);
Optional<User> userOpt = userService.findByLogin(login);
if (userOpt.isPresent()) {
User user = userOpt.get();
ExampleOAuth2User oAuth2User = new ExampleOAuth2User(user);
OAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(oAuth2User, oAuth2User.getAuthorities(), oAuth2User.getProvider());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
} catch (Exception e) {
logger.error("Could not set user authentication in security context", e);
}
chain.doFilter(request, response);
}
private Optional<String> getJwtFromRequest(HttpServletRequest request) {
String token = request.getHeader(AUTHORIZATION);
if (isNotEmpty(token) && token.startsWith("Bearer ")) {
token = token.substring(7);
}
return Optional.ofNullable(token);
}
}
Erstellen Sie einen Aktualisierungs-Token-Endpunkt
Aus Sicherheitsgründen wird die Lebensdauer des Zugriffstokens normalerweise klein gehalten. Wenn es dann gestohlen wird, kann ein Angreifer es nicht auf unbestimmte Zeit verwenden. Um den Benutzer nicht zu zwingen, sich immer wieder bei der Anwendung anzumelden, wird das Aktualisierungstoken verwendet. Es wird vom Server nach erfolgreicher Authentifizierung zusammen mit dem Zugriffstoken ausgegeben und hat eine längere Lebensdauer. Mit ihm können Sie ein neues Token-Paar anfordern. Es wird empfohlen, das Aktualisierungstoken im httpOnly-Cookie zu speichern.
Token-Endpunkt aktualisieren
@Path("/auth")
public class AuthEndpoint extends EndpointBase {
...
@POST
@Path("/token")
@Produces(APPLICATION_JSON)
public Response refreshToken(@CookieParam(REFRESH_TOKEN) String refreshToken) {
return handle(() -> {
if (refreshToken == null) {
throw new InvalidTokenException("Refresh token was not provided.");
}
RefreshToken oldRefreshToken = tokenService.findRefreshToken(refreshToken);
if (oldRefreshToken == null || !tokenService.isValidRefreshToken(oldRefreshToken)) {
throw new InvalidTokenException("Refresh token is not valid or expired.");
}
Map<String, String> result = new HashMap<>();
result.put("token", tokenService.newAccessToken(of(oldRefreshToken.getUser())));
RefreshToken newRefreshToken = newRefreshTokenFor(oldRefreshToken.getUser());
return Response.ok(result).cookie(createRefreshTokenCookie(newRefreshToken)).build();
});
}
}
Überschreiben Sie AuthorizationRequestRepository
Spring Security verwendet das AuthorizationRequestRepository-Objekt, um OAuth2AuthorizationRequest-Objekte für die Dauer des Authentifizierungsprozesses zu speichern. Die Standardimplementierung ist die Klasse HttpSessionOAuth2AuthorizationRequestRepository, die die HTTP-Sitzung als Repository verwendet. weil Unser Service sollte den Status nicht speichern, diese Implementierung ist für uns nicht geeignet. Lassen Sie uns unsere eigene Klasse implementieren, die HTTP-Cookies verwendet.
HttpCookieOAuth2AuthorizationRequestRepository
public class HttpCookieOAuth2AuthorizationRequestRepository implements AuthorizationRequestRepository<OAuth2AuthorizationRequest> {
private static final int COOKIE_EXPIRE_SECONDS = 180;
private static final String OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME = "OAUTH2-AUTH-REQUEST";
@Override
public OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) {
return getCookie(request, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME)
.map(cookie -> deserialize(cookie, OAuth2AuthorizationRequest.class))
.orElse(null);
}
@Override
public void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request, HttpServletResponse response) {
if (authorizationRequest == null) {
removeAuthorizationRequestCookies(request, response);
return;
}
addCookie(response, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME, serialize(authorizationRequest), COOKIE_EXPIRE_SECONDS);
String redirectUriAfterLogin = request.getParameter(QueryParams.REDIRECT_URI);
if (isNotBlank(redirectUriAfterLogin)) {
addCookie(response, REDIRECT_URI, redirectUriAfterLogin, COOKIE_EXPIRE_SECONDS);
}
}
@Override
public OAuth2AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request) {
return loadAuthorizationRequest(request);
}
public void removeAuthorizationRequestCookies(HttpServletRequest request, HttpServletResponse response) {
deleteCookie(request, response, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME);
deleteCookie(request, response, REDIRECT_URI);
}
private static String serialize(Object object) {
return Base64.getUrlEncoder().encodeToString(SerializationUtils.serialize(object));
}
@SuppressWarnings("SameParameterValue")
private static <T> T deserialize(Cookie cookie, Class<T> clazz) {
return clazz.cast(SerializationUtils.deserialize(Base64.getUrlDecoder().decode(cookie.getValue())));
}
}
Spring Security konfigurieren
Lassen Sie uns all das zusammenfassen und Spring Security konfigurieren.
WebSecurityConfig
@Configuration
@EnableWebSecurity
public static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final ExampleOAuth2UserService userService;
private final TokenAuthenticationFilter tokenAuthenticationFilter;
private final AuthenticationFailureHandler authenticationFailureHandler;
private final AuthenticationSuccessHandler authenticationSuccessHandler;
private final HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository;
@Autowired
public WebSecurityConfig(
ExampleOAuth2UserService userService,
TokenAuthenticationFilter tokenAuthenticationFilter,
AuthenticationFailureHandler authenticationFailureHandler,
AuthenticationSuccessHandler authenticationSuccessHandler,
HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository) {
this.userService = userService;
this.tokenAuthenticationFilter = tokenAuthenticationFilter;
this.authenticationFailureHandler = authenticationFailureHandler;
this.authenticationSuccessHandler = authenticationSuccessHandler;
this.authorizationRequestRepository = authorizationRequestRepository;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.sessionManagement(sm -> sm.sessionCreationPolicy(STATELESS))
.exceptionHandling(eh -> eh
.authenticationEntryPoint(new RestAuthenticationEntryPoint())
)
.authorizeRequests(authorizeRequests -> authorizeRequests
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
)
.oauth2Login(oauth2Login -> oauth2Login
.failureHandler(authenticationFailureHandler)
.successHandler(authenticationSuccessHandler)
.userInfoEndpoint(userInfoEndpoint -> userInfoEndpoint.userService(userService))
.authorizationEndpoint(authEndpoint -> authEndpoint.authorizationRequestRepository(authorizationRequestRepository))
);
http.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
}
Erstellen Sie einen Repositorys-Endpunkt
Für welche Authentifizierung über OAuth2 und Bitbucket erforderlich war - die Möglichkeit, mithilfe der Bitbucket-API auf Ihre Ressourcen zuzugreifen. Wir verwenden die Bitbucket-Repositorys-API, um eine Liste der Repositorys des aktuellen Benutzers abzurufen.
Endpunkt des Repositorys
@Path("/api")
public class ApiEndpoint extends EndpointBase {
@Autowired
private BitbucketService bitbucketService;
@GET
@Path("/repositories")
@Produces(APPLICATION_JSON)
public List<Repository> getRepositories() {
return handle(bitbucketService::getRepositories);
}
}
public class BitbucketServiceImpl implements BitbucketService {
private static final String BASE_URL = "https://api.bitbucket.org";
private final Supplier<RestTemplate> restTemplate;
public BitbucketServiceImpl(Supplier<RestTemplate> restTemplate) {
this.restTemplate = restTemplate;
}
@Override
public List<Repository> getRepositories() {
UriComponentsBuilder uriBuilder = fromHttpUrl(format("%s/2.0/repositories", BASE_URL));
uriBuilder.queryParam("role", "member");
ResponseEntity<BitbucketRepositoriesResponse> response = restTemplate.get().exchange(
uriBuilder.toUriString(),
HttpMethod.GET,
new HttpEntity<>(new HttpHeadersBuilder()
.acceptJson()
.build()),
BitbucketRepositoriesResponse.class);
BitbucketRepositoriesResponse body = response.getBody();
return body == null ? emptyList() : extractRepositories(body);
}
private List<Repository> extractRepositories(BitbucketRepositoriesResponse response) {
return response.getValues() == null
? emptyList()
: response.getValues().stream().map(BitbucketServiceImpl.this::convertRepository).collect(toList());
}
private Repository convertRepository(BitbucketRepository bbRepo) {
Repository repo = new Repository();
repo.setId(bbRepo.getUuid());
repo.setFullName(bbRepo.getFullName());
return repo;
}
}
Testen
Zum Testen benötigen wir einen kleinen HTTP-Server, an den das Zugriffstoken gesendet wird. Versuchen wir zunächst, den Endpunkt des Repositorys ohne Zugriffstoken aufzurufen, und stellen Sie sicher, dass in diesem Fall ein 401-Fehler angezeigt wird. Anschließend authentifizieren wir uns. Starten Sie dazu den Server und rufen Sie den Browser unter http: // localhost: 8080 / auth / login auf . Nachdem wir den Benutzernamen / das Passwort eingegeben haben, erhält der Client ein Token und ruft den Endpunkt des Repositorys erneut auf. Dann wird ein neues Token angefordert und der Endpunkt des Repositorys mit dem neuen Token erneut aufgerufen.
OAuth2JwtExampleClient
public class OAuth2JwtExampleClient {
/**
* Start client, then navigate to http://localhost:8080/auth/login.
*/
public static void main(String[] args) throws Exception {
AuthCallbackHandler authEndpoint = new AuthCallbackHandler(8081);
authEndpoint.start(SOCKET_READ_TIMEOUT, true);
HttpResponse response = getRepositories(null);
assert (response.getStatusLine().getStatusCode() == SC_UNAUTHORIZED);
Tokens tokens = authEndpoint.getTokens();
System.out.println("Received tokens: " + tokens);
response = getRepositories(tokens.getAccessToken());
assert (response.getStatusLine().getStatusCode() == SC_OK);
System.out.println("Repositories: " + IOUtils.toString(response.getEntity().getContent(), UTF_8));
// emulate token usage - wait for some time until iat and exp attributes get updated
// otherwise we will receive the same token
Thread.sleep(5000);
tokens = refreshToken(tokens.getRefreshToken());
System.out.println("Refreshed tokens: " + tokens);
// use refreshed token
response = getRepositories(tokens.getAccessToken());
assert (response.getStatusLine().getStatusCode() == SC_OK);
}
private static Tokens refreshToken(String refreshToken) throws IOException {
BasicClientCookie cookie = new BasicClientCookie(REFRESH_TOKEN, refreshToken);
cookie.setPath("/");
cookie.setDomain("localhost");
BasicCookieStore cookieStore = new BasicCookieStore();
cookieStore.addCookie(cookie);
HttpPost request = new HttpPost("http://localhost:8080/auth/token");
request.setHeader(ACCEPT, APPLICATION_JSON.getMimeType());
HttpClient httpClient = HttpClientBuilder.create().setDefaultCookieStore(cookieStore).build();
HttpResponse execute = httpClient.execute(request);
Gson gson = new Gson();
Type type = new TypeToken<Map<String, String>>() {
}.getType();
Map<String, String> response = gson.fromJson(IOUtils.toString(execute.getEntity().getContent(), UTF_8), type);
Cookie refreshTokenCookie = cookieStore.getCookies().stream()
.filter(c -> REFRESH_TOKEN.equals(c.getName()))
.findAny()
.orElseThrow(() -> new IOException("Refresh token cookie not found."));
return Tokens.of(response.get("token"), refreshTokenCookie.getValue());
}
private static HttpResponse getRepositories(String accessToken) throws IOException {
HttpClient httpClient = HttpClientBuilder.create().build();
HttpGet request = new HttpGet("http://localhost:8080/api/repositories");
request.setHeader(ACCEPT, APPLICATION_JSON.getMimeType());
if (accessToken != null) {
request.setHeader(AUTHORIZATION, "Bearer " + accessToken);
}
return httpClient.execute(request);
}
}
Client-Konsolenausgabe.
Received tokens: Tokens(accessToken=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJldm9sdmVjaS10ZXN0a2l0IiwidXNlcm5hbWUiOiJFdm9sdmVDSSBUZXN0a2l0IiwiaWF0IjoxNjA1NDY2MDMxLCJleHAiOjE2MDU0NjY2MzF9.UuRYMdIxzc8ZFEI2z8fAgLz-LG_gDxaim25pMh9jNrDFK6YkEaDqDO8Huoav5JUB0bJyf1lTB0nNPaLLpOj4hw, refreshToken=BBF6dboG8tB4XozHqmZE5anXMHeNUncTVD8CLv2hkaU2KsfyqitlJpgkV4HrQqPk)
Repositories: [{"id":"{c7bb4165-92f1-4621-9039-bb1b6a74488e}","fullName":"test-namespace/test-repository1"},{"id":"{aa149604-c136-41e1-b7bd-3088fb73f1b2}","fullName":"test-namespace/test-repository2"}]
Refreshed tokens: Tokens(accessToken=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJldm9sdmVjaS10ZXN0a2l0IiwidXNlcm5hbWUiOiJFdm9sdmVDSSBUZXN0a2l0IiwiaWF0IjoxNjA1NDY2MDM2LCJleHAiOjE2MDU0NjY2MzZ9.oR2A_9k4fB7qpzxvV5QKY1eU_8aZMYEom-ngc4Kuc5omeGPWyclfqmiyQTpJW_cHOcXbY9S065AE_GKXFMbh_Q, refreshToken=mdc5sgmtiwLD1uryubd2WZNjNzSmc5UGo6JyyzsiYsBgOpeaY3yw3T3l8IKauKYQ)
Quelle
Der vollständige Quellcode für die überprüfte Anwendung befindet sich auf Github .
Links
- OAuth - Wikipedia
- Spring Sicherheitsreferenz
- JSON Web Token (JWT)
- Das OAuth 2.0-Autorisierungsframework
PS
Der von uns erstellte REST-Service wird über das HTTP-Protokoll ausgeführt, um das Beispiel nicht zu komplizieren. Da unsere Token jedoch in keiner Weise verschlüsselt sind, wird empfohlen, auf einen sicheren Kanal (HTTPS) zu wechseln.