最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java Apache Shiro安全框架快速開發(fā)詳解流程

 更新時間:2021年10月27日 10:12:09   作者:龍弟-idea  
Apache Shiro是一個強大且易用的Java安全框架,執(zhí)行身份驗證、授權(quán)、密碼和會話管理。使用Shiro的易于理解的API,您可以快速、輕松地獲得任何應用程序,從最小的移動應用程序到最大的網(wǎng)絡(luò)和企業(yè)應用程序

一、Shiro簡介:

Apache Shiro是一個Java的安全(權(quán)限)框架。
Shiro 可以非常容易的開發(fā)出足夠好的應用,其不僅可以用在JavaSE環(huán)境,也可以用在JavaEE環(huán)境。Shiro可以完成,認證,授權(quán),加密,會話管理,Web集成,緩存等。
官網(wǎng)地址:Apache Shiro | Simple. Java. Security.

shiro功能:

Authentication:身份認證、登錄,驗證用戶是不是擁有相應的身份;
Authorization:授權(quán),即權(quán)限驗證,驗證某個已認證的用戶是否擁有某個權(quán)限,即判斷用戶能否進行什么操作,如:驗證某個用戶是否擁有某個角色,或者細粒度的驗證某個用戶對某個資源是否具有某個權(quán)限!
Session Manager:會話管理,即用戶登錄后就是第一次會話,在沒有退出之前,它的所有信息都在會話中;會話可以是普通的JavaSE環(huán)境,也可以是Web環(huán)境;
Cryptography:加密,保護數(shù)據(jù)的安全性,如密碼加密存儲到數(shù)據(jù)庫中,而不是明文存儲;Web Support: Web支持,可以非常容易的集成到Web環(huán)境;
Caching:緩存,比如用戶登錄后,其用戶信息,擁有的角色、權(quán)限不必每次去查,這樣可以提高效率
Concurrency: Shiro支持多線程應用的并發(fā)驗證,即,如在一個線程中開啟另一個線程,能把權(quán)限自動的傳播過去
Testing:提供測試支持;
Run As:允許一個用戶假裝為另一個用戶(如果他們允許)的身份進行訪問;
Remember Me:記住我,這個是非常常見的功能,即一次登錄后,下次再來的話不用登錄了

Shiro架構(gòu)(外部)

從外部來看Shiro,即從應用程序角度來觀察如何使用shiro完成工作:

