Làm cách nào để xác thực chữ ký JWT?

OAuth 2. 0 để lại thiết kế mã thông báo truy cập về mặt mã hóa và xác thực cho người triển khai. Chúng có thể được đúc dưới dạng Mã thông báo web JSON [JWT]

Ví dụ: máy chủ Connect2id có thể đúc mã thông báo truy cập là JWT có chữ ký RSA. Chúng có thể được xác thực nhanh chóng và hiệu quả bằng khóa chung cho JWT. Xác thực mật mã có thể nhanh hơn và linh hoạt hơn so với việc có quyền truy cập dựa trên mã định danh mà máy chủ tài nguyên kiểm tra bằng cách thực hiện truy vấn mạng tới OAuth 2. 0 để truy xuất thông tin ủy quyền được liên kết

Khung xác thực JWT

Thư viện Nimbus JOSE+JWT bao gồm một khung đơn giản để thực hiện các bước cần thiết nhằm xác thực một JWT

Các bước này là gì?

  1. Phân tích cú pháp JWT -- Chuỗi mã thông báo truy cập được phân tích cú pháp dưới dạng JWT
  2. Kiểm tra loại -- Kiểm tra tham số tiêu đề "typ" [loại] cho biết loại hoặc cách sử dụng JWT. Máy chủ Connect2id đặt nó thành "at+jwt" cho mã truy cập
  3. Kiểm tra thuật toán -- Thuật toán JWS được chỉ định trong tiêu đề JWT được kiểm tra xem nó có khớp với thuật toán đã thỏa thuận/dự kiến ​​hay không [e. g. RS256 cho chữ ký RSA PKCS #1 với SHA-256]. Nếu nhận được mã thông báo có thuật toán không mong muốn, mã thông báo đó sẽ bị từ chối. Điều này ngăn việc hạ cấp và các cuộc tấn công khác có thể xảy ra nếu mã thông báo có bất kỳ thuật toán JOSE nào được chấp nhận
  4. Kiểm tra chữ ký -- Chữ ký số được xác minh bằng cách thử một khóa chung thích hợp từ máy chủ JWK đã đặt. Khóa đã sử dụng thường được xác định bằng tham số tiêu đề "kid" [ID khóa]
  5. Kiểm tra xác nhận quyền sở hữu JWT -- Bộ xác nhận quyền sở hữu JWT được xác thực, chẳng hạn như để đảm bảo mã thông báo không hết hạn và khớp với nhà phát hành, đối tượng dự kiến ​​và các xác nhận quyền sở hữu khác

Nếu bất kỳ kiểm tra nào trong số này không thành công thì mã thông báo được coi là không hợp lệ và yêu cầu phải bị từ chối

Dưới đây là mã Java mẫu để thiết lập Trình xử lý có thể định cấu hìnhJWTProcessor lấy các khóa RSA công khai cần thiết từ tài liệu JSON do máy chủ Connect2id xuất bản [yêu cầu Nimbus JOSE+JWT v8. 2+]

import com.nimbusds.jose.*;
import com.nimbusds.jose.jwk.source.*;
import com.nimbusds.jwt.*;
import com.nimbusds.jwt.proc.*;

// The access token to validate, typically submitted with a HTTP header like
// Authorization: Bearer eyJraWQiOiJDWHVwIiwidHlwIjoiYXQrand0IiwiYWxnIjoi...
String accessToken =
    "eyJraWQiOiJDWHVwIiwidHlwIjoiYXQrand0IiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJib2IiLCJzY" +
    "3AiOlsib3BlbmlkIiwiZW1haWwiXSwiY2xtIjpbIiFCZyJdLCJpc3MiOiJodHRwczpcL1wvZGVtby5jM" +
    "mlkLmNvbVwvYzJpZCIsImV4cCI6MTU3MTMxMjAxOCwiaWF0IjoxNTcxMzExNDE4LCJ1aXAiOnsiZ3Jvd" +
    "XBzIjpbImFkbWluIiwiYXVkaXQiXX0sImp0aSI6ImJBT1BiNWh5TW80IiwiY2lkIjoiMDAwMTIzIn0.Q" +
    "hTAdJK8AbdJJhQarjOz_qvAINQeWJCIYSROVaeRpBfaOrTCUy5gWRf8xrpj1DMibdHwQGPdht3chlAC8" +
    "LGbAorEu0tLLcOwKl4Ql-o30Tdd5QhjNb6PndOY89NbQ1O6cdOZhvV4XB-jUAXi3nDgCw3zvIn2348Va" +
    "2fOAzxUvRs2OGsEDl5d9cmL3e68YqSh7ss12y9oBDyEyz8Py7dtXgt6Tg67n9WlEBG0r4KloGDBdbCCZ" +
    "hlEyURkHaE-3nUcjwd-CEVeqWPO0bsLhwto-80j8BtsfD649GnvaMb9YdbdYhTTs-MkRUQpQIZT0s9oK" +
    "uzKayvZhk0c_0FoSeW7rw";

// Create a JWT processor for the access tokens
ConfigurableJWTProcessor jwtProcessor =
    new DefaultJWTProcessor[];

// Set the required "typ" header "at+jwt" for access tokens issued by the
// Connect2id server, may not be set by other servers
jwtProcessor.setJWSTypeVerifier[
    new DefaultJOSEObjectTypeVerifier[new JOSEObjectType["at+jwt"]]];

// The public RSA keys to validate the signatures will be sourced from the
// OAuth 2.0 server's JWK set, published at a well-known URL. The RemoteJWKSet
// object caches the retrieved keys to speed up subsequent look-ups and can
// also handle key-rollover
JWKSource keySource =
    new RemoteJWKSet[new URL["//demo.c2id.com/jwks.json"]];

// The expected JWS algorithm of the access tokens [agreed out-of-band]
JWSAlgorithm expectedJWSAlg = JWSAlgorithm.RS256;

// Configure the JWT processor with a key selector to feed matching public
// RSA keys sourced from the JWK set URL
JWSKeySelector keySelector =
    new JWSVerificationKeySelector[expectedJWSAlg, keySource];

jwtProcessor.setJWSKeySelector[keySelector];

// Set the required JWT claims for access tokens issued by the Connect2id
// server, may differ with other servers
jwtProcessor.setJWTClaimsSetVerifier[new DefaultJWTClaimsVerifier[
    new JWTClaimsSet.Builder[].issuer["//demo.c2id.com"].build[],
    new HashSet[Arrays.asList["sub", "iat", "exp", "scp", "cid", "jti"]]]];

// Process the token
SecurityContext ctx = null; // optional context parameter, not required here
JWTClaimsSet claimsSet = jwtProcessor.process[accessToken, ctx];

// Print out the token claims set
System.out.println[claimsSet.toJSONObject[]];

Bộ xác nhận quyền sở hữu mã thông báo truy cập đã xác thực được in

{
  "sub" : "alice",
  "cid" : "000123",
  "iss" : "//demo.c2id.com",
  "exp" : 1460345736,
  "scp" : ["openid","email","profile"],
  "clm" : ["!5v8H"],
  "uip" : {"groups":["admin","audit"]}
}

Mã thông báo được mã hóa

Khung xử lý JWT cũng có thể xử lý các mã thông báo được mã hóa sau khi ký [hoặc chỉ được mã hóa]. Để làm được điều đó, bộ xử lý JWT phải được định cấu hình với bộ chọn thích hợp cho các khóa giải mã JWE

Thiết lập ví dụ để xử lý mã thông báo được mã hóa trực tiếp bằng khóa AES dùng chung

// The AES key, obtained from a Java keystore, etc.
SecretKey secretKey = null;

// The expected JWE algorithm and method
JWEAlgorithm expectedJWEAlg = JWEAlgorithm.DIR;
EncryptionMethod expectedJWEEnc = EncryptionMethod.A128GCM;

// The JWE key source
JWKSource jweKeySource = new ImmutableSecret[secretKey];

// Configure a key selector to handle the decryption phase
JWEKeySelector jweKeySelector =
    new JWEDecryptionKeySelector[expectedJWEAlg, expectedJWEEnc, jweKeySource];

jwtProcessor.setJWEKeySelector[jweKeySelector];

JWK đặt thời gian chờ của URL

Hàm tạo RemoteJWKSet cơ bản sẽ tạo một trình truy xuất tài nguyên HTTP nội bộ với thời gian chờ đọc và kết nối HTTP mặc định cũng như giới hạn kích thước thực thể HTTP mặc định

Có thể ghi đè các giá trị mặc định này theo hai cách

  • Ví dụ: bằng cách chuyển một ResourceRetriever đã định cấu hình

    int httpConnectTimeoutMs = 5_000;
    int httpReadTimeoutMs = 5_000;
    int httpSizeLimitBytes = 100_000;
    
    JWKSource jwkSource = new RemoteJWKSet[
            new URL["//demo.c2id.com/jwks.json"],
            new DefaultResourceRetriever[
                httpConnectTimeoutMs, httpReadTimeoutMs, httpSizeLimitBytes
            ]
        ];
    
  • Bằng cách đặt các thuộc tính hệ thống Java sau [thích hợp khi không có cách trực tiếp để tạo RemoteJWKSet, có thể xảy ra trong các khung sử dụng thư viện này nội bộ]

    Đặt thời gian chờ kết nối HTTP là 5 giây

    com.nimbusds.jose.jwk.source.RemoteJWKSet.defaultHttpConnectTimeout=5000
    

    Đặt thời gian chờ đọc HTTP là 2. 5 giây

    com.nimbusds.jose.jwk.source.RemoteJWKSet.defaultHttpReadTimeout=2500
    

    Đặt giới hạn kích thước thực thể HTTP là 100 kilobyte

    com.nimbusds.jose.jwk.source.RemoteJWKSet.defaultHttpSizeLimit=100_000
    

    Các thuộc tính hệ thống Java này được hỗ trợ kể từ phiên bản 9. 16

Bộ chuyển đổi dự phòng JWK đã đặt URL

Định cấu hình URL đặt JWK chuyển đổi dự phòng thứ hai cho RemoteJWKSet trong trường hợp lần đầu tiên của chúng tôi hoặc trở nên không khả dụng [e. g. với HTTP 404 hoặc HTTP 500] thật dễ dàng

URL failoverURL = new URL["//backup.c2id.com/jwks.json"];
JWKSource failoverJWKSource = new RemoteJWKSet[failoverURL];

URL mainURL = new URL["//c2id.com/jwks.json"];
JWKSource jwkSource = new RemoteJWKSet[mailURL, failoverJWKSetSource];

Chuyển đổi dự phòng dựa trên giao diện JWKSource, giao diện này cho phép triển khai các chiến lược tùy ý để khắc phục lỗi mạng và máy chủ lưu trữ

Tính năng này đã có trong phiên bản 9. 17

Cách cắm nguồn JWK thay thế

Ví dụ trên đã sử dụng URL được đặt JWK để cung cấp bộ chọn khóa. Các loại nguồn chính khác được hộp hỗ trợ

  • ImmutableJWKSet -- để chỉ định trực tiếp một nhóm ứng viên JWK theo giá trị

    ________số 8
  • ImmutableSecret -- để chỉ định một JWK đơn lẻ là khóa đối xứng;

    import java.security.SecureRandom;
    import com.nimbusds.jose.jwk.source.ImmutableSecret;
    
    // Generate secret
    byte[] secret = new byte[32];
    new SecureRandom[].nextBytes[secret];
    
    // Create JWK source backed by a singleton secret key
    JWKSource keySource = new ImmutableSecret[secret];
    

Bạn có thể triển khai JWKSource tùy chỉnh của riêng mình, chẳng hạn như dựa trên kho khóa Java hoặc cơ sở dữ liệu nào đó

Xác thực yêu cầu JWT

Có thể định cấu hình DefaultJWTClaimsVerifier để thực hiện tất cả các kiểm tra cần thiết nhằm xác định xem các xác nhận quyền sở hữu JWT có hợp pháp hay không

  • Đối tượng dự kiến ​​-- Nếu JWT có đối tượng [

    {
      "sub" : "alice",
      "cid" : "000123",
      "iss" : "//demo.c2id.com",
      "exp" : 1460345736,
      "scp" : ["openid","email","profile"],
      "clm" : ["!5v8H"],
      "uip" : {"groups":["admin","audit"]}
    }
    
    1] [được đề xuất] thì nó bao gồm mã định danh cho máy chủ tài nguyên

  • Xác nhận đối sánh chính xác -- Xác nhận quyền sở hữu JWT phải có trong JWT và giá trị của chúng phải khớp chính xác. Ví dụ: công ty phát hành [

    {
      "sub" : "alice",
      "cid" : "000123",
      "iss" : "//demo.c2id.com",
      "exp" : 1460345736,
      "scp" : ["openid","email","profile"],
      "clm" : ["!5v8H"],
      "uip" : {"groups":["admin","audit"]}
    }
    
    2]

  • Yêu cầu bắt buộc -- Tên của yêu cầu phải có trong JWT. Ví dụ, chúng có thể bao gồm thời gian hết hạn [

    {
      "sub" : "alice",
      "cid" : "000123",
      "iss" : "//demo.c2id.com",
      "exp" : 1460345736,
      "scp" : ["openid","email","profile"],
      "clm" : ["!5v8H"],
      "uip" : {"groups":["admin","audit"]}
    }
    
    3], chủ đề [
    {
      "sub" : "alice",
      "cid" : "000123",
      "iss" : "//demo.c2id.com",
      "exp" : 1460345736,
      "scp" : ["openid","email","profile"],
      "clm" : ["!5v8H"],
      "uip" : {"groups":["admin","audit"]}
    }
    
    4], ứng dụng khách [
    {
      "sub" : "alice",
      "cid" : "000123",
      "iss" : "//demo.c2id.com",
      "exp" : 1460345736,
      "scp" : ["openid","email","profile"],
      "clm" : ["!5v8H"],
      "uip" : {"groups":["admin","audit"]}
    }
    
    5] và phạm vi [
    {
      "sub" : "alice",
      "cid" : "000123",
      "iss" : "//demo.c2id.com",
      "exp" : 1460345736,
      "scp" : ["openid","email","profile"],
      "clm" : ["!5v8H"],
      "uip" : {"groups":["admin","audit"]}
    }
    
    6]. Danh sách này tự động bao gồm tên của đối tượng dự kiến ​​[nếu được đặt] và tất cả các xác nhận quyền sở hữu đối sánh chính xác

  • Tuyên bố bị cấm -- Danh sách tên của các tuyên bố JWT không được có mặt. Danh sách này có thể được sử dụng để ngăn chặn việc chuyển một loại JWT khác một cách tình cờ hoặc cố ý, đặc biệt là tiêu đề loại [loại] không được sử dụng để truyền tải thông tin đó

    Có ai có thể xác thực JWT không?

    Nó bao gồm các đối tượng JSON, được mã hóa base64url và nối với nhau thành một chuỗi được phân tách bằng dấu chấm. Bất kỳ ai sở hữu JWT đều có thể giải mã nó và xem nội dung .

    Bạn có thể xác minh JWT bằng khóa chung không?

    Bây giờ, bạn có thể xác minh mã thông báo bằng khóa chung . Sử dụng mẫu mã bên dưới để xác thực JWT và lấy thông tin của người dùng từ mã thông báo. Sau khi xác thực người dùng, bạn nhận được một đối tượng người dùng chứa thông tin về người dùng.

Chủ Đề