情境:
我想从stdin
控制台中获取密码输入 - 而不显示用户所输入的内容。在Go语言中是否有类似于getpasswd
的功能?
我尝试了:
我尝试使用syscall.Read
,但它会回显输入的内容。
以下是完成此任务的最佳方法之一。
首先通过go get golang.org/x/term
获取term
包。
package main
import (
"bufio"
"fmt"
"os"
"strings"
"syscall"
"golang.org/x/term"
)
func main() {
username, password, _ := credentials()
fmt.Printf("Username: %s, Password: %s\n", username, password)
}
func credentials() (string, string, error) {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter Username: ")
username, err := reader.ReadString('\n')
if err != nil {
return "", "", err
}
fmt.Print("Enter Password: ")
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
return "", "", err
}
password := string(bytePassword)
return strings.TrimSpace(username), strings.TrimSpace(password), nil
}
int(syscall.Stdin)
无法正常工作。我按照以下步骤使其正常运行:https://dev59.com/wVYN5IYBdhLWcg3wJFX8#47890273 - cwash刚刚在 #go-nuts 邮件列表中看到了一封邮件,有个人编写了一个非常简单的 Go 语言包可供使用。您可以在这里找到它:https://github.com/howeyc/gopass
大致就是这样:
package main
import "fmt"
import "github.com/howeyc/gopass"
func main() {
fmt.Printf("Password: ")
pass := gopass.GetPasswd()
// Do something with pass
}
自 Go v1.11 开始,官方推出了一个包golang.org/x/term
,用于替换已停用的crypto/ssh/terminal
。它具有许多功能,其中包括函数term.ReadPassword
。
使用示例:
package main
import (
"fmt"
"os"
"syscall"
"golang.org/x/term"
)
func main() {
fmt.Print("Password: ")
bytepw, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
os.Exit(1)
}
pass := string(bytepw)
fmt.Printf("\nYou've entered: %q\n", pass)
}
我有一个类似的用例,以下代码片段对我很有效。如果你仍然卡在这里,请随意尝试。
import (
"fmt"
"golang.org/x/crypto/ssh/terminal"
)
func main() {
fmt.Printf("Now, please type in the password (mandatory): ")
password, _ := terminal.ReadPassword(0)
fmt.Printf("Password is : %s", password)
}
当然,你需要事先使用 go get
安装终端包。
terminal.ReadPassword(int(os.Stdin.Fd()))
。 - captncraig您可以通过执行stty -echo
来关闭回显,然后在读取密码后执行stty echo
来打开回显。
// getPassword - Prompt for password. Use stty to disable echoing.
import ( "bufio"; "fmt"; "os"; "strings"; "syscall" )
func getPassword(prompt string) string {
fmt.Print(prompt)
// Common settings and variables for both stty calls.
attrs := syscall.ProcAttr{
Dir: "",
Env: []string{},
Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
Sys: nil}
var ws syscall.WaitStatus
// Disable echoing.
pid, err := syscall.ForkExec(
"/bin/stty",
[]string{"stty", "-echo"},
&attrs)
if err != nil {
panic(err)
}
// Wait for the stty process to complete.
_, err = syscall.Wait4(pid, &ws, 0, nil)
if err != nil {
panic(err)
}
// Echo is disabled, now grab the data.
reader := bufio.NewReader(os.Stdin)
text, err := reader.ReadString('\n')
if err != nil {
panic(err)
}
// Re-enable echo.
pid, err = syscall.ForkExec(
"/bin/stty",
[]string{"stty", "echo"},
&attrs)
if err != nil {
panic(err)
}
// Wait for the stty process to complete.
_, err = syscall.Wait4(pid, &ws, 0, nil)
if err != nil {
panic(err)
}
return strings.TrimSpace(text)
}
需要通过Go ForkExec()函数启动stty:
package main
import (
os "os"
bufio "bufio"
fmt "fmt"
str "strings"
)
func main() {
fmt.Println();
if passwd, err := Getpasswd("Enter password: "); err == nil {
fmt.Printf("\n\nPassword: '%s'\n",passwd)
}
}
func Getpasswd(prompt string) (passwd string, err os.Error) {
fmt.Print(prompt);
const stty_arg0 = "/bin/stty";
stty_argv_e_off := []string{"stty","-echo"};
stty_argv_e_on := []string{"stty","echo"};
const exec_cwdir = "";
fd := []*os.File{os.Stdin,os.Stdout,os.Stderr};
pid, err := os.ForkExec(stty_arg0,stty_argv_e_off,nil,exec_cwdir,fd);
if err != nil {
return passwd, os.NewError(fmt.Sprintf("Failed turning off console echo for password entry:\n\t%s",err))
}
rd := bufio.NewReader(os.Stdin);
os.Wait(pid,0);
line, err := rd.ReadString('\n');
if err == nil {
passwd = str.TrimSpace(line)
} else {
err = os.NewError(fmt.Sprintf("Failed during password entry: %s",err))
}
pid, e := os.ForkExec(stty_arg0,stty_argv_e_on,nil,exec_cwdir,fd);
if e == nil {
os.Wait(pid,0)
} else if err == nil {
err = os.NewError(fmt.Sprintf("Failed turning on console echo post password entry:\n\t%s",e))
}
return passwd, err
}
undefined os.Error
,可以通过将os.Error
更改为error
来修复,类似于https://github.com/imbc/go_starter_package/issues/1。看起来有很多Golang的变化。虽然感谢分享。 - minghua这里是针对Linux特定版本的:
func terminalEcho(show bool) {
// Enable or disable echoing terminal input. This is useful specifically for
// when users enter passwords.
// calling terminalEcho(true) turns on echoing (normal mode)
// calling terminalEcho(false) hides terminal input.
var termios = &syscall.Termios{}
var fd = os.Stdout.Fd()
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd,
syscall.TCGETS, uintptr(unsafe.Pointer(termios))); err != 0 {
return
}
if show {
termios.Lflag |= syscall.ECHO
} else {
termios.Lflag &^= syscall.ECHO
}
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd,
uintptr(syscall.TCSETS),
uintptr(unsafe.Pointer(termios))); err != 0 {
return
}
}
fmt.Print("password: ")
terminalEcho(false)
var pw string
fmt.Scanln(&pw)
terminalEcho(true)
fmt.Println("")
TCGETS系统调用是Linux特有的。在OSX和Windows中有不同的系统调用值。
golang.org/x/crypto/ssh/terminal
包可以做到这一点,而且处理了Go支持的所有操作系统。(例如,在BSD上使用syscall.TIOCSETA
而不是syscall.TCSETS
,在Windows上使用kernel32.dll
)。 - Dave Cfunc GetPassword(prompt string) (err error, text string) {
var modeOn, modeOff uint32
stdin := syscall.Handle(os.Stdin.Fd())
err = syscall.GetConsoleMode(stdin, &modeOn)
if err != nil {
return
}
modeOff = modeOn &^ 0x0004
proc := syscall.MustLoadDLL("kernel32").MustFindProc("SetConsoleMode")
fmt.Print(prompt)
_, _, _ = proc.Call(uintptr(stdin), uintptr(modeOff))
_, err = fmt.Scanln(&text)
if err != nil {
return
}
_, _, _ = proc.Call(uintptr(stdin), uintptr(modeOn))
fmt.Println()
return nil, strings.TrimSpace(text)
}