subject:應用代碼直接交互的對象是Subject,也就是說Shiro的對外API核心就是Subject,Subject代表了當前的用戶,這個用戶不一定是一個具體的人,與當前應用交互的任何東西都是Subject,如網(wǎng)絡(luò)爬蟲,機器人等,與Subject的所有交互都會委托給SecurityManager; Subject其實是一個門面,SecurityManageer才是實際的執(zhí)行者
SecurityManager: 安全管理器,即所有與安全有關(guān)的操作都會與SercurityManager交互,并且它管理著所有的Subject,可以看出它是Shiro的核心,它負責與Shiro的其他組件進行交互,它相當于SpringMVC的
DispatcherServlet的角色
Realm: Shiro從Realm獲取安全數(shù)據(jù)((如用戶,角色,權(quán)限),就是說SecurityManager要驗證用戶身份,那么它需要從Realm 獲取相應的用戶進行比較,來確定用戶的身份是否合法;也需要從Realm得到用戶相應的角色、權(quán)限,進行驗證用戶的操作是否能夠進行,可以把Realm看成DataSource;

Shiro架構(gòu)(內(nèi)部)

subject:任何可以與應用交互的'用戶';
Security Manager:相當于SpringMVC中的DispatcherServlet;是Shiro的心臟,所有具體的交互都通過Security Manager進行控制,它管理者所有的Subject,且負責進行認證,授權(quán),會話,及緩存的管理.

Authenticator:負責Subject認證,是一個擴展點,可以自定義實現(xiàn);可以使用認證策略(AuthenticationStrategy),即什么情況下算用戶認證通過了;
Authorizer: 授權(quán)器,即訪問控制器,用來決定主體是否有權(quán)限進行相應的操作;即控制著用戶能訪問應用中的那些功能;
Realm:可以有一個或者多個的realm,可以認為是安全實體數(shù)據(jù)源,即用于獲取安全實體的,可以用DBC實現(xiàn),也可以是內(nèi)存實現(xiàn)等等,由用戶提供;所以一般在應用中都需要實現(xiàn)自己的realm
SessionManager: 管理Session生命周期的組件,而Shiro并不僅僅可以用在Web環(huán)境,也可以用在普通的JavaSE環(huán)境中
CacheManager:緩存控制器,來管理如用戶,角色,權(quán)限等緩存的;因為這些數(shù)據(jù)基本上很少以受,成到緩存中后可以提高訪問的性能;
Cryptography:密碼模塊,Shiro提高了一些常見的加密組件用于密碼加密,解密等

二、快速入門

1.拷貝案例

1.按照官網(wǎng)提示找到 快速入門案例 GitHub地址:shiro/samples/quickstart/

2.新建一個 Maven 工程,刪除其 src 目錄,將其作為父工程

3.在父工程中新建一個 Maven 模塊

4.復制快速入門案例 POM.xml 文件中的依賴

<dependencies>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.1</version>
        </dependency>
 
        <!-- configure logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.29</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.29</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>

5.把快速入門案例中的 resource 下的log4j.properties復制下來

6.復制一下shiro.ini文件

7.復制一下Quickstart.java文件

8.運行啟動Quickstart.java

2.分析代碼

public class Quickstart {
    private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
    public static void main(String[] args) {
        //工廠模式,通過shiro.ini配置文件中的信息,生成一個工廠實例
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        //獲取當前的用戶對象Subject
        Subject currentUser = SecurityUtils.getSubject();
        //通過當前用戶拿到session
        Session session = currentUser.getSession();
        session.setAttribute("someKey", "aValue");
        String value = (String) session.getAttribute("someKey");
        if (value.equals("aValue")) {
            log.info("Subject=>session[" + value + "]");
        }
        //判斷當前的用戶是否被認證
        if (!currentUser.isAuthenticated()) {
            //Token :令牌,沒有獲取,隨機
            UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
            token.setRememberMe(true);  //設(shè)置記住我
            try {
                currentUser.login(token); //執(zhí)行了登錄操作
            } catch (UnknownAccountException uae) {
                //如果   用戶名不存在
                log.info("There is no user with username of " + token.getPrincipal());
            } catch (IncorrectCredentialsException ice) {
                //如果   密碼不正確
                log.info("Password for account " + token.getPrincipal() + " was incorrect!");
            } catch (LockedAccountException lae) {
                //用戶被鎖定,如密碼輸出過多,則被鎖定
                log.info("The account for username " + token.getPrincipal() + " is locked.  " +
                        "Please contact your administrator to unlock it.");
            }
            //...在此處捕獲更多異常
            catch (AuthenticationException ae) {
                //意外情況 ? 錯誤 ?
            }
        }
        //打印其標識主體(在這種情況下,為用戶名)
        log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
 
        //測試角色是否存在
        if (currentUser.hasRole("schwartz")) {
            log.info("May the Schwartz be with you!");
        } else {
            log.info("Hello, mere mortal.");
        }
        //粗粒度,極限范圍小
        //測試類型化的極限(不是實例級別)
        if (currentUser.isPermitted("lightsaber:wield")) {
            log.info("You may use a lightsaber ring.  Use it wisely.");
        } else {
            log.info("Sorry, lightsaber rings are for schwartz masters only.");
        }
        //細粒度,極限范圍廣
        //實例級別的權(quán)限(非常強大)
        if (currentUser.isPermitted("winnebago:drive:eagle5")) {
            log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
                    "Here are the keys - have fun!");
        } else {
            log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
        }
        //注銷
        currentUser.logout();
        //退出
        System.exit(0);
    }
}

