Go 中的 getpasswd 功能?

发布于 2024-08-18 18:59:43 字数 201 浏览 3 评论 0原文

情况:

我想从 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

小情绪 2024-08-25 18:59:43

以下是完成此任务的最佳方法之一。
首先通过 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
}

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

秋风の叶未落 2024-08-25 18:59:43

刚刚在#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
}

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
}
絕版丫頭 2024-08-25 18:59:43

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

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)
}
埋情葬爱 2024-08-25 18:59:43

我有一个类似的用例,下面的代码片段对我来说效果很好。如果您仍然被困在这里,请随意尝试一下。

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安装终端包。

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.

秋凉 2024-08-25 18:59:43

您可以通过执行 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

無心 2024-08-25 18:59:43

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

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

我已经在 Linux 和 BSD (Mac) 上进行了测试。它不适用于 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)
}

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)
}
久夏青 2024-08-25 18:59:43

需要通过 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
}

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
}
你对谁都笑 2024-08-25 18:59:43

这是特定于 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("")

它是 Linux 特定的 TCGETS 系统调用。 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.

和我恋爱吧 2024-08-25 18:59:43

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

梦一生花开无言 2024-08-25 18:59:43

在打字前关闭回显,并在打字后将其重新打开。

如果没有第三个库,您可以找到在 UNIX 上使用它的方法,如上所示。但在 Windows 上就很难了。

您可以通过使用 Windows kernel32.dll 的方法 SetConsoleMode 来实现它,参考 C: 如何在 Windows 控制台中禁用回显?

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)
}
蓝天 2024-08-25 18:59:43

您可以使用 os.File 对象(或 os.Stdin 变量)的 Read 方法获得所需的行为。以下示例程序将读取一行文本(通过按返回键终止),但在调用 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文