Аутентифікація користувачів на Arduino з RFID

ArduinoВведення
попередній статті я тільки починав працювати з Arduino, в результаті чого закономірно вийшла метеостанція. У цій статті підемо далі — будемо робити аутентифікацію за допомогою RFID карт і Arduino в додатку InterSystems Caché.

Передача аутентифікації
У Caché є механізм делегування аутентификації — передачі процесу аутентифікації користувача кодом. Щоб його включити, треба зробити наступне:

  1. Написати код аутентифікації користувачів в рутині ZAUTHENTICATE. В неї є 4 точки входу: отримання логіна/пароля, їх перевірка та призначення прав, зміна пароля, формування сертифіката. Докладніше про це нижче.

  2. Увімкнути передачу аутентифікації в Caché (SMP → System Administration → Security → System Security → Authentication/CSP Session Options, встановіть прапорець Allow Delegated authentication і збережіть налаштування).

  3. Увімкнути передачу для аутентифікації необхідних сервісів (SMP → Menu → Manage Services → Сервіс → Allowed Authentication Methods → вибрати Delegated → Save) та/або додатків (SMP → Menu → Manage Web Applications → Додаток → Allowed Authentication Methods → вибрати Delegated → Save).
Як це працює
Ось що відбувається, коли користувач автентифікований в сервісі або веб-додатку, для якого включена передача аутентифікації:

  1. Викликається рутина ZAUTHENTICATE. Код цієї рутини пишеться користувачем і може бути будь-яким Caché ObjectScript кодом, в тому числі і $ZF виклики.

  2. Наступний крок залежить від того, чи був успішним виклик ZAUTHENTICATE:

    • Якщо виклик ZAUTHENTICATE успішний і це перший раз, коли користувач автентифікований з ZAUTHENTICATE, то для нього складається запис користувача типу «Delegated user». Якщо ZAUTHENTICATE призначає користувачеві права або інші властивості, вони стають відповідними властивостями.

    • Якщо виклик ZAUTHENTICATE успішний і це не перший раз, коли користувач автентифікований з ZAUTHENTICATE, то його запис користувача оновлюється.


    • Якщо виклик ZAUTHENTICATE не успішний, користувачеві видається помилка доступу.

  3. Якщо для инстанса і сервісу включена двофакторна аутентифікація, то проводиться пошук номера телефону та оператора. Якщо вони задані, відбувається двофакторна аутентифікація, якщо немає — юзер не автентифікований.

  4. Делегований користувач відображається в таблиці користувачів.
Звідки користувачі?
Є два методи аутентифікації в залежності від того, які способи аутентифікації включені для програми/сервісу:

  • Delegated — ім'я/пароль беруться з GetCredentials, перевіряються засобами ZAUTHENTICATE (тип користувача — делегований).

  • Delegated і Password — ім'я/пароль беруться з GetCredentials, перевіряються стандартними механізмами Caché (тип користувача — Caché).
Тепер перейдемо до розгляду рутини ZAUTHENTICATE і її точок входу.

ZAUTHENTICATE
Ця основна рутина, що містить 4 точки входу.

▍GetCredentials

Ця точка входу викликається в тому випадку, коли передача аутентифікації включена для сервісу, і вона викликається замість запиту логіна/пароля користувача. Код цієї рутини встановлює логін і пароль (будь-яким способом). Згодом (поза цієї рутини) отримані логін і пароль аутентифицируются, ніби користувач ввів як зазвичай. Метод отримання логіна та пароля може бути будь — введення з клавіатури, API, зчитування зовнішнього пристрою — у цій статті будемо використовувати аутентифікацію за допомогою RFID карти.

Дана точка входу повертає статус, і якщо це помилка, то вона буде записана в аудит, а спроба аутентифікації буде відхилена. Виняток — помилка $SYSTEM.Status.Error($$$GetCredentialsFailed), в такому випадку користувачеві запропонують ввести логін/пароль стандартним методом Caché. Сигнатура наступна:

GetCredentials(ServiceName, Namespace, Username, Password, Credentials) Public { }

Де:

  • ServiceName — назва сервісу, через який йде підключення
  • Namespace — область, якщо вказана при підключенні
  • Username — ім'я користувача
  • Password — пароль
  • Credentials — в даний час не використовується