三、SpringBoot 集成 Shiro

1.編寫測試環(huán)境

1.在剛剛的父項目中新建一個 springboot 模塊

2.導入 SpringBoot 和 Shiro 整合包的依賴

 <!--SpringBoot 和 Shiro 整合包-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.1</version>
        </dependency>

下面是編寫配置文件
Shiro 三大要素

Subject 用戶 -> ShiroFilterFactoryBean
SecurityManager 管理所有用戶 -> DefaultWebSecurityManager
Realm 連接數(shù)據(jù)

實際操作中對象創(chuàng)建的順序 : realm -> securityManager -> subject

3.編寫自定義的 realm ,需要繼承 AuthorizingRealm

//自定義的 UserRealm        extends AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
    //授權(quán)
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //打印一個提示
        System.out.println("執(zhí)行了授權(quán)方法");
        return null;
    }
 
    //認證
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //打印一個提示
        System.out.println("執(zhí)行了認證方法");
        return null;
    }
}

4.新建一個ShiroConfig配置文件

@Configuration
public class ShiroConfig {
 
    //ShiroFilterFactoryBean:3
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebsecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //設(shè)置安全管理器
        bean.setSecurityManager(defaultWebsecurityManager);
        return bean;
    }
    //DefaultWebSecurityManager:2
    @Bean(name="securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        //關(guān)閉UserRealm
        securityManager.setRealm(userRealm);
        return securityManager;
    }
    //創(chuàng)建realm 對象,需要自定義類:1
    @Bean(name="userRealm")
    public UserRealm userRealm(){
        return new UserRealm();
    }
}

5.測試成功!

2.使用

1.登錄攔截

在getShiroFilterFactoryBean方法中添加需要攔截的登錄請求

@Configuration
public class ShiroConfig {
 
    //ShiroFilterFactoryBean:3
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebsecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //設(shè)置安全管理器
        bean.setSecurityManager(defaultWebsecurityManager);
        //添加shiro的內(nèi)置過濾器
        /*
            anon : 無需認證,就可以訪問
            authc : 必須認證了才能訪問
            user : 必須擁有 “記住我” 功能才能用
            perms : 擁有對某個資源的權(quán)限才能訪問
            role : 擁有某個角色權(quán)限才能訪問
         */
 
        filterMap.put("/user/add","authc");
        filterMap.put("/user/update","authc");
        //攔截
        Map<String,String> filterMap=new LinkedHashMap<>();
        filterMap.put("/user/*","authc");
        bean.setFilterChainDefinitionMap(filterMap);
 
//        //設(shè)置登錄的請求
//        bean.setLoginUrl("/toLogin");
        return bean;
    }

測試:點擊 add鏈接,不會跳到 add 頁面,而是跳到登錄頁,攔截成功

2.用戶認證

1.在Controller層寫一個登錄的方法

    //登錄的方法
    @RequestMapping("/login")
    public String login(String username, String password, Model model) {
        //獲取當前用戶
        Subject subject = SecurityUtils.getSubject();
        //封裝用戶的登錄數(shù)據(jù),獲得令牌
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        //登錄 及 異常處理
        try {
            //執(zhí)行用戶登錄的方法,如果沒有異常就說明OK了
            subject.login(token);
            return "index";
        } catch (UnknownAccountException e) {
            //如果用戶名不存在
            System.out.println("用戶名不存在");
            model.addAttribute("msg", "用戶名錯誤");
            return "login";
        } catch (IncorrectCredentialsException ice) {
            //如果密碼錯誤
            System.out.println("密碼錯誤");
            model.addAttribute("msg", "密碼錯誤");
            return "login";
        }
    }
}

2.測試

可以看出是先執(zhí)行了自定義的UserRealm中的AuthenticationInfo方法,再執(zhí)行登錄相關(guān)的操作

