日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

sa-token快速添加多鑒權體系

作者:蒼穹之躍 更新時間: 2022-04-17 編程語言
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.fun.SaFunction;
import cn.dev33.satoken.jwt.StpLogicJwtForStateless;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;

import java.util.List;

/**
 * 對每個認證鑒權體系新建一個Stp...Util類,然后復制StpUtil類的全部內容,修改標注TODO的地方。
 */
public class StpUserUtil {
    /**
     * 賬號體系標識 TODO 這里改成自己的登錄標識(不同的賬號體系之間區分開)
     */
    public static final String TYPE = "user";    // 將 LoginType 從`login`改為`user`

    /**
     * TODO 這里選擇需要的模式
     * 底層的 StpLogic 對象【標準模式 new StpLogic(TYPE),jwt模式1:new StpLogicJwtForStyle(TYPE),jwt模式2:new StpLogicJwtForMix(TYPE),jwt模式3:new StpLogicJwtForStateless(TYPE)】
     */
    public static StpLogic stpLogic = new StpLogicJwtForStateless(TYPE) {
        // 首先自定義一個 Config 對象
        //TODO 詳細配置,查看SaTokenConfig類,內置了各種默認配置,如果要修改,如下set...即可。
        SaTokenConfig config = new SaTokenConfig()
                .setTokenName("Authorization")
                .setTimeout(2592000)
                .setIsLog(true)
                .setJwtSecretKey("mingyundeanpaiyehao");

        // 然后重寫 stpLogic 配置獲取方法
        @Override
        public SaTokenConfig getConfig() {
            return config;
        }
    };

    /**
     * 獲取當前 StpLogic 的賬號類型
     *
     * @return See Note
     */
    public static String getLoginType() {
        return stpLogic.getLoginType();
    }

    /**
     * 重置 StpLogic 對象
     *
     * @param stpLogic /
     */
    public static void setStpLogic(StpLogic stpLogic) {
        StpUtil.stpLogic = stpLogic;
        // 防止自定義 stpLogic 被覆蓋
        SaManager.putStpLogic(stpLogic);
    }


    // =================== 獲取token 相關 ===================

    /**
     * 返回token名稱
     *
     * @return 此StpLogic的token名稱
     */
    public static String getTokenName() {
        return stpLogic.getTokenName();
    }

    /**
     * 在當前會話寫入當前TokenValue
     *
     * @param tokenValue token值
     */
    public static void setTokenValue(String tokenValue) {
        stpLogic.setTokenValue(tokenValue);
    }

    /**
     * 在當前會話寫入當前TokenValue
     *
     * @param tokenValue    token值
     * @param cookieTimeout Cookie存活時間(秒)
     */
    public static void setTokenValue(String tokenValue, int cookieTimeout) {
        stpLogic.setTokenValue(tokenValue, cookieTimeout);
    }

    /**
     * 獲取當前TokenValue
     *
     * @return 當前tokenValue
     */
    public static String getTokenValue() {
        return stpLogic.getTokenValue();
    }

    /**
     * 獲取當前TokenValue (不裁剪前綴)
     *
     * @return /
     */
    public static String getTokenValueNotCut() {
        return stpLogic.getTokenValueNotCut();
    }

    /**
     * 獲取當前會話的Token信息
     *
     * @return token信息
     */
    public static SaTokenInfo getTokenInfo() {
        return stpLogic.getTokenInfo();
    }


    // =================== 登錄相關操作 ===================

    // --- 登錄

    /**
     * 會話登錄
     *
     * @param id 賬號id,建議的類型:(long | int | String)
     */
    public static void login(Object id) {
        stpLogic.login(id);
    }

    /**
     * 會話登錄,并指定登錄設備
     *
     * @param id     賬號id,建議的類型:(long | int | String)
     * @param device 設備標識
     */
    public static void login(Object id, String device) {
        stpLogic.login(id, device);
    }

    /**
     * 會話登錄,并指定是否 [記住我]
     *
     * @param id              賬號id,建議的類型:(long | int | String)
     * @param isLastingCookie 是否為持久Cookie
     */
    public static void login(Object id, boolean isLastingCookie) {
        stpLogic.login(id, isLastingCookie);
    }