Зазначу важливу особливість цієї точки входу. Якщо для сервісу/програми включена і передача аутентифікації і звичайна аутентифікація по парі логін/пароль (Password Authentication), то логін і пароль, отримані через GetCredentials, будуть використані для стандартної аутентифікації по паролю.

▍ZAUTHENTICATE

У разі якщо первісна аутентифікація успішна, ZAUTHENTICATE встановлює ролі і інші властивості користувача. У разі якщо це не перша аутентифікація, властивості можуть бути змінені. Для цього в коді рутини встановлюються властивості масиву Properties. Сигнатура:

ZAUTHENTICATE(ServiceName, Namespace, Username, Password, Credentials, Properties) Public { }

Масив Properties:

  • Properties("Comment") — коментар
  • Properties("FullName") — ім'я та прізвище
  • Properties("NameSpace") — стартова область
  • Properties("Roles") — список ролей через кому
  • Properties("Routine") — стартова рутина
  • Properties("Password") — пароль
  • Properties("Username") — ім'я користувача
  • Properties("PhoneNumber") — телефонний номер користувача
  • Properties("PhoneProvider") — оператор телефону
  • Properties("AutheEnabled") — включити стандартну двофакторну аутентифікацію (для цього треба встановити значення, рівне $$$AutheTwoFactorSMS)

▍ChangePassword

Точка входу для зміни пароля користувача. Сигнатура наступна:

ChangePassword(Username, NewPassword, OldPassword, Status) Public { }

Де:

  • NewPassword — новий пароль
  • OldPassword — старий пароль
  • Status — результат операції зміни паролю

▍SendTwoFactorToken

Для використання в стандартній двофакторної аутентифікації. Визначає формат запиту і токена аутентифікації. Сигнатура:

SendTwoFactorToken(Username, ServiceName,Namespace,Application,Credentials,SecurityToken,TwoFactorTimeout,UserPhoneNumber) Public { }

Де:

  • Application — CSP додаток або рутина, до якої підключається користувач
  • SecurityToken — маркер, який буде надіслано користувачеві
  • TwoFactorTimeout — час дії сертифіката
  • UserPhoneNumber — телефонний номер користувача
Приклад
Для початку покажу найпростіший приклад для терміналу Caché в Windows — сервісу %Service_Console, який буде питати логін і пароль користувача. Включимо передачу аутентифікації в системі для цього сервісу. Після цього напишемо рутину ZAUTHENTICATE (у %SYS):

ZAUTHENTICATE(ServiceName, Namespace, Username, Password, Credentials, Properties) PUBLIC {
#Iinclude %occErrors
#Include %occStatus
Quit $$$OK
}

GetCredentials(ServiceName, Namespace, Username, Password, Credentials) Public {
#Include %occErrors
#Include %occStatus

Do ##class(%Prompt).GetString("USER:",.Username)
Do ##class(%Prompt).GetString("PASS:",.Password) 
Quit $$$OK
}

У терміналі це буде виглядати аналогічно звичайному логіном.

>USER: _SYSTEM
>PASS: SYS

RFID
Перейдемо до аутентифікації на RFID. Ідея полягає в наступному — з Caché зробимо можливість записувати в зашифрованому вигляді інформацію на картку, а при аутентифікації будемо її зчитувати, розшифровувати і повертати на перевірку.

Для початку зберемо схему з Arduino Uno і модуля RFID-RC522:



Ось код, що використовує бібліотеку MF522 (там же є терморегулятори для інших моделей Arduino). Він по COM порту приймає 2 команди:

  • Get — по ній на com порт передається вміст блоків RFID карти 2, 4, 5, 6
  • Set@bloc2@bloc4@bloc5@bloc6 — по ній вміст блоків 2, 4, 5, 6 на карті перезаписується прийшли даними
Код на C
#include <SPI.h> //include the SPI bus library
#include <MFRC522.h> //include the RFID reader library

#define SS_PIN 10 //slave select pin
#define RST_PIN 9 //reset pin

#define u1b 2 //Block on a card for user1 byte array
#define u2b 4 //Block on a card for user2 byte array
#define p1b 5 //Block on a card for pass1 byte array
#define p2b 6 //Block on a card for pass2 byte array

MFRC522 mfrc522(SS_PIN, RST_PIN); // instatiate a MFRC522 reader object.
MFRC522::MIFARE_Key key; //create a MIFARE_Key struct named 'key', which will hold the card information

byte readbackblock[18]; //This is array used for reading out a block. The MIFARE_Read method requires a buffer that is at least 18