3.修改UserRealm中的doGetAuthenticationInfo方法

    //認證
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //打印一個提示
        System.out.println("執(zhí)行了認證方法");
        // 用戶名密碼
        String name = "root";
        String password = "123456";
        //通過參數(shù)獲取登錄的控制器中生成的 令牌
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        //用戶名認證
        if (!token.getUsername().equals(name)){
            // return null UnKnownAccountException
            return null;
        }
        //密碼認證, Shiro 自己做,為了避免和密碼的接觸
        //最后返回一個 AuthenticationInfo 接口的實現(xiàn)類,這里選擇 SimpleAuthenticationInfo
        // 三個參數(shù):獲取當前用戶的認證 ; 密碼 ; 認證名
        return new SimpleAuthenticationInfo("", password, "");
    }
}

4.測試,輸入錯誤的密碼

輸入正確的密碼,即可登錄成功

四、Shiro整合Mybatis

1.導入依賴

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.19</version>
        </dependency>
 
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
 
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.12</version>
        </dependency>
       <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>

2.新建個application.yml

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
 
    #Spring Boot 默認是不注入這些屬性值的,需要自己綁定
    #druid 數(shù)據(jù)源專有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
 
    #配置監(jiān)控統(tǒng)計攔截的filters,stat:監(jiān)控統(tǒng)計、log4j:日志記錄、wall:防御sql注入
    #如果允許時報錯  java.lang.ClassNotFoundException: org.apache.log4j.Priority
    #則導入 log4j 依賴即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