    /**
     * 會話登錄,并指定所有登錄參數Model
     *
     * @param id         登錄id,建議的類型:(long | int | String)
     * @param loginModel 此次登錄的參數Model
     */
    public static void login(Object id, SaLoginModel loginModel) {
        stpLogic.login(id, loginModel);
    }

    /**
     * 創建指定賬號id的登錄會話
     *
     * @param id 登錄id,建議的類型:(long | int | String)
     * @return 返回會話令牌
     */
    public static String createLoginSession(Object id) {
        return stpLogic.createLoginSession(id);
    }

    /**
     * 創建指定賬號id的登錄會話
     *
     * @param id         登錄id,建議的類型:(long | int | String)
     * @param loginModel 此次登錄的參數Model
     * @return 返回會話令牌
     */
    public static String createLoginSession(Object id, SaLoginModel loginModel) {
        return stpLogic.createLoginSession(id, loginModel);
    }

    // --- 注銷

    /**
     * 會話注銷
     */
    public static void logout() {
        stpLogic.logout();
    }

    /**
     * 會話注銷,根據賬號id
     *
     * @param loginId 賬號id
     */
    public static void logout(Object loginId) {
        stpLogic.logout(loginId);
    }

    /**
     * 會話注銷,根據賬號id 和 設備標識
     *
     * @param loginId 賬號id
     * @param device  設備標識 (填null代表所有注銷設備)
     */
    public static void logout(Object loginId, String device) {
        stpLogic.logout(loginId, device);
    }

    /**
     * 會話注銷,根據指定 Token
     *
     * @param tokenValue 指定token
     */
    public static void logoutByTokenValue(String tokenValue) {
        stpLogic.logoutByTokenValue(tokenValue);
    }

