diff --git a/middleware/hahsid.go b/middleware/option.go
similarity index 59%
rename from middleware/hahsid.go
rename to middleware/option.go
index f66d945..a4c6fcd 100644
--- a/middleware/hahsid.go
+++ b/middleware/option.go
@@ -1,6 +1,7 @@
 package middleware
 
 import (
+	model "github.com/HFO4/cloudreve/models"
 	"github.com/HFO4/cloudreve/pkg/hashid"
 	"github.com/HFO4/cloudreve/pkg/serializer"
 	"github.com/gin-gonic/gin"
@@ -24,3 +25,16 @@ func HashID(IDType int) gin.HandlerFunc {
 		c.Next()
 	}
 }
+
+// IsFunctionEnabled 当功能未开启时阻止访问
+func IsFunctionEnabled(key string) gin.HandlerFunc {
+	return func(c *gin.Context) {
+		if !model.IsTrueVal(model.GetSettingByName(key)) {
+			c.JSON(200, serializer.Err(serializer.CodeNoPermissionErr, "未开启此功能", nil))
+			c.Abort()
+			return
+		}
+
+		c.Next()
+	}
+}
diff --git a/models/migration.go b/models/migration.go
index daca13a..5c3cd6b 100644
--- a/models/migration.go
+++ b/models/migration.go
@@ -173,6 +173,7 @@ Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; verti
 		{Name: "cron_garbage_collect", Value: "@hourly", Type: "cron"},
 		{Name: "cron_notify_user", Value: "@hourly", Type: "cron"},
 		{Name: "cron_ban_user", Value: "@hourly", Type: "cron"},