String inString = ""; // COM port incoming data buffer

void setup() {
Serial.begin(9600); // Initialize serial communications with the PC
SPI.begin(); // Init SPI bus
mfrc522.PCD_Init(); // Init MFRC522 card (in case you wonder what PCD means: proximity coupling device)
// Serial.println("Scan a MIFARE Classic card");

// Prepare the security key for the read and write functions - all six key bytes are set to 0xFF at chip delivery from the factory
// Since the cards in the kit are new and the keys were never defined, they are 0xFF
// if we had a card that was programmed by someone else, we would need to know the key to be able to access it. 
// This key would then need to be stored in 'key' instead.

for (byte i = 0; i < 6; i++) {
key.keyByte[i] = 0xFF; // keyByte is defined in the "MIFARE_Key" 'struct' definition in the .h file of the library
}
}

void loop() {
// put your main code here, to run repeatedly:
// Receive data from com port
while (Serial.available() > 0) {
int inChar = Serial.read();

if (inChar != '\n') {
inString += (char)inChar;
} else {
// New line
while (!initCard()); // connect to an RFID card
String Action = inString.substring(0, 3);
if (Action == "Set") {
// Write login and pass into the card
setUserAndPassToCard(inString);
} else if (Action == "Get") {
// Read login and pass from the card
readUserAndPassToCom();
} else {
Serial.println(Action);
}

disconnectCard(); // disconnect RFID card
inString = "";
}
}
}

/// Read blocks with user/pass info and the output to COM port:
/// user1user2@pass1pass2
void readUserAndPassToCom()
{
readBlockToCom(u1b);
readBlockToCom(u2b);
Serial.write("@");
readBlockToCom(p1b);
readBlockToCom(p2b);
Serial.println("");
}

/// Set user/pass info into a card
/// Data Set@user1@user2@pass1@pass2
/// Sample Data Set@1234567890123456@1234567890123456@1234567890123456@1234567890123456
void setUserAndPassToCard(String Data) {
// Serial.println(Data);
byte user1[16], user2[16], pass1[16], pass2[16];

String user1str = inString.substring(4, 20);
String user2str = inString.substring(21, 37);
String pass1str = inString.substring(38, 54);
String pass2str = inString.substring(55, 71);

stringToArray(user1str, user1, sizeof(user1));
stringToArray(user2str, user2, sizeof(user2));
stringToArray(pass1str, pass1, sizeof(pass1));
stringToArray(pass2str, pass2, sizeof(pass2));

writeBlock(u1b, user1); // u1b is the block number, user1 is the block content
writeBlock(u2b, user2);
writeBlock(p1b, pass1);
writeBlock(p2b, pass2);

Serial.println("Done");
}

void stringToArray(String str, byte array[], int arrlength)
{
for (int j = 0 ; j < arrlength ; j++)
{
array[j] = str.charAt(j);
}
}

bool initCard()
{
// Look for new cards (in case you wonder what PICC means: proximity integrated circuit card)
if ( ! mfrc522.PICC_IsNewCardPresent()) {//if PICC_IsNewCardPresent returns 1, a new card has been found and we continue
return false; //if it did not find a new card is returns a '0' and we return to the start of the loop
}

// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial()) {//if PICC_ReadCardSerial returns 1, the "uid" struct (see MFRC522.h lines 238-45)) contains the ID of the read card.
return false; //if it returns a '0' something went wrong and we return to the start of the loop
}
return true;
}

void disconnectCard()
{
// Halt PICC
mfrc522.PICC_HaltA();
// Stop encryption on PCD
mfrc522.PCD_StopCrypto1();
}

void readBlockToCom(int number)
{
readBlock(number, readbackblock);//read the block back
for (int j = 0 ; j < 16 ; j++) //print the block contents
{
Serial.write (readbackblock[j]);//Serial.write() transmits the ASCII numbers as human readable characters to serial monitor
}
}

