Ich weiß nicht, wie viele Biere es sind, aber dies ist ein Artikel über die Verwendung von OAuth. Es gibt in Java so viele OAuth-Client-Bibliotheken wie Sterne, einschließlich der von Google, aber hier werde ich OAuth berühren, ohne die Client-Bibliothek zum Lernen zu verwenden. Ich denke, es gibt viele gute Artikel über den Mechanismus von OAuth selbst, deshalb werde ich es hier nicht erklären.
Using OAuth 2.0 for Web Server Applications OpenID Connect
OAuth
Sie müssen Ihre OAuth-Anmeldeinformationen bei Google registrieren.
Erstellen Sie Anmeldeinformationen -> OAuth-Client-ID -> Webanwendung
Der Name ist angemessen. Http: // localhost: 8080 / auth
wurde zum genehmigten Umleitungs-URI hinzugefügt. Der hier hinzugefügte Wert ist der Wert, der als Umleitungsziel der Anforderung verwendet werden kann.
Dieses Mal werden wir es nur für die Entwicklung verwenden, geben Sie also localhost an. Es sollte nicht in der Produktion verwendet werden.
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.3.RELEASE")
}
}
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
dependencies {
compile(
'org.springframework.boot:spring-boot-devtools',
'org.springframework.boot:spring-boot-starter-web',
'org.springframework.boot:spring-boot-starter-thymeleaf',
'com.google.guava:guava:25.1-jre',
'com.google.http-client:google-http-client:1.23.0',
'com.google.http-client:google-http-client-gson:1.23.0',
'com.google.code.gson:gson:2.8.5',
)
}
Alles ist in Ordnung, aber dieses Mal werde ich Spring verwenden. ~~ Guava wird für Base64 verwendet und ~~ HttpClient wird zur Vereinfachung von HTTP-Anforderungen verwendet. Verwenden Sie Gson, um JSON zu analysieren.
Nachtrag: Base64 wird von Java 8 unterstützt Es ist völlig unscharf.
Erstellen Sie einen Anmeldebildschirm.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<a th:href="${endpoint}">login</a>
</body>
</html>
private static final String clientId = checkNotNull( System.getenv( "CLIENT_ID" ) );
@GetMapping( "/login" )
public String login( Model model ) {
var endpoint = "https://accounts.google.com/o/oauth2/v2/auth?"
+ "client_id=" + clientId + "&"
+ "redirect_uri="
+ UrlEscapers.urlFormParameterEscaper().escape( "http://localhost:8080/auth" )
+ "&"
+ "access_type=" + "online" + "&"
+ "state=" + "test_state" + "&"
+ "response_type=" + "code" + "&"
+ "scope="
+ UrlEscapers.urlFormParameterEscaper().escape( "email profile" );
model.addAttribute( "endpoint", endpoint );
return "login";
}
"https: // accounts.google.com / o / oauth2 / v2 / auth" ist der Endpunkt für die Google OAuth-API. Legen Sie die erforderlichen Informationen im URL-Abfrageparameter fest.
Parameter | |
---|---|
client_id |
Eine ID, die den Client identifiziert. Verwenden Sie den zuvor in der Entwicklerkonsole erhaltenen Wert. Hier wird es aus der Umgebungsvariablen erhalten. |
redirect_uri |
Das Umleitungsziel. Verwenden Sie den in der Entwicklerkonsole festgelegten Wert. |
access_type |
Dieses Mal werde ich also einen Browser verwendenonline Angegeben. |
state |
state Der Wert selbst kann frei eingestellt werden. MeistensnonceEs wird für solche verwendet. Diesmal ist es einfach ein fester Wert. |
response_type |
So erhalten Sie eine Antwort. Auf der Serverseitecode Es ist repariert. |
scope |
Geben Sie an, welche Informationen Sie mit dieser Anfrage erhalten können. Hieremail undprofile Ist eingestellt. Für verfügbare WerteDokumentSehen. |
Wenn der Nutzer dem hier generierten Link folgt, wird er zum Google-Anmeldebildschirm weitergeleitet. Wenn Sie der Anwendung zum ersten Mal erlauben, auf Ihre Benutzerinformationen zuzugreifen, wird ein Bestätigungsdialogfeld angezeigt. Wenn der Benutzer den Anmeldevorgang abgeschlossen hat, wird er zu "redirect_url" umgeleitet. Sie können ein Zugriffstoken mit dem im Abfrageparameter der URL enthaltenen "Code" anfordern.
@GetMapping( "/auth" )
public String auth( HttpServletRequest request, Model model ) throws IOException {
var params = parseQueryParams( request );
checkState( Objects.equals( params.get( "state" ), "test_state" ) );
var code = URLDecoder.decode( checkNotNull( params.get( "code" ) ), StandardCharsets.UTF_8 );
// ...
Holen Sie sich den Code und den Status aus den Abfrageparametern. state
überprüft, ob der in der vorherigen Anforderung verwendete Wert unverändert zurückgegeben wird. Beachten Sie, dass Sie möglicherweise die URL dekodieren müssen.
var response = httpPost(
"https://www.googleapis.com/oauth2/v4/token",
Map.of(
"code", code,
"client_id", clientId,
"client_secret", clientSecret,
"redirect_uri", "http://localhost:8080/auth",
"grant_type", "authorization_code"
)
).parseAsString();
var responseAsMap = parseJson( response );
Senden Sie eine POST-Anfrage an die URL zur Token-Erfassung. Es war vor einiger Zeit v2, aber aus irgendeinem Grund ist dies v4. Es ist mir egal, weil es dem offiziellen Dokument entspricht.
Parameter | |
---|---|
code |
Oben erhaltencode Wird gesendet wie es ist. |
client_id |
Kunden ID. |
client_secret |
Kundengeheimnis. Legen Sie wie die ID die in der Entwicklerkonsole angegebene fest. |
redirect_uri |
Dies ist kein Weiterleitungsziel, sondern dient zur Überprüfung. In der Konsole festlegen und das in der vorherigen Anforderung verwendete Umleitungsziel erneut festlegen. |
grant_type |
Fester Wertauthorization_code 。 |
Wenn die Anforderung als erfolgreich erkannt wird, wird ein JWT-formatiertes Token zurückgegeben.
JSON Web Token ist in drei Teile unterteilt: Header, Body und Signatur, die durch "." Getrennt sind. Holen Sie sich also jeden Teil.
var idTokenList = Splitter.on( "." ).splitToList( idToken );
var header = parseJson( base64ToStr( idTokenList.get( 0 ) ) );
var body = parseJson( base64ToStr( idTokenList.get( 1 ) ) );
var sign = idTokenList.get( 2 );
Der Header und die Körperteile sind Base64-codiertes JSON. Wenn Sie das Ergebnis nicht überprüfen, können Sie den Wert damit erhalten. Da dies jedoch eine große Sache ist, überprüfen wir auch das Token.
Holen Sie sich den öffentlichen Google-Schlüssel zur Überprüfung. Den Schlüsselspeicherort erhalten Sie unter "https: // accounts.google.com / .well-unknown / openid-configuration".
var response = httpGet( "https://accounts.google.com/.well-known/openid-configuration" ).parseAsString();
var responseAsMap = parseNestedJson( response );
var jwks = responseAsMap.get( "jwks_uri" ).toString();
Senden Sie eine GET-Anforderung an diesen URI, um den JSON abzurufen.
var keyResponse = httpGet( jwks ).parseAsString();
var keysResponseObj = parseJsonAs( keyResponse, Keys.class );
return keysResponseObj.keys.stream()
.filter( k -> k.kid.equals( kid ) )
.findAny()
.orElseThrow();
Mehrere Verschlüsselungsschlüssel werden im Array "Schlüssel" gespeichert. Suchen Sie in der Kopfzeile des ID-Tokens nach einem "Kind", das mit dem "Kind" übereinstimmt.
var signature = Signature.getInstance( "SHA256withRSA" );
signature.initVerify( KeyFactory.getInstance( "RSA" ).generatePublic( new RSAPublicKeySpec(
new BigInteger( 1, base64ToByte( n ) ),
new BigInteger( 1, base64ToByte( e ) )
) ) );
signature.update( contentToVerify.getBytes( StandardCharsets.UTF_8 ) );
return signature.verify( base64ToByte( sign ) );
Konvertieren Sie den Schlüssel JSON in das Java-Format und stellen Sie sicher, dass er bekannt ist. Eigentlich sollte das Schlüsselformat auch von JSON bezogen werden, aber hier ist es ein fester Wert von "RSA".
Nach Bestätigung der Signatur besteht der nächste Schritt darin, die Parameter zu überprüfen.
checkState( Set.of( "https://accounts.google.com", "accounts.google.com" ).contains( body.get( "iss" ) ) );
Emittent, stellen Sie sicher, dass "iss" legitim ist. Für Google ist es entweder "https: // accounts.google.com" oder "accounts.google.com".
checkState( Objects.equals( body.get( "aud" ), clientId ) );
Stellen Sie sicher, dass die Zielgruppe "aud" mit der Client-ID übereinstimmt.
var now = System.currentTimeMillis();
checkState( now <= Long.parseLong( body.get( "exp" ) ) * 1000 + 1000 );
checkState( now >= Long.parseLong( body.get( "iat" ) ) * 1000 - 1000 );
Stellen Sie schließlich sicher, dass der Schlüssel innerhalb des Ablaufdatums liegt und die Signaturzeit gültig ist. Der Wert ist in Sekunden. Der Grund für das Addieren und Subtrahieren von 1000 besteht darin, die Zeitverzögerung mit dem Server zu absorbieren.
Nachdem wir überprüft haben, ob das ID-Token gültig ist, erhalten wir den Wert. Hier werden die E-Mail-Adresse, der Name und die URL des Gesichtsbildes erfasst und im Modell festgelegt.
model.addAttribute( "email", id.get( "email" ) )
.addAttribute( "name", id.get( "name" ) )
.addAttribute( "picture", id.get( "picture" ) );
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Result</title>
</head>
<body>
<img th:src="${picture}">
<p th:text="'Email: ' + ${email}"></p>
<p th:text="'Name: ' + ${name}"></p>
<a th:href="${back}">back</a>
</body>
</html>
Um die verschiedenen APIs von Google zu verwenden, verwenden Sie die in JWT enthaltenen "access_token" und "refresh_token", um jede Anfrage zu senden.
Gehen Sie zu localhost: 8080 / login
.
Gehen Sie zu Google und authentifizieren Sie sich.
Ich habe das Gesichtsbild, die E-Mail-Adresse und den Namen erhalten.
Recommended Posts