2022年10月

下载镜像

准确来说是下载根文件系统,这里从清华镜像源下载。

https://mirrors.tuna.tsinghua.edu.cn/lxc-images/

Debian:
https://mirrors.tuna.tsinghua.edu.cn/lxc-images/images/debian/bullseye/amd64/

这里有defaultcloud两种选择,cloud镜像稍大,可能包含一些针对云额外配置,选择default就可以了,下载时选择rootfs.tar.xz即可。

预配置

下载后解压,默认的镜像root用户没有密码,同时IP也不是固定的,所以我们可以进行预配置:

mkdir lxc_debian
tar xvf rootfs.tar.xz -C lxc_debian

# 设置主机名
sed -i "s/LXC_NAME/your_hostname/" ./lxc_debian/etc/hostname ./lxc_debian/etc/hosts

# 网络配置
cat > ./lxc_debian/etc/systemd/network/eth0.network <<EOF
[Match]
Name=eth0
[Network]
Address=192.168.203.210/24
Gateway=192.168.203.1
DNS=223.5.5.5
DNS=223.6.6.6
EOF

# chroot
sudo chroot lxc_debian
(chroot_env) # passwd
(chroot_env) # exit

# 或者直接修改 /etc/shadow
# password: 0000
root:$y$j9T$2iElAGeU9fyGjzJWcHac01$ieVx2juZRDG8DORlyvuCstq0iI8/EVu9JrmsUmhekC.

导入Virt-manager

新建LXC连接:

221030093405.png

221030093521.png

新建容器:

221030093539.png

221030093600.png

选择Root目录:

221030093712.png

启动:

221030093841.png

前言

没有前言。

生成Payload

我们可以直接使用golang-jwt中的RegisteredClaims结构:

jwt.RegisteredClaims{
    // 发行人主题
    Issuer:    "",
    // 被签发者主题
    Subject:   "",
    // 用户标识符
    Audience:  nil,
    // 过期时间
    ExpiresAt: nil,
    // 不早于
    NotBefore: nil,
    // 签发时间
    IssuedAt:  nil,
    // JWT的唯一标识符
    ID:        "",
}

此部分参见:https://datatracker.ietf.org/doc/html/rfc7519#section-4.1

或者我们可以自定义一个结构体,只需要引用RegisteredClaims就行:

// 嵌套结构
type CustomInfo struct {
    Name string `json:"name"`
}

// 自定义 payload
type CustomTokenBody struct {
    *jwt.RegisteredClaims
    User CustomInfo `json:"user"`
    Role string
}

生成JWT

选择签署算法:

// HMAC_HS256_HS384_HS512
// RSA_RS256_RS384_RS512
// ECDSA_ES256_ES384_ES512
// EdDSA_Ed25519
signingMethodECSDA := jwt.GetSigningMethod("ES256")

新建JWT并签署Payload

token := jwt.New(signingMethodECSDA)

// payload
token.Claims = &CustomTokenBody{
    &jwt.RegisteredClaims{
        ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 10)),
    },
    CustomInfo{
        Name: "yeziruo",
    },
    "yeziruo",
}
// 使用私钥签署
// key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
signedString, err := token.SignedString(key)
if err != nil {
    panic(err)
}
fmt.println(signedString)

最后生成:

eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjYxNTk2NjMsInVzZXIiOnsibmFtZSI6InllemlydW8ifSwiUm9sZSI6InllemlydW8ifQ.mCatXHq0ooh49Eusr2aWLJKwwy6K_zogDBVCTc3eBzY_ygePVnFL78QpzkGeuCCyhtjdobuKGx0VxBs6PlYrQA
//payload:{"exp":1666159663,"user":{"name":"yeziruo"},"Role":"yeziruo"}

解析JWT

共有三种解析方法:

// 解析并验证
jwt.Parse
// 仅解析
jwt.ParseUnverified
// 解析并验证
jwt.ParseWithClaims

ParseParseWithClaims有一定区别:

// 解析JWT
parse, err := jwt.Parse(signedString, func(token *jwt.Token) (interface{}, error) {
    // 如果你有多个私钥用于签名,那么可以在此通过 kid 来选择相应的密钥
    // 这里获取的 token.Claims 是一个 map
    // 返回公钥
    return key.Public(), nil
})

parse, err = jwt.ParseWithClaims(signedString, &CustomTokenBody{}, func(token *jwt.Token) (interface{}, error) {
    // 这里获取的 token.Claims 会被填充到指定的结构体
    return key.Public(), nil
})

// err 可以返回验证失败原因
// fmt.println(err)
fmt.Println(parse.Valid)

将其作为Gin中间件

func JWTAuth(priKey *ecdsa.PrivateKey) gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        parse, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
            return priKey.Public(), nil
        })
        if err != nil || !parse.Valid {
            msg := "token verification failed"
            if err != nil {
                msg = err.Error()
            }
            c.JSON(http.StatusUnauthorized, gin.H{
                "code":    http.StatusUnauthorized,
                "message": msg,
            })
            c.Abort()
        } else {
            c.Set("jwt", parse)
            c.Next()
        }
    }
}

2219143001.png

参见

https://datatracker.ietf.org/doc/html/rfc7519
https://pkg.go.dev/github.com/golang-jwt/jwt