int writeBlock(int blockNumber, byte arrayAddress[])
{
// this makes sure that we only write data into blocks. Every 4th block is a trailer block for the access/security info.
int largestModulo4Number = blockNumber / 4 * 4;
int trailerBlock = largestModulo4Number + 3; //determine trailer block for the sector
if (blockNumber > 2 && (blockNumber + 1) % 4 == 0) {
Serial.print(blockNumber); //block number is a trailer block (modulo 4); quit and send error code 2
Serial.println(" is a trailer block:");
return 2;
}
//Serial.print(blockNumber);
//Serial.println(" is a data block:");

/*****************************************authentication of the desired block for access***********************************/
byte status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
// byte PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key *key, Uid *uid);
// this method is used to authenticate a certain block for writing or reading
// command: See enumerations above -> PICC_CMD_MF_AUTH_KEY_A = 0x60 (=1100000), 
// this command performs authentication with Key A
// blockAddr is the number of the block from 0 to 15.
// MIFARE_Key *key is a pointer to the MIFARE_Key struct defined above, this struct needs to be defined for each block. 
// New cards have all A/B= FF FF FF FF FF FF
// Uid *uid is a pointer to the UID struct that contains the user ID of the card.
if (status != MFRC522::STATUS_OK) {
Serial.print("PCD_Authenticate() failed: ");
Serial.println(mfrc522.GetStatusCodeName(status));
return 3;//return "3" as error message
}
// it appears the authentication needs to be made before every block read/write within a specific sector.
// If a different sector is being authenticated access to the previous one is lost.

/*****************************************writing the block***********************************************************/

status = mfrc522.MIFARE_Write(blockNumber, arrayAddress, 16);
//valueBlockA is the block number, MIFARE_Write(block number (0-15), byte array containing 16 values, number of bytes in block (=16))
// status = mfrc522.MIFARE_Write(9, value1Block, 16);
if (status != MFRC522::STATUS_OK) {
Serial.print("MIFARE_Write() failed: ");
Serial.println(mfrc522.GetStatusCodeName(status));
return 4;//return "4" as error message
}
//Serial.println("block was written");
}

int readBlock(int blockNumber, byte arrayAddress[])
{
int largestModulo4Number = blockNumber / 4 * 4;
int trailerBlock = largestModulo4Number + 3; //determine trailer block for the sector

/*****************************************authentication of the desired block for access********************************************/
byte status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
// byte PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key *key, Uid *uid);
// this method is used to authenticate a certain block for writing or reading
// command: See enumerations above -> PICC_CMD_MF_AUTH_KEY_A = 0x60 (=1100000), 
// this command performs authentication with Key A
// blockAddr is the number of the block from 0 to 15.
// MIFARE_Key *key is a pointer to the MIFARE_Key struct defined above, this struct needs to be defined for each block. 
// New cards have all A/B= FF FF FF FF FF FF
// Uid *uid is a pointer to the UID struct that contains the user ID of the card.
if (status != MFRC522::STATUS_OK) {
Serial.print("PCD_Authenticate() failed (read): ");
Serial.println(mfrc522.GetStatusCodeName(status));
return 3;//return "3" as error message
}
// it appears the authentication needs to be made before every block read/write within a specific sector.
// If a different sector is being authenticated access to the previous one is lost.

/*****************************************reading a block***********************************************************/

byte buffersize = 18;//we need to define a variable with the read buffer size, since the MIFARE_Read method below needs a pointer to the variable that contains the size...
status = mfrc522.MIFARE_Read(blockNumber, arrayAddress, &buffersize);//&buffersize is a pointer to the buffersize variable; MIFARE_Read requires a pointer instead of just a number
if (status != MFRC522::STATUS_OK) {
Serial.print("MIFARE_read() failed: ");
Serial.println(mfrc522.GetStatusCodeName(status));
return 4;//return "4" as error message
}
}

Клас Arduino.Представник, який має 2 точки входу:

  • SetCredentials — приймає на вхід логін і пароль, шифрує їх AES шифруванням за допомогою ключа, що зберігається в системі і записує на RFID-картку.

  • GetCredentials — отримує шифротекст з карти і розшифровує його, повертаючи логін, пароль і статус операції.
