Panduan Untuk Perkhidmatan Pengesahan Dan Pengesahan Java (JAAS)

Java Teratas

Saya baru sahaja mengumumkan kursus Learn Spring yang baru , yang berfokus pada asas-asas Spring 5 dan Spring Boot 2:

>> SEMAK KURSUS

1. Gambaran keseluruhan

Java Authentication And Authorization Service (JAAS) adalah kerangka keselamatan tingkat rendah Java SE yang menambah model keselamatan dari keamanan berdasarkan kod ke keamanan berbasis pengguna . Kami boleh menggunakan JAAS untuk dua tujuan:

  • Pengesahan: Mengenal pasti entiti yang sedang menjalankan kod
  • Kebenaran: Setelah disahkan, pastikan entiti ini mempunyai hak atau kebenaran kawalan akses yang diperlukan untuk melaksanakan kod sensitif

Dalam tutorial ini, kita akan membahas cara menyiapkan JAAS dalam aplikasi contoh dengan menerapkan dan mengkonfigurasi berbagai APInya, terutama LoginModule .

2. Bagaimana JAAS Berfungsi

Semasa menggunakan JAAS dalam aplikasi, beberapa API terlibat:

  • CallbackHandler : Digunakan untuk mengumpulkan kelayakan pengguna dan disediakan secara pilihan semasa membuat LoginContext
  • Konfigurasi : Bertanggungjawab untuk memuatkan implementasi LoginModule dan boleh diberikan secara pilihan semasa pembuatan LoginContext
  • LoginModule : Digunakan secara berkesan untuk mengesahkan pengguna

Kami akan menggunakan implementasi lalai untuk Configuration API dan menyediakan implementasi kami sendiri untuk CallbackHandler dan LoginModule API.

3. Menyediakan Pelaksanaan CallbackHandler

Sebelum menggali implementasi LoginModule , pertama-tama kita perlu menyediakan implementasi untuk antara muka CallbackHandler , yang digunakan untuk mengumpulkan bukti pengguna .

Ini memiliki satu metode, menangani () , yang menerima pelbagai Callback s. Sebagai tambahan, JAAS sudah menyediakan banyak implementasi Callback , dan kami akan menggunakan NameCallback dan PasswordCallback untuk mengumpulkan nama pengguna dan kata laluan.

Mari lihat pelaksanaan antara muka CallbackHandler kami :

public class ConsoleCallbackHandler implements CallbackHandler { @Override public void handle(Callback[] callbacks) throws UnsupportedCallbackException { Console console = System.console(); for (Callback callback : callbacks) { if (callback instanceof NameCallback) { NameCallback nameCallback = (NameCallback) callback; nameCallback.setName(console.readLine(nameCallback.getPrompt())); } else if (callback instanceof PasswordCallback) { PasswordCallback passwordCallback = (PasswordCallback) callback; passwordCallback.setPassword(console.readPassword(passwordCallback.getPrompt())); } else { throw new UnsupportedCallbackException(callback); } } } }

Oleh itu, untuk meminta dan membaca nama pengguna, kami telah menggunakan:

NameCallback nameCallback = (NameCallback) callback; nameCallback.setName(console.readLine(nameCallback.getPrompt()));

Begitu juga, untuk meminta dan membaca kata laluan:

PasswordCallback passwordCallback = (PasswordCallback) callback; passwordCallback.setPassword(console.readPassword(passwordCallback.getPrompt()));

Kemudian, kita akan melihat cara memanggil CallbackHandler semasa melaksanakan LoginModule .

4. Menyediakan Pelaksanaan LoginModule

Untuk kesederhanaan, kami akan menyediakan pelaksanaan yang menyimpan pengguna berkod keras. Oleh itu, mari kita panggilnya InMemoryLoginModule :

