Linux系统中,如何将在终端输入密码时将密码隐藏?
最近做简单的登录界面时,不做任何操作的话,在终端输入密码的同时也会显示输入的密码是什么,这样对于隐蔽性和使用都有不好的体验。那么我就想到将密码用字符'*'隐藏起来,这样看起来才像一个完整的登录界面。
当然,使用QT做登录界面隐藏密码就没有这么麻烦,在本文中,仅作为乐趣供读者参考。
1. 首先来看看实现效果是怎么样的:
在该程序中,我输入了密码是123456,它在输入的同时也将密码用星号'*'代替了,密码也不会丢失,仍然保存在数组中,所以第二行就能完整显示密码。
2. 实现分析
实现隐藏密码的方法就两个过程。第一,使自己在终端输入的字符不显示;第二,在终端上同步输入星号'*'。在终端上输入等量的星号'*'并不难,但是我们要同时让,自己在键盘上输入的字符不显示在终端上,那么,如何让自己输入的字符不显示在终端上,也能存入内存中呢?我们需要禁用回显和行缓冲。
回显:使键盘输入的字符立即显示到终端上;行缓冲:使输入的数据存储到缓冲区中,直到用户输入回车键后才会被传递给程序。这意味着在输入密码时,密码会被暂时存储在缓冲区中,可能会被其他程序截获。禁用行缓冲模式可以避免密码被截获。
3. 函数介绍
经过上述的分析,接下来介绍一个函数,来实现上述的要求。
头文件:
#include <termios.h>
该头文件定义了一个termios结构体,可以使用tcgetattr和tcsetattr函数来获取和设置终端属性。通过设置终端的输入模式,可以实现在输入密码时用星号代替。
禁用回显和行缓冲的代码演示:
// 禁用终端回显和行缓冲
struct termios old_term, new_term;
tcgetattr(STDIN_FILENO, &old_term); // 获取当前终端属性,存储到old_term中
new_term = old_term;
new_term.c_lflag &= ~(ECHO | ICANON); // 禁用回显和行缓冲两个属性
tcsetattr(STDIN_FILENO, TCSANOW, &new_term);
- 先定义两个结构体,分别代表两种模式。
- 使用tcgetattr函数获取当前属性,以保证可以回到旧属性。
- 赋值
- 将新属性中的回显和行缓冲两个属性禁用,ECHO表示是否回显输入字符,ICANON表示是否启用行缓冲模式。
- 使用tcsetattr函数使用新属性,TCSANOW参数表示立即生效。
在4中,(ECHO | ICANON)表示将ECHO和ICANON标志位置反,即将它们的值从1变为0,再使用&运算符将new_term.c_lflag中的ECHO和ICANON标志位设置为0,而保留其他标志位的值不变。
这样就实现了在键盘上输入但不显示到终端的方法。
4. 输入星号'*'
while((ch = getchar()) != '\n' && i < MAX) {
pwd[i++] = ch;
printf("*");
}
每读取一个字符,都会输出一个星号'*',保证了星号和键盘输入同步进行。
5. 完整代码
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#define MAX 20
int main(void)
{
char pwd[MAX+1];
int i = 0;
char ch;
// 禁用终端回显和行缓冲
struct termios old_term, new_term;
tcgetattr(STDIN_FILENO, &old_term);
new_term = old_term;
new_term.c_lflag &= ~(ECHO | ICANON); // 禁用回显和行缓冲两个属性
tcsetattr(STDIN_FILENO, TCSANOW, &new_term);
printf("Enter password : ");
while((ch = getchar()) != '\n' && i < MAX) {
pwd[i++] = ch;
printf("*");
}
pwd[i] = '\0';
printf("\nYour password is %s\n", pwd);
// 恢复终端属性
tcsetattr(STDIN_FILENO, TCSANOW,&old_term);
return 0;
}