Viết OpenID client cho JVM
Java
77
Scala
50
White

Ngoc Dao viết ngày 20/03/2016

Bài này nói về OpenID 2.0, có vẻ không còn được sử dụng rộng rãi nữa (ví dụ Google không còn hỗ trợ). Hiện tại, có thể bạn nên dùng OpenID Connect.

Nếu chưa biết OpenID là gì, xin đọc Wikipedia để biết ý tưởng mục đích chung v.v. Bài viết này ghi chú một số kinh nghiệm khi viết OpenID client cho JVM.

Nguyên lí

OpenID chỉ dùng cho web. Có 3 chủ thể tham gia. Xem sơ đồ trên, có 6 bước:

  1. Browser kết nối đến trang web của chúng ta. Trang web này đóng vai trò là OpenID client.
  2. OpenID client sẽ kết nối với trang web OpenID server để bắt tay trao đổi thông tin trước. Thông tin trả về từ OpenID server gồm có 2 phần X1 và X2. X1 là private chỉ OpenID server và client biết với nhau. X2 là public có thể cho browser biết. X1 sẽ được OpenID client lưu vào session của browser để đánh dấu.
  3. Trang web OpenID client redirect browser đến trang web OpenID server, kèm với X2, để nhờ server này authenticate giùm.
  4. User đăng nhập ở trang web OpenID server.
  5. Sau khi user đăng nhập thành công, trang web OpenID server sẽ redirect user trở lại trang web OpenID client, kèm với thông tin X3.
  6. OpenID client móc X1 từ session ra, kiểm tra X3 dựa trên X1 để chắc chắn đây là user mình đã redirect sang OpenID server, chứ không phải là hacker ở đâu ra tự nhiên chen vào.

Thư viện

Có 2 thư viện để viết OpenID client "xài được" là JOpenIDOpenID4Java. JOpenID gọn nhẹ hơn, OpenID4Java nặng đô hơn, nhưng có thêm tính năng cho phép viết OpenID server. Các thư viện này sẽ giúp chúng ta dễ dàng thực hiện các bước OpenID client phải làm như liệt kê ở trên.

openid.claimed_id và openid.identity

Tham khảo chuẩn OpenID 2.0hướng dẫn của Google.

Ở bước 3 và 5, OpenID client và server đều có thể chỉ định openid.claimed_id và openid.identity để bên này báo bên kia biết là mình muốn ID là gì.

Thư viện JOpenID phiên bản 1.08 hiện tại nhường server quyết định, không cho chương trình chúng ta chỉ định 2 tham số này. Nếu muốn chỉ định, trước khi redirect bạn có thể sửa lại 2 tham số này, đại loại như sau (viết bằng Scala):

...
val url        = manager.getAuthenticationUrl(endpoint, association)
val claimedUrl = url.replaceAll(
  URLEncoder.encode(
    "http://specs.openid.net/auth/2.0/identifier_select",
    "UTF-8"),
  URLEncoder.encode(openId, "UTF-8"))
  response.sendRedirect(claimedUrl)
...

Self-signed HTTPS OpenID server

Nếu bạn có OpenID server tự viết hoặc tự setup + server này dùng là HTTPS + HTTPS certificate là do bạn tự tạo chứ không bỏ tiền mua, thì OpenID client sẽ văng lỗi ở bước 2 khi kết nối đến OpenID server.

Nguyên nhân là JVM mặc định luôn kiểm tra certificate, gặp certificate bạn tự tạo, nó cho là certificate này là bậy bạ nên không chịu kết nối.

Có nhiều cách giải quyết. Ví dụ tắt hoàn toàn tính năng kiểm tra trên, hoặc cải tiến thêm chút để chỉ tắt khi kết nối đến HTTPS server của bạn. 2 cách trên không tốt lắm vì để lỗ hổng cho hacker.

Cách tốt hơn là làm cho JVM của OpenID client chịu xài certificate do bạn tự tạo. Cách này lại chia làm 2 cách nhỏ, một là cấu hình JRE (mọi JVM instance dùng JRE này đều bị ảnh hưởng), hai là chỉ cấu hình cho JVM instance của bạn (run time).

Trên MacOS X, tương đương với $JAVA_HOME/jre/lib/security trong bài viết hướng dẫn cấu hình JRE ở trên là /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home/lib/security.

Cách cấu hình JRE không phải lúc nào cũng có tác dụng, ví dụ khi chương trình là web servlet chạy trong Application Server hoành tránh như JBoss, GlassFish. Lúc này, phải dùng đến cách chỉ cấu hình cho JVM instance.

import java.io.FileInputStream
import java.security.KeyStore
import javax.net.ssl.{HttpsURLConnection, SSLContext, TrustManagerFactory}

def setJavaKeyStore(jks: String, passwordo: Option[String]) {
  // Must be null if password is not used
  val password = if (passwordo == None) null else passwordo.get.toCharArray

  val keyStore   = KeyStore.getInstance(KeyStore.getDefaultType)
  val trustStore = new FileInputStream(jks)
  keyStore.load(trustStore, password)
  trustStore.close

  val tmf = TrustManagerFactory.getInstance(
    TrustManagerFactory.getDefaultAlgorithm)
    tmf.init(keyStore)
  val ctx = SSLContext.getInstance("TLS")
  ctx.init(null, tmf.getTrustManagers, null)
  val sslFactory = ctx.getSocketFactory

  HttpsURLConnection.setDefaultSSLSocketFactory(sslFactory)
}

Trên đây là đoạn mã Scala. jks là đường dẫn đến tập tin jssecacerts trong bài viết hướng dẫn cấu hình JRE ở trên.

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Ngoc Dao

102 bài viết.
283 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
62 8
Làm thế nào để nâng cấp trang web mà không làm gián đoạn dịch vụ? Đây là câu hỏi phỏng vấn các công ty lớn thường hỏi khi bạn xin vào vị trí làm lậ...
Ngoc Dao viết hơn 2 năm trước
62 8
White
40 1
Bài viết này giải thích sự khác khác nhau giữa hai ngành khoa học máy tính (computer science) và kĩ thuật phần mềm (software engineering), hi vọng ...
Ngoc Dao viết hơn 2 năm trước
40 1
White
34 1
Nếu là team leader, giám đốc công ty hay tướng chỉ huy quân đội, vấn đề cơ bản bạn gặp phải là “hướng mọi người đi theo con đường bạn chỉ ra”. Thử...
Ngoc Dao viết hơn 2 năm trước
34 1
Bài viết liên quan
White
10 0
Kí tự Regex cơ bản Về cơ bản thì các sử lý matching của scala.util.matching.Regex sẽ được "phó thác" (delegate) cho java Regex. Bạn có thể tạo một ...
huydx viết 3 năm trước
10 0
White
7 1
Trong scala kí tự _ được dùng với khá nhiều mục đích .. không liên quan đến nhau. Tạm note lại cái đã khi nào có time sẽ quay lại viết cẩn thận sa...
huydx viết 3 năm trước
7 1
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
102 bài viết.
283 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!