    /**
     * 踢人下線,根據賬號id
     * 

當對方再次訪問系統時,會拋出NotLoginException異常,場景值=-5

* * @param loginId 賬號id */ public static void kickout(Object loginId) { stpLogic.kickout(loginId); } /** * 踢人下線,根據賬號id 和 設備標識 *

當對方再次訪問系統時,會拋出NotLoginException異常,場景值=-5

* * @param loginId 賬號id * @param device 設備標識 (填null代表踢出所有設備) */ public static void kickout(Object loginId, String device) { stpLogic.kickout(loginId, device); } /** * 踢人下線,根據指定 Token *

當對方再次訪問系統時,會拋出NotLoginException異常,場景值=-5

* * @param tokenValue 指定token */ public static void kickoutByTokenValue(String tokenValue) { stpLogic.kickoutByTokenValue(tokenValue); } /** * 頂人下線,根據賬號id 和 設備標識 *

當對方再次訪問系統時,會拋出NotLoginException異常,場景值=-4

* * @param loginId 賬號id * @param device 設備標識 (填null代表頂替所有設備) */ public static void replaced(Object loginId, String device) { stpLogic.replaced(loginId, device); } // 查詢相關 /** * 當前會話是否已經登錄 * * @return 是否已登錄 */ public static boolean isLogin() { return stpLogic.isLogin(); } /** * 檢驗當前會話是否已經登錄,如未登錄,則拋出異常 */ public static void checkLogin() { stpLogic.checkLogin(); } /** * 獲取當前會話賬號id, 如果未登錄,則拋出異常 * * @return 賬號id */ public static Object getLoginId() { return stpLogic.getLoginId(); } /** * 獲取當前會話賬號id, 如果未登錄,則返回默認值 * * @param 返回類型 * @param defaultValue 默認值 * @return 登錄id */ public static T getLoginId(T defaultValue) { return stpLogic.getLoginId(defaultValue); } /** * 獲取當前會話賬號id, 如果未登錄,則返回null * * @return 賬號id */ public static Object getLoginIdDefaultNull() { return stpLogic.getLoginIdDefaultNull(); } /** * 獲取當前會話賬號id, 并轉換為String類型 * * @return 賬號id */ public static String getLoginIdAsString() { return stpLogic.getLoginIdAsString(); } /** * 獲取當前會話賬號id, 并轉換為int類型 * * @return 賬號id */ public static int getLoginIdAsInt() { return stpLogic.getLoginIdAsInt(); } /** * 獲取當前會話賬號id, 并轉換為long類型 * * @return 賬號id */ public static long getLoginIdAsLong() { return stpLogic.getLoginIdAsLong(); } /** * 獲取指定Token對應的賬號id,如果未登錄,則返回 null * * @param tokenValue token * @return 賬號id */ public static Object getLoginIdByToken(String tokenValue) { return stpLogic.getLoginIdByToken(tokenValue); } /** * 獲取Token擴展信息(只在jwt模式下有效) * * @param key 鍵值 * @return 對應的擴展數據 */ public static Object getExtra(String key) { return stpLogic.getExtra(key); } // =================== User-Session 相關 =================== /** * 獲取指定賬號id的Session, 如果Session尚未創建,isCreate=是否新建并返回 * * @param loginId 賬號id * @param isCreate 是否新建 * @return Session對象 */ public static SaSession getSessionByLoginId(Object loginId, boolean isCreate) { return stpLogic.getSessionByLoginId(loginId, isCreate); } /** * 獲取指定key的Session, 如果Session尚未創建,則返回null * * @param sessionId SessionId * @return Session對象 */ public static SaSession getSessionBySessionId(String sessionId) { return stpLogic.getSessionBySessionId(sessionId); } /** * 獲取指定賬號id的Session,如果Session尚未創建,則新建并返回 * * @param loginId 賬號id * @return Session對象 */ public static SaSession getSessionByLoginId(Object loginId) { return stpLogic.getSessionByLoginId(loginId); } /** * 獲取當前會話的Session, 如果Session尚未創建,isCreate=是否新建并返回 * * @param isCreate 是否新建 * @return Session對象 */ public static SaSession getSession(boolean isCreate) { return stpLogic.getSession(isCreate); } /** * 獲取當前會話的Session,如果Session尚未創建,則新建并返回 * * @return Session對象 */ public static SaSession getSession() { return stpLogic.getSession(); } // =================== Token-Session 相關 =================== /** * 獲取指定Token-Session,如果Session尚未創建,則新建并返回 * * @param tokenValue Token值 * @return Session對象 */ public static SaSession getTokenSessionByToken(String tokenValue) { return stpLogic.getTokenSessionByToken(tokenValue); } /** * 獲取當前Token-Session,如果Session尚未創建,則新建并返回 * * @return Session對象 */ public static SaSession getTokenSession() { return stpLogic.getTokenSession(); } // =================== [臨時有效期] 驗證相關 =================== /** * 檢查當前token 是否已經[臨時過期],如果已經過期則拋出異常 */ public static void checkActivityTimeout() { stpLogic.checkActivityTimeout(); } /** * 續簽當前token:(將 [最后操作時間] 更新為當前時間戳) *

請注意: 即時token已經 [臨時過期] 也可續簽成功, * 如果此場景下需要提示續簽失敗,可在此之前調用 checkActivityTimeout() 強制檢查是否過期即可

*/ public static void updateLastActivityToNow() { stpLogic.updateLastActivityToNow(); } // =================== 過期時間相關 =================== /** * 獲取當前登錄者的 token 剩余有效時間 (單位: 秒) * * @return token剩余有效時間 */ public static long getTokenTimeout() { return stpLogic.getTokenTimeout(); } /** * 獲取當前登錄者的 User-Session 剩余有效時間 (單位: 秒) * * @return token剩余有效時間 */ public static long getSessionTimeout() { return stpLogic.getSessionTimeout(); } /** * 獲取當前 Token-Session 剩余有效時間 (單位: 秒) * * @return token剩余有效時間 */ public static long getTokenSessionTimeout() { return stpLogic.getTokenSessionTimeout(); } /** * 獲取當前 token [臨時過期] 剩余有效時間 (單位: 秒) * * @return token [臨時過期] 剩余有效時間 */ public static long getTokenActivityTimeout() { return stpLogic.getTokenActivityTimeout(); } /** * 對當前 Token 的 timeout 值進行續期 * * @param timeout 要修改成為的有效時間 (單位: 秒) */ public static void renewTimeout(long timeout) { stpLogic.renewTimeout(timeout); } /** * 對指定 Token 的 timeout 值進行續期 * * @param tokenValue 指定token * @param timeout 要修改成為的有效時間 (單位: 秒) */ public static void renewTimeout(String tokenValue, long timeout) { stpLogic.renewTimeout(tokenValue, timeout); } // =================== 角色驗證操作 =================== /** * 獲取:當前賬號的角色集合 * * @return / */ public static List getRoleList() { return stpLogic.getRoleList(); } /** * 獲取:指定賬號的角色集合 * * @param loginId 指定賬號id * @return / */ public static List getRoleList(Object loginId) { return stpLogic.getRoleList(loginId); } /** * 判斷:當前賬號是否擁有指定角色, 返回true或false * * @param role 角色標識 * @return 是否含有指定角色標識 */ public static boolean hasRole(String role) { return stpLogic.hasRole(role); } /** * 判斷:指定賬號是否含有指定角色標識, 返回true或false * * @param loginId 賬號id * @param role 角色標識 * @return 是否含有指定角色標識 */ public static boolean hasRole(Object loginId, String role) { return stpLogic.hasRole(loginId, role); } /** * 判斷:當前賬號是否含有指定角色標識 [指定多個,必須全部驗證通過] * * @param roleArray 角色標識數組 * @return true或false */ public static boolean hasRoleAnd(String... roleArray) { return stpLogic.hasRoleAnd(roleArray); } /** * 判斷:當前賬號是否含有指定角色標識 [指定多個,只要其一驗證通過即可] * * @param roleArray 角色標識數組 * @return true或false */ public static boolean hasRoleOr(String... roleArray) { return stpLogic.hasRoleOr(roleArray); } /** * 校驗:當前賬號是否含有指定角色標識, 如果驗證未通過,則拋出異常: NotRoleException * * @param role 角色標識 */ public static void checkRole(String role) { stpLogic.checkRole(role); } /** * 校驗:當前賬號是否含有指定角色標識 [指定多個,必須全部驗證通過] * * @param roleArray 角色標識數組 */ public static void checkRoleAnd(String... roleArray) { stpLogic.checkRoleAnd(roleArray); } /** * 校驗:當前賬號是否含有指定角色標識 [指定多個,只要其一驗證通過即可] * * @param roleArray 角色標識數組 */ public static void checkRoleOr(String... roleArray) { stpLogic.checkRoleOr(roleArray); } // =================== 權限驗證操作 =================== /** * 獲取:當前賬號的權限碼集合 * * @return / */ public static List getPermissionList() { return stpLogic.getPermissionList(); } /** * 獲取:指定賬號的權限碼集合 * * @param loginId 指定賬號id * @return / */ public static List getPermissionList(Object loginId) { return stpLogic.getPermissionList(loginId); } /** * 判斷:當前賬號是否含有指定權限, 返回true或false * * @param permission 權限碼 * @return 是否含有指定權限 */ public static boolean hasPermission(String permission) { return stpLogic.hasPermission(permission); } /** * 判斷:指定賬號id是否含有指定權限, 返回true或false * * @param loginId 賬號id * @param permission 權限碼 * @return 是否含有指定權限 */ public static boolean hasPermission(Object loginId, String permission) { return stpLogic.hasPermission(loginId, permission); } /** * 判斷:當前賬號是否含有指定權限, [指定多個,必須全部具有] * * @param permissionArray 權限碼數組 * @return true 或 false */ public static boolean hasPermissionAnd(String... permissionArray) { return stpLogic.hasPermissionAnd(permissionArray); } /** * 判斷:當前賬號是否含有指定權限 [指定多個,只要其一驗證通過即可] * * @param permissionArray 權限碼數組 * @return true 或 false */ public static boolean hasPermissionOr(String... permissionArray) { return stpLogic.hasPermissionOr(permissionArray); } /** * 校驗:當前賬號是否含有指定權限, 如果驗證未通過,則拋出異常: NotPermissionException * * @param permission 權限碼 */ public static void checkPermission(String permission) { stpLogic.checkPermission(permission); } /** * 校驗:當前賬號是否含有指定權限 [指定多個,必須全部驗證通過] * * @param permissionArray 權限碼數組 */ public static void checkPermissionAnd(String... permissionArray) { stpLogic.checkPermissionAnd(permissionArray); } /** * 校驗:當前賬號是否含有指定權限 [指定多個,只要其一驗證通過即可] * * @param permissionArray 權限碼數組 */ public static void checkPermissionOr(String... permissionArray) { stpLogic.checkPermissionOr(permissionArray); } // =================== id 反查token 相關操作 =================== /** * 獲取指定賬號id的tokenValue *

在配置為允許并發登錄時,此方法只會返回隊列的最后一個token, * 如果你需要返回此賬號id的所有token,請調用 getTokenValueListByLoginId * * @param loginId 賬號id * @return token值 */ public static String getTokenValueByLoginId(Object loginId) { return stpLogic.getTokenValueByLoginId(loginId); } /** * 獲取指定賬號id指定設備端的tokenValue *

在配置為允許并發登錄時,此方法只會返回隊列的最后一個token, * 如果你需要返回此賬號id的所有token,請調用 getTokenValueListByLoginId * * @param loginId 賬號id * @param device 設備標識 * @return token值 */ public static String getTokenValueByLoginId(Object loginId, String device) { return stpLogic.getTokenValueByLoginId(loginId, device); } /** * 獲取指定賬號id的tokenValue集合 * * @param loginId 賬號id * @return 此loginId的所有相關token */ public static List getTokenValueListByLoginId(Object loginId) { return stpLogic.getTokenValueListByLoginId(loginId); } /** * 獲取指定賬號id指定設備端的tokenValue 集合 * * @param loginId 賬號id * @param device 設備標識 * @return 此loginId的所有相關token */ public static List getTokenValueListByLoginId(Object loginId, String device) { return stpLogic.getTokenValueListByLoginId(loginId, device); } /** * 返回當前會話的登錄設備 * * @return 當前令牌的登錄設備 */ public static String getLoginDevice() { return stpLogic.getLoginDevice(); } // =================== 會話管理 =================== /** * 根據條件查詢Token * * @param keyword 關鍵字 * @param start 開始處索引 (-1代表查詢所有) * @param size 獲取數量 * @return token集合 */ public static List searchTokenValue(String keyword, int start, int size) { return stpLogic.searchTokenValue(keyword, start, size); } /** * 根據條件查詢SessionId * * @param keyword 關鍵字 * @param start 開始處索引 (-1代表查詢所有) * @param size 獲取數量 * @return sessionId集合 */ public static List searchSessionId(String keyword, int start, int size) { return stpLogic.searchSessionId(keyword, start, size); } /** * 根據條件查詢Token專屬Session的Id * * @param keyword 關鍵字 * @param start 開始處索引 (-1代表查詢所有) * @param size 獲取數量 * @return sessionId集合 */ public static List searchTokenSessionId(String keyword, int start, int size) { return stpLogic.searchTokenSessionId(keyword, start, size); } // ------------------- 賬號封禁 ------------------- /** * 封禁指定賬號 *

此方法不會直接將此賬號id踢下線,而是在對方再次登錄時拋出`DisableLoginException`異常 * * @param loginId 指定賬號id * @param disableTime 封禁時間, 單位: 秒 (-1=永久封禁) */ public static void disable(Object loginId, long disableTime) { stpLogic.disable(loginId, disableTime); } /** * 指定賬號是否已被封禁 (true=已被封禁, false=未被封禁) * * @param loginId 賬號id * @return see note */ public static boolean isDisable(Object loginId) { return stpLogic.isDisable(loginId); } /** * 獲取指定賬號剩余封禁時間,單位:秒(-1=永久封禁,-2=未被封禁) * * @param loginId 賬號id * @return see note */ public static long getDisableTime(Object loginId) { return stpLogic.getDisableTime(loginId); } /** * 解封指定賬號 * * @param loginId 賬號id */ public static void untieDisable(Object loginId) { stpLogic.untieDisable(loginId); } // =================== 身份切換 =================== /** * 臨時切換身份為指定賬號id * * @param loginId 指定loginId */ public static void switchTo(Object loginId) { stpLogic.switchTo(loginId); } /** * 結束臨時切換身份 */ public static void endSwitch() { stpLogic.endSwitch(); } /** * 當前是否正處于[身份臨時切換]中 * * @return 是否正處于[身份臨時切換]中 */ public static boolean isSwitch() { return stpLogic.isSwitch(); } /** * 在一個代碼段里方法內,臨時切換身份為指定賬號id * * @param loginId 指定賬號id * @param function 要執行的方法 */ public static void switchTo(Object loginId, SaFunction function) { stpLogic.switchTo(loginId, function); } // ------------------- 二級認證 ------------------- /** * 在當前會話 開啟二級認證 * * @param safeTime 維持時間 (單位: 秒) */ public static void openSafe(long safeTime) { stpLogic.openSafe(safeTime); } /** * 當前會話 是否處于二級認證時間內 * * @return true=二級認證已通過, false=尚未進行二級認證或認證已超時 */ public static boolean isSafe() { return stpLogic.isSafe(); } /** * 檢查當前會話是否已通過二級認證,如未通過則拋出異常 */ public static void checkSafe() { stpLogic.checkSafe(); } /** * 獲取當前會話的二級認證剩余有效時間 (單位: 秒, 返回-2代表尚未通過二級認證) * * @return 剩余有效時間 */ public static long getSafeTime() { return stpLogic.getSafeTime(); } /** * 在當前會話 結束二級認證 */ public static void closeSafe() { stpLogic.closeSafe(); } // =================== 歷史API,兼容舊版本 =================== /** *

本函數設計已過時,未來版本可能移除此函數,請及時更換為 StpUtil.getLoginType() ,使用方式保持不變

*

* 獲取當前StpLogin的loginKey * * @return 當前StpLogin的loginKey */ @Deprecated public static String getLoginKey() { return stpLogic.getLoginType(); } /** *

本函數設計已過時,未來版本可能移除此函數,請及時更換為 StpUtil.login() ,使用方式保持不變

*

* 在當前會話上登錄id * * @param loginId 登錄id,建議的類型:(long | int | String) */ @Deprecated public static void setLoginId(Object loginId) { stpLogic.login(loginId); } /** *

本函數設計已過時,未來版本可能移除此函數,請及時更換為 StpUtil.login() ,使用方式保持不變

*

* 在當前會話上登錄id, 并指定登錄設備 * * @param loginId 登錄id,建議的類型:(long | int | String) * @param device 設備標識 */ @Deprecated public static void setLoginId(Object loginId, String device) { stpLogic.login(loginId, device); } /** *

本函數設計已過時,未來版本可能移除此函數,請及時更換為 StpUtil.login() ,使用方式保持不變

*

* 在當前會話上登錄id, 并指定登錄設備 * * @param loginId 登錄id,建議的類型:(long | int | String) * @param isLastingCookie 是否為持久Cookie */ @Deprecated public static void setLoginId(Object loginId, boolean isLastingCookie) { stpLogic.login(loginId, isLastingCookie); } /** *

本函數設計已過時,未來版本可能移除此函數,請及時更換為 StpUtil.login() ,使用方式保持不變

*

* 在當前會話上登錄id, 并指定所有登錄參數Model * * @param loginId 登錄id,建議的類型:(long | int | String) * @param loginModel 此次登錄的參數Model */ @Deprecated public static void setLoginId(Object loginId, SaLoginModel loginModel) { stpLogic.login(loginId, loginModel); } /** *

本函數設計已過時,未來版本可能移除此函數,請及時更換為 StpUtil.kickout() ,使用方式保持不變

*

* 會話注銷,根據賬號id (踢人下線) *

當對方再次訪問系統時,會拋出NotLoginException異常,場景值=-2 * * @param loginId 賬號id */ @Deprecated public static void logoutByLoginId(Object loginId) { stpLogic.kickout(loginId); } /** *

本函數設計已過時,未來版本可能移除此函數,請及時更換為 StpUtil.kickout() ,使用方式保持不變

*

* 會話注銷,根據賬號id and 設備標識 (踢人下線) *

當對方再次訪問系統時,會拋出NotLoginException異常,場景值=-2

* * @param loginId 賬號id * @param device 設備標識 (填null代表所有注銷設備) */ @Deprecated public static void logoutByLoginId(Object loginId, String device) { stpLogic.kickout(loginId, device); } }

新增這個類,修改里面TODO的地方即可。

原文鏈接:https://blog.csdn.net/wenxingchen/article/details/123912552

欄目分類
最近更新