在Go中实现getpasswd功能?

huangapple go评论148阅读模式
英文:

getpasswd functionality in Go?

问题

情况:

我想从stdin控制台获取一个密码输入,而不显示用户输入的内容。在Go语言中是否有类似于getpasswd功能的东西?

我尝试过的:

我尝试使用syscall.Read,但它会显示用户输入的内容。

英文:

Situation:

I want to get a password entry from the stdin console - without echoing what the user types. Is there something comparable to getpasswd functionality in Go?

What I tried:

I tried using syscall.Read, but it echoes what is typed.

答案1

得分: 131

以下是完成此任务的最佳方法之一。
首先通过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("用户名: %s, 密码: %s\n", username, password)
}

func credentials() (string, string, error) {
    reader := bufio.NewReader(os.Stdin)

    fmt.Print("输入用户名: ")
    username, err := reader.ReadString('\n')
    if err != nil {
        return "", "", err
    }

    fmt.Print("输入密码: ")
    bytePassword, err := term.ReadPassword(int(syscall.Stdin))
    if err != nil {
        return "", "", err
    }

    password := string(bytePassword)
    return strings.TrimSpace(username), strings.TrimSpace(password), nil
}

http://play.golang.org/p/l-9IP1mrhA

英文:

The following is one of best ways to get it done.
First get term package by go get golang.org/x/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
}

http://play.golang.org/p/l-9IP1mrhA

答案2

得分: 29

刚刚在 #go-nuts 邮件列表中看到一封邮件。有人写了一个相当简单的 Go 包供使用。你可以在这里找到它:https://github.com/howeyc/gopass

它的代码如下:

package main

import "fmt"
import "github.com/howeyc/gopass"

func main() {
    fmt.Printf("密码: ")
    pass := gopass.GetPasswd()
    // 使用 pass 做一些操作
}
英文:

Just saw a mail in #go-nuts maillist. There is someone who wrote quite a simple go package to be used. You can find it here: https://github.com/howeyc/gopass

It something like that:

package main

import "fmt"
import "github.com/howeyc/gopass"

func main() {
    fmt.Printf("Password: ")
    pass := gopass.GetPasswd()
    // Do something with pass
}

答案3

得分: 24

自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("密码: ")
	bytepw, err := term.ReadPassword(int(syscall.Stdin))
	if err != nil {
		os.Exit(1)
	}
	pass := string(bytepw)
	fmt.Printf("\n你输入的是: %q\n", pass)
}
英文:

Since Go ~v1.11 there is an official package golang.org/x/term which replaces the deprecated crypto/ssh/terminal. It has, among other things, the function term.ReadPassword.

Example usage:

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)
}

答案4

得分: 11

我有一个类似的用例,下面的代码片段对我很有效。如果你还在困扰,可以随意尝试一下。

import (
	"fmt"
	"golang.org/x/crypto/ssh/terminal"
)

func main() {
	fmt.Printf("现在,请输入密码(必填):")
	password, _ := terminal.ReadPassword(0)

	fmt.Printf("密码是:%s", password)
}

当然,你需要事先使用go get安装terminal包。

英文:

I had a similar usecase and the following code snippet works well for me. Feel free to try this if you are still stuck here.

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)
}

Of course, you need to install terminal package using go get beforehand.

答案5

得分: 7

你可以通过执行stty -echo来关闭回显,然后在读取密码后执行stty echo来重新打开回显。

英文:

you can do this by execing stty -echo to turn off echo and then stty echo after reading in the password to turn it back on

答案6

得分: 5

这是我使用Go1.6.2开发的一个解决方案,你可能会觉得有用。

它只使用了以下标准包:bufiofmtosstringssyscall。具体来说,它使用了syscall.ForkExec()syscall.Wait4()来调用stty来禁用/启用终端回显。

我在Linux和BSD(Mac)上进行了测试。它在Windows上不起作用。