Arduino.Delegate
/// Delegated Authentication with Arduino.
/// Installation steps:<br>
/// 1. Connect arduino (and upload C code from Delegated.ino there)<br>
/// 2. Make this class visible in %SYS namespace (import or there map pckage)<br>
/// 3. Set SerialPort parameter to a correct value and recompile the class<br>
/// 4. Run <example>Do ##class(Arduino.Delegated).InitEncryption(Key, IV)</example>
/// 5. Write encrypted user credentials to RFID card with SetCredentials<br>
/// 6. Import ZAUTHENTICATE into %SYS<br>
/// 7. Enable session is not and password auth for relevant services and/or apps
Class Arduino.Delegated [ Abstract ]
{

Parameter SerialPort As %String = "com3";

/// Creates managed encryption key.<br>
/// key - Input key material.
/// Key material 16, 24, 32 characters or long (on Unicode systems, character with all values < 256) is used directly.
/// Otherwise, Password-Based Key Derivation Function #2 (PBKDF2)
/// is used with HMAC-SHA-1,
/// no salt, and one iteration
/// to generate an AES key of the next larger valid size (up to 32 байт).
/// (See RSA Laboratories Public-Key Cryptography Standards #5 for more information.)
/// <br><br>
/// IV - Initialization vector (optional).
/// If this argument is present it must be 16 characters long (on Unicode systems, character with all values < 256).
/// If this argument is omitted (or is an empty string), a null initialization vector is used.
/// <br>
/// <example>Do ##class(Arduino.Delegated).Init("", "")</example>
ClassMethod Init(Key As %String, IV As %String)
{
New $Namespace
Set $Namespace = "%SYS"
Set ^Arduino("Key")= Key
Set ^Arduino("IV")= IV
}

/// Send Arduino the command to set credentials on a card to Username/Password (encrypted)
/// <example>Do ##class(Arduino.Delegated).SetCredentials("_SYSTEM", "SYS")</example>
ClassMethod SetCredentials(Username As %String(MAXLEN=15), Password As %String(MAXLEN=15)) As %Status
{

Set Status = $$$OK

Set CipherUsername = ..EncryptText(Username)
Set CipherPassword = ..EncryptText(Password)

Set User1 = $Extract(CipherUsername, 1, 16)

Set User2 = $Extract(CipherUsername, 17, 32)
Set User2 = ..AppendToString(User2, , 16)

Set Pass1 = $Extract(CipherPassword, 1, 16)

Set Pass2 = $Extract(CipherPassword, 17, 32)
Set Pass2 = ..AppendToString(Pass2, , 16)

Set CommandList = $ListBuild("Set", User1, User2, Pass1, Pass2)
Command Set = $ListToString(CommandList, "@")

Set Status = ..ExecuteCommand(.Command)
If (Status = "Done") {
Set Status = $$$OK
} Else {
Set Status = $$$ERROR($$$GeneralError, "SetCredentials failure, received: " _ Status) 
}

Return Status
}

/// Connect to an Arduino device, receive credentials, decode them and set to Username/Password variables.
/// <example>do ##class(Arduino.Delegated).GetCredentials(.Username, .Password)</example>
ClassMethod GetCredentials(Output Username As %String, Output As Password %String) As %Status
{
Kill Username, Password
Set Username = ""
Set Password = ""
Set Status = $$$OK

Set Credentials = ..ExecuteCommand("Get")
If (($L(Credentials) =65) && ($L(Credentials,"@") = 2)) {
Set CipherUsername = $Piece(Credentials, "@", 1)
Set CipherPassword = $Piece(Credentials, "@", 2)
Set CipherUsername = $Extract(CipherUsername, 1, 24) // we need only first 24 characters
Set CipherPassword = $Extract(CipherPassword, 1, 24)
Set Username = ..DecryptText(CipherUsername)
Set Password = ..DecryptText(CipherPassword)
} Else {
Set Status = $$$ERROR($$$GeneralError, "GetCredentials failure, received: " _ Credentials) 
}

Return Status
}

/// Send one line at a time using common terminating characters (i.e., CR) and receive output
/// Possible comands:<br>
/// <b>Get</b> - reads an RFID card and returns information in a format: user@pass<br>
/// <b>Set@user1@user2@pass1@pass2</b> - sets information on a RFID card 
/// in a format: user@pass (where user = user1@user2)<br>
/// Returns output, produced by Arduino
/// <example>w ##class(Arduino.Delegated).ExecuteCommand("Get")</example>
ClassMethod ExecuteCommand(ByRef Command As %String, SerialPort = {..#SerialPort}) As %String
{
set x=""
try {
//Parameters used to open the serial device:
// portstate = " 0801n0" - by byte position:
// 1: space indicates "don't disconnect the port"
// 2: 0 indicates "don't use modem control"
// 3: 8 indicates 8 data bits
// 4: 0 indicates no parity
// 5: 1 indicates one stop bit
// 6: n indicates that flow control is disabled
// 7: 0 indicates disable DTR
// /BAUD=9600 determines the baud rate, of course.
open SerialPort:(:::" 0801n0":/BAUD=9600)
set old = $io //Keep track of the original device
use SerialPort

write $char(10)
hang 1
write Command _ $Char(10)

read x //Read until a termination character is reached
use old
close SerialPort 
} catch ex {
close SerialPort
w $System.Status.GetErrorText(ex.AsStatus())
}

return x
}

/// Get key to encode/decode via EncryptText/DecryptText
ClassMethod GetKey() [ CodeMode = expression ]
{
$Get(^Arduino("Key"))
}

/// Get IV to encode/decode via EncryptText/DecryptText
ClassMethod GetIV() [ CodeMode = expression ]
{
$Get(^Arduino("IV"))
}

/// Encrypt PlainText with AESCBCEncrypt
/// <example>Write ##class(Arduino.Delegated).EncryptText("string")</example>
ClassMethod EncryptText(PlainText As %String) As %String
{

Set Text=$ZConvert(PlainText,"O","UTF8")
Set Text=$System.Encryption.AESCBCEncrypt(Text, ..GetKey(), ..GetIV())
Set Ciphertext=$System.Encryption.Base64Encode(Text)
Return Ciphertext
}

/// Decrypt PlainText with AESCBCEncrypt
/// <example>Write ##class(Arduino.Delegated).DecryptText("sFgKzZVle187N4OqhhcXPw==")</example>
ClassMethod DecryptText(CipherText As %String) As %String
{
Set Text=$System.Encryption.Base64Decode(CipherText)
Set Text=$System.Encryption.AESCBCDecrypt(Text, ..GetKey(), ..GetIV())
Set PlainText=$ZConvert(Text,"І","UTF8") 
Return PlainText
}

/// Extends right side of a String by Character up to Length chars
/// <example>Write ##class(Arduino.Delegated).AppendToString("")</example>
ClassMethod AppendToString(String As %String, Character As %String(MAXLEN=1) = "_", Length As %Integer = {$Length(String)}) As %String
{
Set Difference = Length - $Length(String) 
Return:Difference<=0 String
Set Tail = $Justify("", Difference)
Set Tail = $Translate(Tail, " ", Character)
Return String _ Tail
}

}

Рутина ZAUTHENTICATE, яка викликає клас Arduino.Delegated, метод GetCredentials:

ZAUTHENTICATE(ServiceName, Namespace, Username, Password, Credentials, Properties) PUBLIC {
#Include %occStatus
Quit $$$OK
}

GetCredentials(ServiceName, Namespace, Username, Password, Credentials) Public {
#Include %occErrors
#Include %occStatus 
Quit ##class(Arduino.Delegated).GetCredentials(.Username, .Password)
}

Готово! Зібране пристрій виглядає ось так:



Встановлюємо ключі шифрування в терміналі, %SYS (там повинен бути доступний клас Arduino.Delegated):

Do ##class(Arduino.Delegated).InitEncryption(Key, IV)

Де Key — ключ шифрування, IV — вектор ініціалізації. Вони будуть використовуватися для шифрування логіна і пароля. Підключаємо Arduino до Caché і записуємо на картку інформацію для аутентифікації командою:

Do ##class(Arduino.Delegated).SetCredentials("_SYSTEM", "SYS")

Включаємо аутентифікацію Delegated та Password у потрібних сервісах/веб-додатках і можна аутентифицироваться (наприклад, у терміналі або порталі управління системою) підносячи картку до зчитувача RFID карт.

Можливі поліпшення
  • Підвищення рівня безпеки з допомогою використання керованих ключів шифрування для шифрування логіна і пароля.

  • Підвищення рівня безпеки з допомогою використання двофакторної аутентифікації — спочатку отримувати пару логін/пароль, а потім зчитувати карту, на якій зберігається ключ, унікальний для користувача. Потім потрібно звіряти отриманий ключ з тим, що зберігається в системі для даного користувача. Варіанти зберігання довільних даних користувача обсуждались Community порталі InterSystems.

  • Додати можливість зберігання логіна і пароля довше 15 символів кожен.
Висновки
Гнучка система аутентифікації Caché дозволяє реалізувати довільну логіку аутентифікації користувачів.

Посилання
» Документация
» GitHub репозиторій з кодом (в області SAMPLES є приклад рутини ZAUTHENTICATE)

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.