mirror of
https://github.com/casdoor/casdoor.git
synced 2025-05-23 02:35:49 +08:00
Make cert work.
This commit is contained in:
parent
ba1646a0c3
commit
5e8897e41b
@ -28,7 +28,7 @@ func (c *RootController) GetOidcDiscovery() {
|
||||
// @Tag OIDC API
|
||||
// @router /api/certs [get]
|
||||
func (c *RootController) GetOidcCert() {
|
||||
jwks, err := object.GetJSONWebKeySet()
|
||||
jwks, err := object.GetJsonWebKeySet()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
|
@ -31,6 +31,7 @@ type Application struct {
|
||||
HomepageUrl string `xorm:"varchar(100)" json:"homepageUrl"`
|
||||
Description string `xorm:"varchar(100)" json:"description"`
|
||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||
Cert string `xorm:"varchar(100)" json:"cert"`
|
||||
EnablePassword bool `json:"enablePassword"`
|
||||
EnableSignUp bool `json:"enableSignUp"`
|
||||
EnableSigninSession bool `json:"enableSigninSession"`
|
||||
|
@ -124,6 +124,12 @@ func UpdateCert(id string, cert *Cert) bool {
|
||||
}
|
||||
|
||||
func AddCert(cert *Cert) bool {
|
||||
if cert.PublicKey == "" || cert.PrivateKey == "" {
|
||||
publicKey, privateKey := generateRsaKeys(cert.BitSize, cert.ExpireInYears, cert.Name, cert.Owner)
|
||||
cert.PublicKey = publicKey
|
||||
cert.PrivateKey = privateKey
|
||||
}
|
||||
|
||||
affected, err := adapter.Engine.Insert(cert)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -144,3 +150,15 @@ func DeleteCert(cert *Cert) bool {
|
||||
func (p *Cert) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", p.Owner, p.Name)
|
||||
}
|
||||
|
||||
func getCertByApplication(application *Application) *Cert {
|
||||
if application.Cert != "" {
|
||||
return getCert("admin", application.Cert)
|
||||
} else {
|
||||
return GetDefaultCert()
|
||||
}
|
||||
}
|
||||
|
||||
func GetDefaultCert() *Cert {
|
||||
return getCert("admin", "cert-built-in")
|
||||
}
|
||||
|
@ -14,12 +14,23 @@
|
||||
|
||||
package object
|
||||
|
||||
import "github.com/casbin/casdoor/util"
|
||||
import (
|
||||
_ "embed"
|
||||
|
||||
"github.com/casbin/casdoor/util"
|
||||
)
|
||||
|
||||
//go:embed token_jwt_key.pem
|
||||
var tokenJwtPublicKey string
|
||||
|
||||
//go:embed token_jwt_key.key
|
||||
var tokenJwtPrivateKey string
|
||||
|
||||
func InitDb() {
|
||||
initBuiltInOrganization()
|
||||
initBuiltInUser()
|
||||
initBuiltInApplication()
|
||||
initBuiltInCert()
|
||||
initBuiltInLdap()
|
||||
}
|
||||
|
||||
@ -90,6 +101,7 @@ func initBuiltInApplication() {
|
||||
Logo: "https://cdn.casbin.com/logo/logo_1024x256.png",
|
||||
HomepageUrl: "https://casdoor.org",
|
||||
Organization: "built-in",
|
||||
Cert: "cert-built-in",
|
||||
EnablePassword: true,
|
||||
EnableSignUp: true,
|
||||
Providers: []*ProviderItem{},
|
||||
@ -109,6 +121,28 @@ func initBuiltInApplication() {
|
||||
AddApplication(application)
|
||||
}
|
||||
|
||||
func initBuiltInCert() {
|
||||
cert := getCert("admin", "cert-built-in")
|
||||
if cert != nil {
|
||||
return
|
||||
}
|
||||
|
||||
cert = &Cert{
|
||||
Owner: "admin",
|
||||
Name: "cert-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: "Built-in Cert",
|
||||
Scope: "JWT",
|
||||
Type: "x509",
|
||||
CryptoAlgorithm: "RSA",
|
||||
BitSize: 4096,
|
||||
ExpireInYears: 20,
|
||||
PublicKey: tokenJwtPublicKey,
|
||||
PrivateKey: tokenJwtPrivateKey,
|
||||
}
|
||||
AddCert(cert)
|
||||
}
|
||||
|
||||
func initBuiltInLdap() {
|
||||
ldap := GetLdap("ldap-built-in")
|
||||
if ldap != nil {
|
||||
|
@ -72,13 +72,15 @@ func GetOidcDiscovery() OidcDiscovery {
|
||||
return oidcDiscovery
|
||||
}
|
||||
|
||||
func GetJSONWebKeySet() (jose.JSONWebKeySet, error) {
|
||||
func GetJsonWebKeySet() (jose.JSONWebKeySet, error) {
|
||||
cert := GetDefaultCert()
|
||||
|
||||
//follows the protocol rfc 7517(draft)
|
||||
//link here: https://self-issued.info/docs/draft-ietf-jose-json-web-key.html
|
||||
//or https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-key
|
||||
certPEMBlock := []byte(tokenJwtPublicKey)
|
||||
certDERBlock, _ := pem.Decode(certPEMBlock)
|
||||
x509Cert, _ := x509.ParseCertificate(certDERBlock.Bytes)
|
||||
certPemBlock := []byte(cert.PublicKey)
|
||||
certDerBlock, _ := pem.Decode(certPemBlock)
|
||||
x509Cert, _ := x509.ParseCertificate(certDerBlock.Bytes)
|
||||
|
||||
var jwk jose.JSONWebKey
|
||||
jwk.Key = x509Cert.PublicKey
|
||||
|
@ -332,7 +332,9 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
||||
Code: "",
|
||||
}
|
||||
}
|
||||
claims, err := ParseJwtToken(refreshToken)
|
||||
|
||||
cert := getCertByApplication(application)
|
||||
claims, err := ParseJwtToken(refreshToken, cert)
|
||||
if err != nil {
|
||||
return &Code{
|
||||
Message: "error: invalid refresh_token",
|
||||
|
@ -23,12 +23,6 @@ import (
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
)
|
||||
|
||||
//go:embed token_jwt_key.pem
|
||||
var tokenJwtPublicKey string
|
||||
|
||||
//go:embed token_jwt_key.key
|
||||
var tokenJwtPrivateKey string
|
||||
|
||||
type Claims struct {
|
||||
*User
|
||||
Name string `json:"name,omitempty"`
|
||||
@ -68,9 +62,10 @@ func generateJwtToken(application *Application, user *User, nonce string) (strin
|
||||
claims.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
|
||||
refreshToken := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||
|
||||
// Use "token_jwt_key.key" as RSA private key
|
||||
privateKey := tokenJwtPrivateKey
|
||||
key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(privateKey))
|
||||
cert := getCertByApplication(application)
|
||||
|
||||
// RSA private key
|
||||
key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(cert.PrivateKey))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
@ -84,14 +79,14 @@ func generateJwtToken(application *Application, user *User, nonce string) (strin
|
||||
return tokenString, refreshTokenString, err
|
||||
}
|
||||
|
||||
func ParseJwtToken(token string) (*Claims, error) {
|
||||
func ParseJwtToken(token string, cert *Cert) (*Claims, error) {
|
||||
t, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
|
||||
// Use "token_jwt_key.pem" as RSA public key
|
||||
publicKey, err := jwt.ParseRSAPublicKeyFromPEM([]byte(tokenJwtPublicKey))
|
||||
// RSA public key
|
||||
publicKey, err := jwt.ParseRSAPublicKeyFromPEM([]byte(cert.PublicKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -20,19 +20,14 @@ import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/casbin/casdoor/util"
|
||||
)
|
||||
|
||||
func generateRsaKeys(fileId string) {
|
||||
func generateRsaKeys(bitSize int, expireInYears int, commonName string, organization string) (string, string) {
|
||||
// https://stackoverflow.com/questions/64104586/use-golang-to-get-rsa-key-the-same-way-openssl-genrsa
|
||||
// https://stackoverflow.com/questions/43822945/golang-can-i-create-x509keypair-using-rsa-key
|
||||
|
||||
bitSize := 4096
|
||||
|
||||
// Generate RSA key.
|
||||
key, err := rsa.GenerateKey(rand.Reader, bitSize)
|
||||
if err != nil {
|
||||
@ -50,12 +45,12 @@ func generateRsaKeys(fileId string) {
|
||||
tml := x509.Certificate{
|
||||
// you can add any attr that you need
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(20, 0, 0),
|
||||
NotAfter: time.Now().AddDate(expireInYears, 0, 0),
|
||||
// you have to generate a different serial number each execution
|
||||
SerialNumber: big.NewInt(123456),
|
||||
Subject: pkix.Name{
|
||||
CommonName: "Casdoor Cert",
|
||||
Organization: []string{"Casdoor Organization"},
|
||||
CommonName: commonName,
|
||||
Organization: []string{organization},
|
||||
},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
@ -70,9 +65,5 @@ func generateRsaKeys(fileId string) {
|
||||
Bytes: cert,
|
||||
})
|
||||
|
||||
// Write private key to file.
|
||||
util.WriteBytesToPath(privateKeyPem, fmt.Sprintf("%s.key", fileId))
|
||||
|
||||
// Write certificate (aka public key) to file.
|
||||
util.WriteBytesToPath(certPem, fmt.Sprintf("%s.pem", fileId))
|
||||
return string(certPem), string(privateKeyPem)
|
||||
}
|
||||
|
@ -14,9 +14,20 @@
|
||||
|
||||
package object
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/casbin/casdoor/util"
|
||||
)
|
||||
|
||||
func TestGenerateRsaKeys(t *testing.T) {
|
||||
fileId := "token_jwt_key"
|
||||
generateRsaKeys(fileId)
|
||||
publicKey, privateKey := generateRsaKeys(4096, 20, "Casdoor Cert", "Casdoor Organization")
|
||||
|
||||
// Write certificate (aka public key) to file.
|
||||
util.WriteStringToPath(publicKey, fmt.Sprintf("%s.pem", fileId))
|
||||
|
||||
// Write private key to file.
|
||||
util.WriteStringToPath(privateKey, fmt.Sprintf("%s.key", fileId))
|
||||
}
|
||||
|
@ -31,7 +31,8 @@ func AutoSigninFilter(ctx *context.Context) {
|
||||
// "/page?access_token=123"
|
||||
accessToken := ctx.Input.Query("accessToken")
|
||||
if accessToken != "" {
|
||||
claims, err := object.ParseJwtToken(accessToken)
|
||||
cert := object.GetDefaultCert()
|
||||
claims, err := object.ParseJwtToken(accessToken, cert)
|
||||
if err != nil {
|
||||
responseError(ctx, "invalid JWT token")
|
||||
return
|
||||
@ -71,7 +72,8 @@ func AutoSigninFilter(ctx *context.Context) {
|
||||
// Authorization: Bearer bearerToken
|
||||
bearerToken := parseBearerToken(ctx)
|
||||
if bearerToken != "" {
|
||||
claims, err := object.ParseJwtToken(bearerToken)
|
||||
cert := object.GetDefaultCert()
|
||||
claims, err := object.ParseJwtToken(bearerToken, cert)
|
||||
if err != nil {
|
||||
responseError(ctx, err.Error())
|
||||
return
|
||||
|
@ -137,7 +137,7 @@ class UserListPage extends BaseListPage {
|
||||
title: i18next.t("general:Name"),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: (Setting.isMobile()) ? "80px" : "100px",
|
||||
width: (Setting.isMobile()) ? "80px" : "110px",
|
||||
fixed: 'left',
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps('name'),
|
||||
|
@ -33,6 +33,29 @@
|
||||
"Token format": "Token format",
|
||||
"Token format - Tooltip": "Token format - Tooltip"
|
||||
},
|
||||
"cert": {
|
||||
"Bit size": "Bit size",
|
||||
"Bit size - Tooltip": "Bit size - Tooltip",
|
||||
"Copy private key": "Copy private key",
|
||||
"Copy public key": "Copy public key",
|
||||
"Crypto algorithm": "Crypto algorithm",
|
||||
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
|
||||
"Download private key": "Download private key",
|
||||
"Download public key": "Download public key",
|
||||
"Edit Cert": "Edit Cert",
|
||||
"Expire in years": "Expire in years",
|
||||
"Expire in years - Tooltip": "Expire in years - Tooltip",
|
||||
"Private key": "Private key",
|
||||
"Private key - Tooltip": "Private key - Tooltip",
|
||||
"Private key copied to clipboard successfully": "Private key copied to clipboard successfully",
|
||||
"Public key": "Public key",
|
||||
"Public key - Tooltip": "Public key - Tooltip",
|
||||
"Public key copied to clipboard successfully": "Public key copied to clipboard successfully",
|
||||
"Scope": "Scope",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Type": "Type",
|
||||
"Type - Tooltip": "Type - Tooltip"
|
||||
},
|
||||
"code": {
|
||||
"Code You Received": "Code You Received",
|
||||
"Email code": "Email code",
|
||||
@ -69,6 +92,7 @@
|
||||
"Avatar - Tooltip": "Avatar to show to others",
|
||||
"Back Home": "Back Home",
|
||||
"Captcha": "Captcha",
|
||||
"Certs": "Certs",
|
||||
"Client IP": "Client IP",
|
||||
"Created time": "Created time",
|
||||
"Default avatar": "Default avatar",
|
||||
|
@ -33,6 +33,29 @@
|
||||
"Token format": "Token format",
|
||||
"Token format - Tooltip": "Token format - Tooltip"
|
||||
},
|
||||
"cert": {
|
||||
"Bit size": "Bit size",
|
||||
"Bit size - Tooltip": "Bit size - Tooltip",
|
||||
"Copy private key": "Copy private key",
|
||||
"Copy public key": "Copy public key",
|
||||
"Crypto algorithm": "Crypto algorithm",
|
||||
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
|
||||
"Download private key": "Download private key",
|
||||
"Download public key": "Download public key",
|
||||
"Edit Cert": "Edit Cert",
|
||||
"Expire in years": "Expire in years",
|
||||
"Expire in years - Tooltip": "Expire in years - Tooltip",
|
||||
"Private key": "Private key",
|
||||
"Private key - Tooltip": "Private key - Tooltip",
|
||||
"Private key copied to clipboard successfully": "Private key copied to clipboard successfully",
|
||||
"Public key": "Public key",
|
||||
"Public key - Tooltip": "Public key - Tooltip",
|
||||
"Public key copied to clipboard successfully": "Public key copied to clipboard successfully",
|
||||
"Scope": "Scope",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Type": "Type",
|
||||
"Type - Tooltip": "Type - Tooltip"
|
||||
},
|
||||
"code": {
|
||||
"Code You Received": "Code You Received",
|
||||
"Email code": "Email code",
|
||||
@ -69,6 +92,7 @@
|
||||
"Avatar - Tooltip": "Avatar - Tooltip",
|
||||
"Back Home": "Back Home",
|
||||
"Captcha": "Captcha",
|
||||
"Certs": "Certs",
|
||||
"Client IP": "Client IP",
|
||||
"Created time": "Created time",
|
||||
"Default avatar": "Default avatar",
|
||||
|
@ -33,6 +33,29 @@
|
||||
"Token format": "Token format",
|
||||
"Token format - Tooltip": "Token format - Tooltip"
|
||||
},
|
||||
"cert": {
|
||||
"Bit size": "Bit size",
|
||||
"Bit size - Tooltip": "Bit size - Tooltip",
|
||||
"Copy private key": "Copy private key",
|
||||
"Copy public key": "Copy public key",
|
||||
"Crypto algorithm": "Crypto algorithm",
|
||||
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
|
||||
"Download private key": "Download private key",
|
||||
"Download public key": "Download public key",
|
||||
"Edit Cert": "Edit Cert",
|
||||
"Expire in years": "Expire in years",
|
||||
"Expire in years - Tooltip": "Expire in years - Tooltip",
|
||||
"Private key": "Private key",
|
||||
"Private key - Tooltip": "Private key - Tooltip",
|
||||
"Private key copied to clipboard successfully": "Private key copied to clipboard successfully",
|
||||
"Public key": "Public key",
|
||||
"Public key - Tooltip": "Public key - Tooltip",
|
||||
"Public key copied to clipboard successfully": "Public key copied to clipboard successfully",
|
||||
"Scope": "Scope",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Type": "Type",
|
||||
"Type - Tooltip": "Type - Tooltip"
|
||||
},
|
||||
"code": {
|
||||
"Code You Received": "Code You Received",
|
||||
"Email code": "Email code",
|
||||
@ -69,6 +92,7 @@
|
||||
"Avatar - Tooltip": "Avatar to show to others",
|
||||
"Back Home": "Back Home",
|
||||
"Captcha": "Captcha",
|
||||
"Certs": "Certs",
|
||||
"Client IP": "Client IP",
|
||||
"Created time": "Created time",
|
||||
"Default avatar": "Default avatar",
|
||||
|
@ -33,6 +33,29 @@
|
||||
"Token format": "Token format",
|
||||
"Token format - Tooltip": "Token format - Tooltip"
|
||||
},
|
||||
"cert": {
|
||||
"Bit size": "Bit size",
|
||||
"Bit size - Tooltip": "Bit size - Tooltip",
|
||||
"Copy private key": "Copy private key",
|
||||
"Copy public key": "Copy public key",
|
||||
"Crypto algorithm": "Crypto algorithm",
|
||||
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
|
||||
"Download private key": "Download private key",
|
||||
"Download public key": "Download public key",
|
||||
"Edit Cert": "Edit Cert",
|
||||
"Expire in years": "Expire in years",
|
||||
"Expire in years - Tooltip": "Expire in years - Tooltip",
|
||||
"Private key": "Private key",
|
||||
"Private key - Tooltip": "Private key - Tooltip",
|
||||
"Private key copied to clipboard successfully": "Private key copied to clipboard successfully",
|
||||
"Public key": "Public key",
|
||||
"Public key - Tooltip": "Public key - Tooltip",
|
||||
"Public key copied to clipboard successfully": "Public key copied to clipboard successfully",
|
||||
"Scope": "Scope",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Type": "Type",
|
||||
"Type - Tooltip": "Type - Tooltip"
|
||||
},
|
||||
"code": {
|
||||
"Code You Received": "Code You Received",
|
||||
"Email code": "Email code",
|
||||
@ -69,6 +92,7 @@
|
||||
"Avatar - Tooltip": "Avatar to show to others",
|
||||
"Back Home": "Back Home",
|
||||
"Captcha": "Captcha",
|
||||
"Certs": "Certs",
|
||||
"Client IP": "Client IP",
|
||||
"Created time": "Created time",
|
||||
"Default avatar": "Default avatar",
|
||||
|
@ -33,6 +33,29 @@
|
||||
"Token format": "Token format",
|
||||
"Token format - Tooltip": "Token format - Tooltip"
|
||||
},
|
||||
"cert": {
|
||||
"Bit size": "Bit size",
|
||||
"Bit size - Tooltip": "Bit size - Tooltip",
|
||||
"Copy private key": "Copy private key",
|
||||
"Copy public key": "Copy public key",
|
||||
"Crypto algorithm": "Crypto algorithm",
|
||||
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
|
||||
"Download private key": "Download private key",
|
||||
"Download public key": "Download public key",
|
||||
"Edit Cert": "Edit Cert",
|
||||
"Expire in years": "Expire in years",
|
||||
"Expire in years - Tooltip": "Expire in years - Tooltip",
|
||||
"Private key": "Private key",
|
||||
"Private key - Tooltip": "Private key - Tooltip",
|
||||
"Private key copied to clipboard successfully": "Private key copied to clipboard successfully",
|
||||
"Public key": "Public key",
|
||||
"Public key - Tooltip": "Public key - Tooltip",
|
||||
"Public key copied to clipboard successfully": "Public key copied to clipboard successfully",
|
||||
"Scope": "Scope",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Type": "Type",
|
||||
"Type - Tooltip": "Type - Tooltip"
|
||||
},
|
||||
"code": {
|
||||
"Code You Received": "Code You Received",
|
||||
"Email code": "Email code",
|
||||
@ -69,6 +92,7 @@
|
||||
"Avatar - Tooltip": "Avatar to show to others",
|
||||
"Back Home": "Back Home",
|
||||
"Captcha": "Captcha",
|
||||
"Certs": "Certs",
|
||||
"Client IP": "Client IP",
|
||||
"Created time": "Created time",
|
||||
"Default avatar": "Default avatar",
|
||||
|
@ -33,6 +33,29 @@
|
||||
"Token format": "Token format",
|
||||
"Token format - Tooltip": "Token format - Tooltip"
|
||||
},
|
||||
"cert": {
|
||||
"Bit size": "Bit size",
|
||||
"Bit size - Tooltip": "Bit size - Tooltip",
|
||||
"Copy private key": "Copy private key",
|
||||
"Copy public key": "Copy public key",
|
||||
"Crypto algorithm": "Crypto algorithm",
|
||||
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
|
||||
"Download private key": "Download private key",
|
||||
"Download public key": "Download public key",
|
||||
"Edit Cert": "Edit Cert",
|
||||
"Expire in years": "Expire in years",
|
||||
"Expire in years - Tooltip": "Expire in years - Tooltip",
|
||||
"Private key": "Private key",
|
||||
"Private key - Tooltip": "Private key - Tooltip",
|
||||
"Private key copied to clipboard successfully": "Private key copied to clipboard successfully",
|
||||
"Public key": "Public key",
|
||||
"Public key - Tooltip": "Public key - Tooltip",
|
||||
"Public key copied to clipboard successfully": "Public key copied to clipboard successfully",
|
||||
"Scope": "Scope",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Type": "Type",
|
||||
"Type - Tooltip": "Type - Tooltip"
|
||||
},
|
||||
"code": {
|
||||
"Code You Received": "Code You Received",
|
||||
"Email code": "Email code",
|
||||
@ -69,6 +92,7 @@
|
||||
"Avatar - Tooltip": "Avatar to show to others",
|
||||
"Back Home": "Back Home",
|
||||
"Captcha": "Captcha",
|
||||
"Certs": "Certs",
|
||||
"Client IP": "Client IP",
|
||||
"Created time": "Created time",
|
||||
"Default avatar": "Default avatar",
|
||||
|
@ -33,6 +33,29 @@
|
||||
"Token format": "Access Token格式",
|
||||
"Token format - Tooltip": "Access Token格式"
|
||||
},
|
||||
"cert": {
|
||||
"Bit size": "位大小",
|
||||
"Bit size - Tooltip": "Bit size - Tooltip",
|
||||
"Copy private key": "复制私钥",
|
||||
"Copy public key": "复制公钥",
|
||||
"Crypto algorithm": "加密算法",
|
||||
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
|
||||
"Download private key": "下载私钥",
|
||||
"Download public key": "下载公钥",
|
||||
"Edit Cert": "修改证书",
|
||||
"Expire in years": "有效期(年)",
|
||||
"Expire in years - Tooltip": "Expire in years - Tooltip",
|
||||
"Private key": "私钥",
|
||||
"Private key - Tooltip": "Private key - Tooltip",
|
||||
"Private key copied to clipboard successfully": "私钥已成功复制到剪贴板",
|
||||
"Public key": "公钥",
|
||||
"Public key - Tooltip": "Public key - Tooltip",
|
||||
"Public key copied to clipboard successfully": "公钥已成功复制到剪贴板",
|
||||
"Scope": "用途",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Type": "类型",
|
||||
"Type - Tooltip": "Type - Tooltip"
|
||||
},
|
||||
"code": {
|
||||
"Code You Received": "验证码",
|
||||
"Email code": "邮箱验证码",
|
||||
@ -69,6 +92,7 @@
|
||||
"Avatar - Tooltip": "向其他人展示的头像",
|
||||
"Back Home": "返回到首页",
|
||||
"Captcha": "人机验证码",
|
||||
"Certs": "证书",
|
||||
"Client IP": "客户端IP",
|
||||
"Created time": "创建时间",
|
||||
"Default avatar": "默认头像",
|
||||
|
Loading…
x
Reference in New Issue
Block a user