JWT #
JWT (JSON Web Token)はWebサービスにアクセスする際に送付するアクセストークンに使われるデータです。
JWTはヘッダ、ペイロード、シグネチャの3つの部分からなりまる。それぞれのフィールドはBASE64でエンコードされ、.で接続した文字列となります。
シグネチャがついているため、秘密鍵が分からない限りは、改変ができない仕組みになっています。
なお、ヘッダ、ペイロードともに暗号化はされていないので、base64デコードすれば中身は見ることができます。
Go #
ペイロードには"sub"(Subject)という項目に"user_name"を入れています。
JWTの生成にはgolang-jwtを使います。
package main
import (
"encoding/base64"
"encoding/hex"
"fmt"
"github.com/golang-jwt/jwt/v5"
"strings"
)
func main() {
// Create Token
sub_name := "user_name"
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{"sub": sub_name})
keyString := "e8ad309ce40f7c7a45e237c30b7b64aabd0a743edb88d08465e10512c13f2a0f"
key, err := hex.DecodeString(keyString)
if err != nil {
panic(err)
}
s, err := token.SignedString(key)
if err != nil {
panic(err)
}
fmt.Println(s)
for _, t := range strings.Split(s, ".")[:2] {
u, err := base64.RawURLEncoding.DecodeString(t)
if err != nil {
panic(err)
}
fmt.Println(string(u))
}
// Verify Token
token, err = jwt.Parse(s, func(token *jwt.Token) (interface{}, error) {
key, err := hex.DecodeString(keyString)
if err != nil {
panic(err)
}
return key, nil
})
if err != nil {
panic(err)
}
claims := token.Claims.(jwt.MapClaims)
username, err := claims.GetSubject()
if err == nil && token.Valid {
fmt.Printf("subject = %s\n", username)
}
}
Python #
PyJWTをpipでインストールしておきます。
pip install PyJWT
import jwt
import base64
key_str = "e8ad309ce40f7c7a45e237c30b7b64aabd0a743edb88d08465e10512c13f2a0f"
key = bytes.fromhex(key_str)
payload = {"sub" : "user_name"}
token = jwt.encode(payload, key=key, algorithm="HS256")
print(token)
for t in token.split(".")[:2]:
print(base64.urlsafe_b64decode(t + '==='))
payload = jwt.decode(token, key, algorithms=["HS256"])
username = payload.get("sub")
if username is not None:
print(f"subject = {username}")
Deno #
import { create, verify } from "https://deno.land/x/djwt/mod.ts";
const keyString = "e8ad309ce40f7c7a45e237c30b7b64aabd0a743edb88d08465e10512c13f2a0f";
const keyData = new Uint8Array(keyString.length / 2);
for (let i = 0; i < keyString.length / 2; i++) {
keyData[i] = parseInt(keyString.substring(i * 2, (i + 1) * 2), 16);
}
const key = await crypto.subtle.importKey(
"raw",
keyData,
{ name: "HMAC", hash: "SHA-256" },
true,
["sign", "verify"],
);
const jwt = await create({ alg: "HS256", typ: "JWT" }, { sub: "user_name" }, key);
console.log(jwt)
const payload = await verify(jwt, key);
console.log(payload)
Rust #
cargo add jsonwebtoken
cargo add serde
use serde::{Serialize, Deserialize};
use jsonwebtoken::{encode, decode, Header, Algorithm, Validation, EncodingKey, DecodingKey};
use std::num::ParseIntError;
use std::collections::HashSet;
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
sub: String,
}
fn main() {
let my_claims = Claims{sub:"user_name".to_string()};
let key_str = "e8ad309ce40f7c7a45e237c30b7b64aabd0a743edb88d08465e10512c13f2a0f";
let key = decode_hex(key_str).unwrap();
let token = encode(&Header::default(), &my_claims, &EncodingKey::from_secret(&key)).unwrap();
println!("{}", token);
let mut my_validation = Validation::new(Algorithm::HS256);
my_validation.required_spec_claims = HashSet::new();
let dec_claims = decode::<Claims>(&token, &DecodingKey::from_secret(&key), &my_validation).unwrap();
println!("{:?}", dec_claims);
}
fn decode_hex(s: &str) -> Result<Vec<u8>, ParseIntError> {
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16))
.collect()
}