Je ne sais pas combien de bières il s'agit, mais ceci est un article sur la façon d'utiliser OAuth. Il existe autant de bibliothèques clientes OAuth en Java qu'il y a d'étoiles, y compris celle de Google, mais ici je vais toucher OAuth sans utiliser la bibliothèque cliente pour l'étude. Je pense qu'il y a beaucoup de bons articles sur le mécanisme d'OAuth lui-même, donc je ne vais pas l'expliquer ici.
Using OAuth 2.0 for Web Server Applications OpenID Connect
OAuth
Vous devez enregistrer vos informations d'identification OAuth auprès de Google.
Créer des informations d'identification-> ID client OAuth-> Application Web
Le nom est approprié. Ajout de http: // localhost: 8080 / auth
à l'URI de redirection approuvé. La valeur ajoutée ici sera la valeur qui peut être utilisée comme destination de redirection de la demande.
Cette fois, nous ne l'utiliserons que pour le développement, spécifiez donc localhost. Il ne doit pas être utilisé en production.
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',
)
}
Tout va bien, mais cette fois, j'utiliserai Spring. ~~ Guava est utilisé pour Base64 et ~~ HttpClient est utilisé pour la simplification des requêtes HTTP. Utilisez Gson pour analyser JSON.
Addendum: Base64 est pris en charge à partir de Java 8 C'est complètement flou.
Créez un écran de connexion.
<!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
est le point de terminaison de l'API Google OAuth. Définissez les informations requises dans le paramètre de requête URL.
paramètre | |
---|---|
client_id |
Un identifiant qui identifie le client. Utilisez la valeur obtenue précédemment dans la console développeur. Ici, il est obtenu à partir de la variable d'environnement. |
redirect_uri |
La destination de la redirection. Utilisez la valeur définie dans la console développeur. |
access_type |
Cette fois, je vais utiliser un navigateur, donconline Spécifier. |
state |
state La valeur elle-même peut être définie librement. La plupartnonceIl est utilisé pour tels que. Cette fois, il s'agit simplement d'une valeur fixe. |
response_type |
Comment recevoir une réponse. Côté serveurcode C'est réparé. |
scope |
Spécifiez les informations que vous pouvez obtenir avec cette demande. iciemail etprofile Est réglé. Pour les valeurs disponiblesdocumentVoir. |
Lorsque l'utilisateur suit le lien généré ici, il sera redirigé vers l'écran de connexion Google. La première fois que vous autorisez l'application à accéder à vos informations utilisateur, une boîte de dialogue de confirmation s'affiche.
Lorsque l'utilisateur termine le processus de connexion, il sera redirigé vers redirect_url
. Vous pouvez demander un jeton d'accès en utilisant le code
inclus dans le paramètre de requête de l'URL.
@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 );
// ...
Récupérez le "code" et "l'état" à partir des paramètres de requête. state
vérifie que la valeur utilisée dans la requête précédente est renvoyée telle quelle. Notez que vous devrez peut-être décoder l'URL.
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 );
Envoyez une demande POST à l'URL pour l'acquisition de jetons. C'était la v2 il y a quelque temps, mais pour une raison quelconque, c'est la v4. Je m'en fiche parce que c'est selon le document officiel.
paramètre | |
---|---|
code |
Obtenu ci-dessuscode Est envoyé tel quel. |
client_id |
Identité du client. |
client_secret |
Secret du client. Comme l'ID, définissez celui spécifié dans la console développeur. |
redirect_uri |
Ce n'est pas une destination de redirection, mais pour vérification. Définissez dans la console et définissez à nouveau la destination de redirection utilisée dans la demande précédente. |
grant_type |
Valeur fixeauthorization_code 。 |
Si la demande est reconnue comme réussie, un jeton au format JWT est renvoyé.
Le jeton Web JSON est divisé en 3 parties, l'en-tête, le corps et la signature séparés par .
, alors obtenez chaque partie.
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 );
L'en-tête et les parties du corps sont en JSON encodé en Base64. Si vous ne vérifiez pas le résultat, vous pouvez obtenir la valeur avec juste cela, mais comme c'est un gros problème, vérifions également le jeton.
Obtenez la clé publique Google à utiliser pour la vérification. Obtenez l'emplacement de la clé à partir de https: // accounts.google.com / .well-known / 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();
Envoyez une requête GET à cet URI pour obtenir le JSON.
var keyResponse = httpGet( jwks ).parseAsString();
var keysResponseObj = parseJsonAs( keyResponse, Keys.class );
return keysResponseObj.keys.stream()
.filter( k -> k.kid.equals( kid ) )
.findAny()
.orElseThrow();
Plusieurs clés de chiffrement sont stockées dans le tableau «keys». Recherchez un kid
qui correspond à kid
dans l'en-tête du jeton d'identification.
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 ) );
Convertissez la clé JSON au format Java et assurez-vous qu'elle est bien connue. En fait, le format de clé doit également être obtenu à partir de JSON, mais ici c'est une valeur fixe de RSA
.
Après avoir confirmé la signature, l'étape suivante consiste à vérifier les paramètres.
checkState( Set.of( "https://accounts.google.com", "accounts.google.com" ).contains( body.get( "iss" ) ) );
Émetteur, assurez-vous que «non» est légitime. Pour Google, il s'agit de l'un des paramètres suivants: https: // accounts.google.com, ʻaccounts.google.com`.
checkState( Objects.equals( body.get( "aud" ), clientId ) );
Assurez-vous que l'audience, ʻaud`, correspond à l'ID client.
var now = System.currentTimeMillis();
checkState( now <= Long.parseLong( body.get( "exp" ) ) * 1000 + 1000 );
checkState( now >= Long.parseLong( body.get( "iat" ) ) * 1000 - 1000 );
Enfin, assurez-vous que la clé est dans la date d'expiration et que l'heure de signature est valide. La valeur est en secondes. La raison de l'ajout et de la soustraction de 1000 est d'absorber le décalage avec le serveur.
Maintenant que nous avons vérifié que le jeton d'identification est valide, nous obtenons la valeur. Ici, l'adresse e-mail, le nom et l'URL de l'image du visage sont acquis et définis dans le modèle.
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>
Pour utiliser les différentes API de Google, utilisez ʻaccess_token et
refresh_token` inclus dans JWT pour envoyer chaque requête.
Allez dans localhost: 8080 / login
.
Allez sur Google et authentifiez-vous.
J'ai eu l'image du visage, l'adresse e-mail et le nom.
Recommended Posts