// getPassword - 提示输入密码。使用stty禁用回显。
import ( "bufio"; "fmt"; "os"; "strings"; "syscall" )
func getPassword(prompt string) string {
    fmt.Print(prompt)

    // 用于两个stty调用的公共设置和变量。
    attrs := syscall.ProcAttr{
        Dir:   "",
        Env:   []string{},
        Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
        Sys:   nil}
    var ws syscall.WaitStatus

    // 禁用回显。
    pid, err := syscall.ForkExec(
        "/bin/stty",
        []string{"stty", "-echo"},
        &attrs)
    if err != nil {
        panic(err)
    }

    // 等待stty进程完成。
    _, err = syscall.Wait4(pid, &ws, 0, nil)
    if err != nil {
        panic(err)
    }

    // 回显已禁用,现在获取数据。
    reader := bufio.NewReader(os.Stdin)
    text, err := reader.ReadString('\n')
    if err != nil {
        panic(err)
    }

    // 启用回显。
    pid, err = syscall.ForkExec(
        "/bin/stty",
        []string{"stty", "echo"},
        &attrs)
    if err != nil {
        panic(err)
    }

    // 等待stty进程完成。
    _, err = syscall.Wait4(pid, &ws, 0, nil)
    if err != nil {
        panic(err)
    }

    return strings.TrimSpace(text)
}
英文:

Here is a solution that I developed using Go1.6.2 that you might find useful.

It only uses the following standard packages: bufio, fmt, os, strings and syscall. More specifically, it uses syscall.ForkExec() and syscall.Wait4() to invoke stty to disable/enable terminal echo.

I have tested it on Linux and BSD (Mac). It will not work on windows.

// 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)
}

答案7

得分: 2

需要通过Go的ForkExec()函数来启动stty:

package main

import (
    os      "os"
    bufio   "bufio"
    fmt     "fmt"
    str     "strings"
)

func main() {
    fmt.Println();
    if passwd, err := Getpasswd("输入密码: "); err == nil {
        fmt.Printf("\n\n密码: '%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("关闭密码输入时的控制台回显失败:\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("密码输入过程中出错: %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("开启密码输入后的控制台回显失败:\n\t%s",e))
    }
    return passwd, err
}
英文:

Required launching stty via Go ForkExec() function:

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
}

答案8

得分: 2

这是一个针对Linux特定的版本:

func terminalEcho(show bool) {
    // 启用或禁用终端输入的回显。这对于用户输入密码时特别有用。
    // 调用 terminalEcho(true) 打开回显(正常模式)
    // 调用 terminalEcho(false) 隐藏终端输入。
    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有不同的系统调用值。

英文:

Here is a version specific to 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
    }
}

So to use it:

fmt.Print("password: ")

terminalEcho(false)
var pw string
fmt.Scanln(&pw)
terminalEcho(true)
fmt.Println("")

It's the TCGETS syscall that is linux specific. There are different syscall values for OSX and Windows.

答案9

得分: 1

您还可以使用https://github.com/peterh/liner软件包的PasswordPrompt函数。

英文:

You could also use PasswordPrompt function of https://github.com/peterh/liner package.

答案10

得分: 0

> 在输入之前关闭回显,在输入之后打开回显。

在Unix上,你可以找到上面显示的方法来完成它,但在Windows上很困难。

你可以通过使用Windows的kernel32.dll中的SetConsoleMode方法来实现,参考C: How to disable echo in windows console?中的被接受的答案。

func 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)
}
英文:

> Turning off echo before typing and turning on to turn it back on after typing.

Without third library, you can find ways to do with it on unix shown above. But it's difficult on Windows.

You can achieve it by method SetConsoleMode with windows kernel32.dll referring to the accepted answer from C: How to disable echo in windows console?

func 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)
}

答案11

得分: -3

你可以通过os.File对象的Read方法(或os.Stdin变量)来实现所需的行为。以下示例程序将读取一行文本(通过按下回车键终止),但在fmt.Printf调用之前不会回显。

package main

import "fmt"
import "os"

func main() {
  var input []byte = make([]byte, 100)
  os.Stdin.Read(input)
  fmt.Printf("%s", input)
}

如果你想要更高级的行为,可能需要使用Go的C包装工具,并为低级API调用创建一些包装器。

英文:

You can get the behavior you want with the Read method from the os.File object (or the os.Stdin variable). The following sample program will read a line of text (terminated with by pressing the return key) but won't echo it until the fmt.Printf call.

package main

import "fmt"
import "os"

func main() {
  var input []byte = make( []byte, 100 );
  os.Stdin.Read( input );
  fmt.Printf( "%s", input );
}

If you want more advanced behavior, you're probably going to have to use the Go C-wrapper utilities and create some wrappers for low-level api calls.

huangapple
  • 本文由 发表于 2010年1月26日 11:25:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/2137357.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定