+		{Name: "authn_enabled", Value: "1", Type: "authn"},
 	}
 
 	for _, value := range defaultSettings {
diff --git a/models/user_authn.go b/models/user_authn.go
index 99c09a1..d9b2be5 100644
--- a/models/user_authn.go
+++ b/models/user_authn.go
@@ -1,10 +1,13 @@
 package model
 
 import (
+	"encoding/base64"
 	"encoding/binary"
 	"encoding/json"
 	"fmt"
+	"github.com/HFO4/cloudreve/pkg/hashid"
 	"github.com/duo-labs/webauthn/webauthn"
+	"net/url"
 )
 
 /*
@@ -30,7 +33,10 @@ func (user User) WebAuthnDisplayName() string {
 
 // WebAuthnIcon 获得用户头像
 func (user User) WebAuthnIcon() string {
-	return "https://cdn4.buysellads.net/uu/1/46074/1559075156-slack-carbon-red_2x.png"
+	avatar, _ := url.Parse("/api/v3/user/avatar/" + hashid.HashID(user.ID, hashid.UserID) + "/l")
+	base := GetSiteURL()
+	base.Scheme = "https"
+	return base.ResolveReference(avatar).String()
 }
 
 // WebAuthnCredentials 获得已注册的验证器凭证
@@ -44,10 +50,29 @@ func (user User) WebAuthnCredentials() []webauthn.Credential {
 }
 
 // RegisterAuthn 添加新的验证器
-func (user *User) RegisterAuthn(credential *webauthn.Credential) {
-	res, err := json.Marshal([]webauthn.Credential{*credential})
+func (user *User) RegisterAuthn(credential *webauthn.Credential) error {
+	exists := user.WebAuthnCredentials()
+	exists = append(exists, *credential)
+	res, err := json.Marshal(exists)
 	if err != nil {
-		fmt.Println(err)
+		return err
 	}
+
+	return DB.Model(user).Update("authn", string(res)).Error
+}
+
+// RemoveAuthn 删除验证器
+func (user *User) RemoveAuthn(id string) {
+	exists := user.WebAuthnCredentials()
+	for i := 0; i < len(exists); i++ {
+		idEncoded := base64.StdEncoding.EncodeToString(exists[i].ID)
+		if idEncoded == id {
+			exists[len(exists)-1], exists[i] = exists[i], exists[len(exists)-1]
+			exists = exists[:len(exists)-1]
+			break
+		}
+	}
+
+	res, _ := json.Marshal(exists)
 	DB.Model(user).Update("authn", string(res))
 }
diff --git a/pkg/authn/auth.go b/pkg/authn/auth.go
index 5b4187a..7cd36a7 100644
--- a/pkg/authn/auth.go
+++ b/pkg/authn/auth.go
@@ -1,21 +1,23 @@
 package authn
 
 import (
-	"fmt"
+	model "github.com/HFO4/cloudreve/models"
+	"github.com/HFO4/cloudreve/pkg/util"
 	"github.com/duo-labs/webauthn/webauthn"
 )
 
 var AuthnInstance *webauthn.WebAuthn
 
+// Init 初始化webauthn
 func Init() {
 	var err error
+	base := model.GetSiteURL()
 	AuthnInstance, err = webauthn.New(&webauthn.Config{
-		RPDisplayName: "Duo Labs",                 // Display Name for your site
-		RPID:          "localhost",                // Generally the FQDN for your site
-		RPOrigin:      "http://localhost:3000",    // The origin URL for WebAuthn requests
-		RPIcon:        "https://duo.com/logo.png", // Optional icon URL for your site
+		RPDisplayName: model.GetSettingByName("siteName"), // Display Name for your site
+		RPID:          base.Hostname(),                    // Generally the FQDN for your site
+		RPOrigin:      base.String(),                      // The origin URL for WebAuthn requests
 	})
 	if err != nil {
-		fmt.Println(err)
+		util.Log().Error("无法初始化WebAuthn, %s", err)
 	}
 }
diff --git a/pkg/serializer/setting.go b/pkg/serializer/setting.go
index 0a0453d..f39528d 100644
--- a/pkg/serializer/setting.go
+++ b/pkg/serializer/setting.go
@@ -16,6 +16,7 @@ type SiteConfig struct {
 	ShareScoreRate     string `json:"share_score_rate"`
 	HomepageViewMethod string `json:"home_view_method"`
 	ShareViewMethod    string `json:"share_view_method"`
+	Authn              bool   `json:"authn"'`
 	User               User   `json:"user"`
 }
 
@@ -75,6 +76,7 @@ func BuildSiteConfig(settings map[string]string, user *model.User) Response {
 			ShareScoreRate:     checkSettingValue(settings, "share_score_rate"),
 			HomepageViewMethod: checkSettingValue(settings, "home_view_method"),
 			ShareViewMethod:    checkSettingValue(settings, "share_view_method"),
+			Authn:              model.IsTrueVal(checkSettingValue(settings, "authn_enabled")),
 			User:               userRes,
 		}}
 	return res
diff --git a/pkg/serializer/user.go b/pkg/serializer/user.go
index 3852d55..6de6e11 100644
--- a/pkg/serializer/user.go
+++ b/pkg/serializer/user.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"github.com/HFO4/cloudreve/models"
 	"github.com/HFO4/cloudreve/pkg/hashid"
+	"github.com/duo-labs/webauthn/webauthn"
 )
 
 // CheckLogin 检查登录
@@ -64,6 +65,26 @@ type storage struct {
 	Total uint64 `json:"total"`
 }
 
+// WebAuthnCredentials 外部验证器凭证
+type WebAuthnCredentials struct {
+	ID          []byte `json:"id"`
+	FingerPrint string `json:"fingerprint"`
+}
+
+// BuildWebAuthnList 构建设置页面凭证列表
+func BuildWebAuthnList(credentials []webauthn.Credential) []WebAuthnCredentials {
+	res := make([]WebAuthnCredentials, 0, len(credentials))
+	for _, v := range credentials {
+		credential := WebAuthnCredentials{
+			ID:          v.ID,
+			FingerPrint: fmt.Sprintf("% X", v.Authenticator.AAGUID),
+		}
+		res = append(res, credential)
+	}
+
+	return res
+}
+
 // BuildUser 序列化用户
 func BuildUser(user model.User) User {
 	tags, _ := model.GetTagsByUID(user.ID)
diff --git a/routers/controllers/site.go b/routers/controllers/site.go
index a3d64b7..b7507c6 100644
--- a/routers/controllers/site.go
+++ b/routers/controllers/site.go
@@ -25,6 +25,7 @@ func SiteConfig(c *gin.Context) {
 		"share_score_rate",
 		"home_view_method",
 		"share_view_method",
+		"authn_enabled",
 	)
 
 	// 如果已登录,则同时返回用户信息和标签
diff --git a/routers/controllers/user.go b/routers/controllers/user.go
index 7f15f2b..d9b2911 100644
--- a/routers/controllers/user.go
+++ b/routers/controllers/user.go
@@ -2,6 +2,7 @@ package controllers
 
 import (
 	"encoding/json"
+	"fmt"
 	model "github.com/HFO4/cloudreve/models"
 	"github.com/HFO4/cloudreve/pkg/authn"
 	"github.com/HFO4/cloudreve/pkg/serializer"
@@ -17,7 +18,7 @@ func StartLoginAuthn(c *gin.Context) {
 	userName := c.Param("username")
 	expectedUser, err := model.GetUserByEmail(userName)
 	if err != nil {
-		c.JSON(200, serializer.Err(401, "用户邮箱或密码错误", err))
+		c.JSON(200, serializer.Err(401, "用户不存在", err))
 		return
 	}
 
@@ -56,7 +57,7 @@ func FinishLoginAuthn(c *gin.Context) {
 	_, err = authn.AuthnInstance.FinishLogin(expectedUser, sessionData, c.Request)
 
 	if err != nil {
-		c.JSON(200, serializer.Err(401, "用户邮箱或密码错误", err))
+		c.JSON(200, serializer.Err(401, "登录验证失败", err))
 		return
 	}
 
@@ -96,13 +97,24 @@ func FinishRegAuthn(c *gin.Context) {
 	err := json.Unmarshal(sessionDataJSON, &sessionData)
 
 	credential, err := authn.AuthnInstance.FinishRegistration(currUser, sessionData, c.Request)
-
-	currUser.RegisterAuthn(credential)
 	if err != nil {
 		c.JSON(200, ErrorResponse(err))
 		return
 	}
-	c.JSON(200, serializer.Response{Code: 0})
+
+	err = currUser.RegisterAuthn(credential)
+	if err != nil {
+		c.JSON(200, ErrorResponse(err))
+		return
+	}
+
+	c.JSON(200, serializer.Response{
+		Code: 0,
+		Data: map[string]interface{}{
+			"id":          credential.ID,
+			"fingerprint": fmt.Sprintf("% X", credential.Authenticator.AAGUID),
+		},
+	})
 }
 
 // UserLogin 用户登录
@@ -265,6 +277,10 @@ func UpdateOption(c *gin.Context) {
 			subService = &user.PasswordChange{}
 		case "2fa":
 			subService = &user.Enable2FA{}
+		case "authn":
+			subService = &user.DeleteWebAuthn{}
+		case "theme":
+			subService = &user.ThemeChose{}
 		}
 
 		subErr = c.ShouldBindJSON(subService)
diff --git a/routers/router.go b/routers/router.go
index fc891c3..7dc37f2 100644
--- a/routers/router.go
+++ b/routers/router.go
@@ -104,9 +104,13 @@ func InitMasterRouter() *gin.Engine {
 			// 用户登录
 			user.POST("session", controllers.UserLogin)
 			// WebAuthn登陆初始化
-			user.GET("authn/:username", controllers.StartLoginAuthn)
+			user.GET("authn/:username",
+				middleware.IsFunctionEnabled("authn_enabled"),
+				controllers.StartLoginAuthn)
 			// WebAuthn登陆
-			user.POST("authn/finish/:username", controllers.FinishLoginAuthn)
+			user.POST("authn/finish/:username",
+				middleware.IsFunctionEnabled("authn_enabled"),
+				controllers.FinishLoginAuthn)
 			// 获取用户主页展示用分享
 			user.GET("profile/:id",
 				middleware.HashID(hashid.UserID),
@@ -263,7 +267,8 @@ func InitMasterRouter() *gin.Engine {
 				user.DELETE("session", controllers.UserSignOut)
 
 				// WebAuthn 注册相关
-				authn := user.Group("authn")
+				authn := user.Group("authn",
+					middleware.IsFunctionEnabled("authn_enabled"))
 				{
 					authn.PUT("", controllers.StartRegAuthn)
 					authn.PUT("finish", controllers.FinishRegAuthn)
diff --git a/service/user/setting.go b/service/user/setting.go
index 6ccb7fc..9bfd803 100644
--- a/service/user/setting.go
+++ b/service/user/setting.go
@@ -33,7 +33,7 @@ type AvatarService struct {
 
 // SettingUpdateService 设定更改服务
 type SettingUpdateService struct {
-	Option string `uri:"option" binding:"required,eq=nick|eq=theme|eq=homepage|eq=vip|eq=qq|eq=policy|eq=password|eq=2fa"`
+	Option string `uri:"option" binding:"required,eq=nick|eq=theme|eq=homepage|eq=vip|eq=qq|eq=policy|eq=password|eq=2fa|eq=authn"`
 }
 
 // OptionsChangeHandler 属性更改接口
@@ -75,10 +75,36 @@ type Enable2FA struct {
 	Code string `json:"code" binding:"required"`
 }
 
-// Update 更改密码
+// DeleteWebAuthn 删除WebAuthn凭证
+type DeleteWebAuthn struct {
+	ID string `json:"id" binding:"required"`
+}
+
+// ThemeChose 主题选择
+type ThemeChose struct {
+	Theme string `json:"theme" binding:"required,hexcolor|rgb|rgba|hsl"`
+}
+
+// Update 更新主题设定
+func (service *ThemeChose) Update(c *gin.Context, user *model.User) serializer.Response {
+	user.OptionsSerialized.PreferredTheme = service.Theme
+	if err := user.UpdateOptions(); err != nil {
+		return serializer.DBErr("主题切换失败", err)
+	}
+
+	return serializer.Response{}
+}
+
+// Update 删除凭证
+func (service *DeleteWebAuthn) Update(c *gin.Context, user *model.User) serializer.Response {
+	user.RemoveAuthn(service.ID)
+	return serializer.Response{}
+}
+
+// Update 更改二步验证设定
 func (service *Enable2FA) Update(c *gin.Context, user *model.User) serializer.Response {
 	if user.TwoFactor == "" {
-
+		// 开启2FA
 		secret, ok := util.GetSession(c, "2fa_init").(string)
 		if !ok {
 			return serializer.Err(serializer.CodeParamErr, "未初始化二步验证", nil)
@@ -92,6 +118,15 @@ func (service *Enable2FA) Update(c *gin.Context, user *model.User) serializer.Re
 			return serializer.DBErr("无法更新二步验证设定", err)
 		}
 
+	} else {
+		// 关闭2FA
+		if !totp.Validate(service.Code, user.TwoFactor) {
+			return serializer.ParamErr("验证码不正确", nil)
+		}
+
+		if err := user.Update(map[string]interface{}{"two_factor": ""}); err != nil {
+			return serializer.DBErr("无法更新二步验证设定", err)
+		}
 	}
 
 	return serializer.Response{}
@@ -318,8 +353,9 @@ func (service *SettingService) Settings(c *gin.Context, user *model.User) serial
 			"homepage":      !user.OptionsSerialized.ProfileOff,
 			"two_factor":    user.TwoFactor != "",
 			"prefer_theme":  user.OptionsSerialized.PreferredTheme,
-			"themes":        model.GetSettingByNames("themes"),
+			"themes":        model.GetSettingByName("themes"),
 			"group_expires": groupExpires,
+			"authn":         serializer.BuildWebAuthnList(user.WebAuthnCredentials()),
 		},
 	}
 }