public class InMemoryLoginModule implements LoginModule { private static final String USERNAME = "testuser"; private static final String PASSWORD = "testpassword"; private Subject subject; private CallbackHandler callbackHandler; private Map sharedState; private Map options; private boolean loginSucceeded = false; private Principal userPrincipal; //... }

Pada subseksyen berikutnya, kami akan memberikan implementasi untuk kaedah yang lebih penting: inisialisasi () , login () , dan komit () .

4.1. memulakan ()

The LoginModule mula-mula dimuatkan dan kemudian dimulakan dengan Subjek dan CallbackHandler . Selain itu, LoginModule dapat menggunakan Peta untuk berkongsi data di antara mereka, dan Peta lain untuk menyimpan data konfigurasi peribadi:

public void initialize( Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { this.subject = subject; this.callbackHandler = callbackHandler; this.sharedState = sharedState; this.options = options; }

4.2. log masuk()

Dalam kaedah login () , kami menggunakan kaedah CallbackHandler.handle () dengan NameCallback dan PasswordCallback untuk meminta dan mendapatkan nama pengguna dan kata laluan. Kemudian, kami membandingkan kelayakan yang diberikan ini dengan kod keras:

@Override public boolean login() throws LoginException { NameCallback nameCallback = new NameCallback("username: "); PasswordCallback passwordCallback = new PasswordCallback("password: ", false); try { callbackHandler.handle(new Callback[]{nameCallback, passwordCallback}); String username = nameCallback.getName(); String password = new String(passwordCallback.getPassword()); if (USERNAME.equals(username) && PASSWORD.equals(password)) { loginSucceeded = true; } } catch (IOException | UnsupportedCallbackException e) { //... } return loginSucceeded; }

Kaedah log masuk () harus kembali benar untuk operasi yang berjaya dan salah untuk log masuk yang gagal .

4.3. melakukan ()

Sekiranya semua panggilan ke LoginModule # masuk berjaya, kami mengemas kini Subjek dengan Pengetua tambahan :

@Override public boolean commit() throws LoginException { if (!loginSucceeded) { return false; } userPrincipal = new UserPrincipal(username); subject.getPrincipals().add(userPrincipal); return true; }

Jika tidak, kaedah pengguguran () dipanggil.

Pada tahap ini, implementasi LoginModule kami sudah siap dan perlu dikonfigurasi sehingga dapat dimuat secara dinamis menggunakan penyedia perkhidmatan Konfigurasi .

5. Konfigurasi LoginModule

JAAS menggunakan penyedia perkhidmatan Konfigurasi untuk memuat LoginModule s semasa waktu berjalan. Secara lalai, ia menyediakan dan menggunakan ConfigFile pelaksanaan mana LoginModule s dikonfigurasikan melalui fail log masuk. Sebagai contoh, berikut adalah kandungan fail yang digunakan untuk LoginModule kami :

jaasApplication { com.baeldung.jaas.loginmodule.InMemoryLoginModule required debug=true; };

Seperti yang kita lihat, kami telah memberikan nama kelas yang memenuhi syarat pelaksanaan LoginModule , bendera yang diperlukan , dan pilihan untuk debug.

Akhirnya, ambil perhatian bahawa kami juga dapat menentukan fail masuk melalui harta sistem java.security.auth.login.config :

$ java -Djava.security.auth.login.config=src/main/resources/jaas/jaas.login.config

Kami juga dapat menentukan satu atau lebih fail masuk melalui properti login.config.url dalam fail keselamatan Java, $ {java.home} /jre/lib/security/java.security :

login.config.url.1=file:${user.home}/.java.login.config

6. Pengesahan

Pertama, aplikasi memulakan proses pengesahan dengan membuat instance LoginContext . Untuk melakukannya, kita dapat melihat konstruktor penuh untuk mempunyai idea tentang apa yang kita perlukan sebagai parameter:

LoginContext(String name, Subject subject, CallbackHandler callbackHandler, Configuration config)
  • nama : digunakan sebagai indeks untuk memuatkan hanya yang sepadan LoginModule s
  • subjek : mewakili pengguna atau perkhidmatan yang ingin log masuk
  • callbackHandler : bertanggungjawab untuk menyampaikan kelayakan pengguna dari aplikasi ke LoginModule
  • config : bertanggungjawab memuat LoginModule s yang sesuai dengan parameter nama

Di sini, kami akan menggunakan konstruktor yang terlalu banyak di mana kami akan menyediakan pelaksanaan CallbackHandler kami :

LoginContext(String name, CallbackHandler callbackHandler)

Sekarang kita mempunyai CallbackHandler dan LoginModule yang dikonfigurasi , kita dapat memulakan proses pengesahan dengan menginisialisasi objek LoginContext :

LoginContext loginContext = new LoginContext("jaasApplication", new ConsoleCallbackHandler());

At this point, we can invoke the login() method to authenticate the user:

loginContext.login();

The login() method, in turn, creates a new instance of our LoginModule and calls its login() method. And, upon successful authentication, we can retrieve the authenticated Subject:

Subject subject = loginContext.getSubject();

Now, let's run a sample application that has the LoginModule wired in:

$ mvn clean package $ java -Djava.security.auth.login.config=src/main/resources/jaas/jaas.login.config \ -classpath target/core-java-security-2-0.1.0-SNAPSHOT.jar com.baeldung.jaas.JaasAuthentication

When we're prompted to provide the username and password, we'll use testuser and testpassword as credentials.

7. Authorization

Authorization comes into play when the user is first connected and associated with the AccessControlContext. Using the Java security policy, we can grant one or more access control rights to Principals. We can then prevent access to sensitive code by calling the SecurityManager#checkPermission method:

SecurityManager.checkPermission(Permission perm)

7.1. Defining Permissions

An access control right or permission is the ability to execute an action on a resource. We can implement a permission by subclassing the Permission abstract class. To do so, we need to provide a resource name and a set of possible actions. For example, we can use FilePermission to configure access control rights on files. Possible actions are read, write, execute, and so on. For scenarios where actions are not necessary, we may simply use the BasicPermision.

Next, we'll provide an implementation of permission through the ResourcePermission class where users may have permission to access a resource:

public final class ResourcePermission extends BasicPermission { public ResourcePermission(String name) { super(name); } }

Later, we'll configure an entry for this permission through the Java security policy.

7.2. Granting Permissions

Usually, we don't need to know the policy file syntax because we can always use the Policy Tool to create one. Let's take a look at our policy file:

grant principal com.sun.security.auth.UserPrincipal testuser { permission com.baeldung.jaas.ResourcePermission "test_resource" };

In this sample, we've granted the test_resource permission to the testuser user.

7.3. Checking Permissions

Once the Subject is authenticated and permissions are configured, we can check for access by calling the Subject#doAs or Subject#doAsPrivilieged static methods. For this purpose, we'll provide a PrivilegedAction where we can protect access to sensitive code. In the run() method, we call the SecurityManager#checkPermission method to ensure that the authenticated user has the test_resource permission:

public class ResourceAction implements PrivilegedAction { @Override public Object run() { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new ResourcePermission("test_resource")); } System.out.println("I have access to test_resource !"); return null; } }

The last thing is to call the Subject#doAsPrivileged method:

Subject subject = loginContext.getSubject(); PrivilegedAction privilegedAction = new ResourceAction(); Subject.doAsPrivileged(subject, privilegedAction, null);

Seperti pengesahan, kami akan menjalankan aplikasi mudah untuk kebenaran di mana, selain LoginModule , kami menyediakan fail konfigurasi kebenaran:

$ mvn clean package $ java -Djava.security.manager -Djava.security.policy=src/main/resources/jaas/jaas.policy \ -Djava.security.auth.login.config=src/main/resources/jaas/jaas.login.config \ -classpath target/core-java-security-2-0.1.0-SNAPSHOT.jar com.baeldung.jaas.JaasAuthorization

8. Kesimpulannya

Dalam artikel ini, kami telah menunjukkan cara menerapkan JAAS dengan menjelajahi kelas dan antarmuka utama dan menunjukkan cara mengkonfigurasinya. Terutama, kami telah melaksanakan LoginModule penyedia perkhidmatan .

Seperti biasa, kod dalam artikel ini terdapat di GitHub.

Bahagian bawah Java

Saya baru sahaja mengumumkan kursus Learn Spring yang baru , yang berfokus pada asas-asas Spring 5 dan Spring Boot 2:

>> SEMAK KURSUS