mybatis:
  type-aliases-package: com.huang.pojo
  mapper-locations: classpath:mybatis/mapper/*.xml

3.在application.properties

mybatis.type-aliases-package=com.longdi.pojo
mybatis.mapper-locations=classpath:mapper/*.xml

4.導入依賴

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
            <scope>provided</scope>
        </dependency>

5.編寫User類

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
        private int id;
        private String name;
        private String pwd;
        private String perms;
}

6.編寫UserMapper

@Repository
@Mapper
public interface UserMapper {
    public User  queryUserByName(String name);
 
}

7.編寫UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 
<mapper namespace="com.longdi.mapper.UserMapper">
 
    <select id="queryUserByName" resultType="User" parameterType="String">
        select * from mybatis.user where name=#{name}
    </select>
 
</mapper>

8.Service層

UserService接口:

public interface UserService {
    public User queryUserByName(String name);
}

9.編寫接口實現(xiàn)類UserServiceImpl

@Service
public class UserServiceImpl implements UserService{
 
    @Autowired
    UserMapper userMapper;
 
    @Override
    public User queryUserByName(String name) {
        return userMapper.queryUserByName(name);
    }
}

10.在ShiroSpringbootApplicationTests測試

@SpringBootTest
class ShiroSpringbootApplicationTests {
    @Autowired
    UserServiceImpl userService;
    @Test
    void contextLoads() {
        System.out.println(userService.queryUserByName("longdi"));
    }
}

11.成功連接數(shù)據(jù)庫

五、實現(xiàn)請求授權(quán)

1.在ShiroConfig類中修改

2.controller跳轉(zhuǎn)

3.登錄攔截授權(quán),測試成功

4.編寫授權(quán)doGetAuthorizationInfo方法

5.請求授權(quán)測試成功

六、Shiro整合Thymeleaf

1.導入依賴

        <!--shiro和thymeleaf整合-->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>

2.整合ShiroDialect

3.index.html

4.測試

5.在認證里放session

6.修改index.html

到此這篇關(guān)于Java Apache Shiro安全框架快速開發(fā)詳解流程的文章就介紹到這了,更多相關(guān)Java Shiro框架內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java數(shù)據(jù)庫連接池的特點及步驟

    java數(shù)據(jù)庫連接池的特點及步驟

    大家好,本篇文章主要講的是數(shù)據(jù)庫連接池的特點及步驟,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • Java雪花算法的原理和實現(xiàn)方法

    Java雪花算法的原理和實現(xiàn)方法

    這篇文章主要介紹了Java雪花算法的原理和實現(xiàn)方法,雪花算法是一種分布式唯一ID生成算法,可以生成全局唯一的ID標識符,就像自然界中雪花一般沒有相同的雪花,下面將詳細介紹,感興趣的可以學習一下
    2023-10-10
  • JAVA基于Arrays.sort()實現(xiàn)數(shù)組升序和降序

    JAVA基于Arrays.sort()實現(xiàn)數(shù)組升序和降序

    這篇文章主要介紹了JAVA基于Arrays.sort()實現(xiàn)數(shù)組升序和降序,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-06-06
  • Java正確比較浮點數(shù)的方法

    Java正確比較浮點數(shù)的方法

    這篇文章主要介紹了Java正確比較浮點數(shù)的方法,幫助大家更好的利用Java比較浮點數(shù)數(shù)據(jù),感興趣的朋友可以了解下
    2020-11-11
  • 使用@TableField(updateStrategy=FieldStrategy.IGNORED)遇到的坑記錄

    使用@TableField(updateStrategy=FieldStrategy.IGNORED)遇到的坑記錄

    這篇文章主要介紹了使用@TableField(updateStrategy=FieldStrategy.IGNORED)遇到的坑及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • spring中12種@Transactional的失效場景(小結(jié))

    spring中12種@Transactional的失效場景(小結(jié))

    日常我們進行業(yè)務開發(fā)時,基本上使用的都是聲明式事務,即為使用@Transactional注解的方式,本文主要介紹了spring中12種@Transactional的失效場景,感興趣的小伙伴們可以參考一下
    2022-01-01
  • Java超詳細介紹抽象類與接口的使用

    Java超詳細介紹抽象類與接口的使用

    在類中沒有包含足夠的信息來描繪一個具體的對象,這樣的類稱為抽象類,接口是Java中最重要的概念之一,它可以被理解為一種特殊的類,不同的是接口的成員沒有執(zhí)行體,是由全局常量和公共的抽象方法所組成,本文給大家介紹Java抽象類和接口,感興趣的朋友一起看看吧
    2022-05-05
  • Spring Boot 定制與優(yōu)化內(nèi)置的Tomcat容器實例詳解

    Spring Boot 定制與優(yōu)化內(nèi)置的Tomcat容器實例詳解

    本文主要記錄對內(nèi)置容器優(yōu)化和定制的方式,用于自己加深對SpringBoot理解。本文給大家介紹的非常詳細,具有參考借鑒價值,需要的朋友參考下吧
    2017-12-12
  • JAVA如何調(diào)用Shell腳本

    JAVA如何調(diào)用Shell腳本

    本篇文章主要介紹了JAVA如何調(diào)用Shell腳本,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • 常用的Java數(shù)據(jù)結(jié)構(gòu)知識點匯總

    常用的Java數(shù)據(jù)結(jié)構(gòu)知識點匯總

    這篇文章主要介紹了常用的Java數(shù)據(jù)結(jié)構(gòu)知識點匯總,數(shù)據(jù)結(jié)構(gòu)分線性數(shù)據(jù)結(jié)構(gòu)和非線性數(shù)據(jù)結(jié)構(gòu),下面對此作詳細介紹,需要的小伙伴可以參考一下,希望對你的學習或工作有所幫助
    2022-03-03

最新評論

霍山县| 鲜城| 大安市| 墨玉县| 湄潭县| 晋中市| 彰武县| 博罗县| 克拉玛依市| 修武县| 思茅市| 乌鲁木齐县| 连江县| 武平县| 乐平市| 辽宁省| 沐川县| 那坡县| 玉山县| 芜湖市| 瑞昌市| 贵德县| 潜江市| 栖霞市| 武清区| 西和县| 古田县| 澄江县| 瑞昌市| 田阳县| 黔西| 谷城县| 漳平市| 峨山| 厦门市| 龙里县| 东宁县| 冕宁县| 福泉市| 呼和浩特市| 梁河县|