日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

第六章 Realm及相關(guān)對象——《跟我學(xué)Shiro》

系統(tǒng) 1712 0

?

目錄貼: ?跟我學(xué)Shiro目錄貼

?

?

6.1 Realm

【2.5 Realm】及【3.5 Authorizer】部分都已經(jīng)詳細(xì)介紹過Realm了,接下來再來看一下一般真實環(huán)境下的Realm如何實現(xiàn)。

??

1 、定義實體及關(guān)系


即用戶-角色之間是多對多關(guān)系,角色-權(quán)限之間是多對多關(guān)系;且用戶和權(quán)限之間通過角色建立關(guān)系;在系統(tǒng)中驗證時通過權(quán)限驗證,角色只是權(quán)限集合,即所謂的顯示角色;其實權(quán)限應(yīng)該對應(yīng)到資源(如菜單、URL、頁面按鈕、Java方法等)中,即應(yīng)該將權(quán)限字符串存儲到資源實體中,但是目前為了簡單化,直接提取一個權(quán)限表,【綜合示例】部分會使用完整的表結(jié)構(gòu)。

?

用戶實體包括:編號(id)、用戶名(username)、密碼(password)、鹽(salt)、是否鎖定(locked);是否鎖定用于封禁用戶使用,其實最好使用Enum字段存儲,可以實現(xiàn)更復(fù)雜的用戶狀態(tài)實現(xiàn)。

角色實體包括:、編號(id)、角色標(biāo)識符(role)、描述(description)、是否可用(available);其中角色標(biāo)識符用于在程序中進(jìn)行隱式角色判斷的,描述用于以后再前臺界面顯示的、是否可用表示角色當(dāng)前是否激活。

權(quán)限實體包括:編號(id)、權(quán)限標(biāo)識符(permission)、描述(description)、是否可用(available);含義和角色實體類似不再闡述。

?

另外還有兩個關(guān)系實體:用戶-角色實體(用戶編號、角色編號,且組合為復(fù)合主鍵);角色-權(quán)限實體(角色編號、權(quán)限編號,且組合為復(fù)合主鍵)。

?

sql及實體請參考源代碼中的sql\shiro.sql 和 com.github.zhangkaitao.shiro.chapter6.entity對應(yīng)的實體。

?

2 、環(huán)境準(zhǔn)備

為了方便數(shù)據(jù)庫操作,使用了“org.springframework: spring-jdbc: 4.0.0.RELEASE”依賴,雖然是spring4版本的,但使用上和spring3無區(qū)別。其他依賴請參考源碼的pom.xml。

?

3 、定義 Service Dao

為了實現(xiàn)的簡單性,只實現(xiàn)必須的功能,其他的可以自己實現(xiàn)即可。

?

PermissionService

      public interface PermissionService {
    public Permission createPermission(Permission permission);
    public void deletePermission(Long permissionId);
}

    

實現(xiàn)基本的創(chuàng)建/刪除權(quán)限。

?

RoleService ?

      public interface RoleService {
    public Role createRole(Role role);
    public void deleteRole(Long roleId);
    //添加角色-權(quán)限之間關(guān)系
    public void correlationPermissions(Long roleId, Long... permissionIds);
    //移除角色-權(quán)限之間關(guān)系
    public void uncorrelationPermissions(Long roleId, Long... permissionIds);//
}?
    

相對于PermissionService多了關(guān)聯(lián)/移除關(guān)聯(lián)角色-權(quán)限功能。

?

UserService ?

      public interface UserService {
    public User createUser(User user); //創(chuàng)建賬戶
    public void changePassword(Long userId, String newPassword);//修改密碼
    public void correlationRoles(Long userId, Long... roleIds); //添加用戶-角色關(guān)系
    public void uncorrelationRoles(Long userId, Long... roleIds);// 移除用戶-角色關(guān)系
    public User findByUsername(String username);// 根據(jù)用戶名查找用戶
    public Set<String> findRoles(String username);// 根據(jù)用戶名查找其角色
    public Set<String> findPermissions(String username); //根據(jù)用戶名查找其權(quán)限
}?
    

此處使用findByUsername、findRoles及findPermissions來查找用戶名對應(yīng)的帳號、角色及權(quán)限信息。之后的Realm就使用這些方法來查找相關(guān)信息。

?

UserServiceImpl ??

      public User createUser(User user) {
    //加密密碼
    passwordHelper.encryptPassword(user);
    return userDao.createUser(user);
}
public void changePassword(Long userId, String newPassword) {
    User user =userDao.findOne(userId);
    user.setPassword(newPassword);
    passwordHelper.encryptPassword(user);
    userDao.updateUser(user);
}?
    

在創(chuàng)建賬戶及修改密碼時直接把生成密碼操作委托給PasswordHelper。

?

PasswordHelper

      public class PasswordHelper {
    private RandomNumberGenerator randomNumberGenerator =
     new SecureRandomNumberGenerator();
    private String algorithmName = "md5";
    private final int hashIterations = 2;
    public void encryptPassword(User user) {
        user.setSalt(randomNumberGenerator.nextBytes().toHex());
        String newPassword = new SimpleHash(
                algorithmName,
                user.getPassword(),
                ByteSource.Util.bytes(user.getCredentialsSalt()),
                hashIterations).toHex();
        user.setPassword(newPassword);
    }
}?
    

之后的CredentialsMatcher需要和此處加密的算法一樣。user.getCredentialsSalt()輔助方法返回username+salt。

?

為了節(jié)省篇幅,對于DAO/Service的接口及實現(xiàn),具體請參考源碼com.github.zhangkaitao.shiro.chapter6。另外請參考Service層的測試用例com.github.zhangkaitao.shiro.chapter6.service.ServiceTest。

?

4 、定義 Realm

RetryLimitHashedCredentialsMatcher ?

和第五章的一樣,在此就不羅列代碼了,請參考源碼com.github.zhangkaitao.shiro.chapter6.credentials.RetryLimitHashedCredentialsMatcher。

??

UserRealm

另外請參考Service層的測試用例com.github.zhangkaitao.shiro.chapter6.service.ServiceTest。?

      public class UserRealm extends AuthorizingRealm {
    private UserService userService = new UserServiceImpl();
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String)principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(userService.findRoles(username));
        authorizationInfo.setStringPermissions(userService.findPermissions(username));
        return authorizationInfo;
    }
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String)token.getPrincipal();
        User user = userService.findByUsername(username);
        if(user == null) {
            throw new UnknownAccountException();//沒找到帳號
        }
        if(Boolean.TRUE.equals(user.getLocked())) {
            throw new LockedAccountException(); //帳號鎖定
        }
        //交給AuthenticatingRealm使用CredentialsMatcher進(jìn)行密碼匹配,如果覺得人家的不好可以在此判斷或自定義實現(xiàn)
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user.getUsername(), //用戶名
                user.getPassword(), //密碼
                ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+salt
                getName()  //realm name
        );
        return authenticationInfo;
    }
}?
    

1 、UserRealm 父類AuthorizingRealm 將獲取Subject 相關(guān)信息分成兩步 :獲取身份驗證信息(doGetAuthenticationInfo)及授權(quán)信息(doGetAuthorizationInfo);

2 、doGetAuthenticationInfo 獲取身份驗證相關(guān)信息 :首先根據(jù)傳入的用戶名獲取User信息;然后如果user為空,那么拋出沒找到帳號異常UnknownAccountException;如果user找到但鎖定了拋出鎖定異常LockedAccountException;最后生成AuthenticationInfo信息,交給間接父類AuthenticatingRealm使用CredentialsMatcher進(jìn)行判斷密碼是否匹配,如果不匹配將拋出密碼錯誤異常IncorrectCredentialsException;另外如果密碼重試此處太多將拋出超出重試次數(shù)異常ExcessiveAttemptsException;在組裝SimpleAuthenticationInfo信息時,需要傳入:身份信息(用戶名)、憑據(jù)(密文密碼)、鹽(username+salt),CredentialsMatcher使用鹽加密傳入的明文密碼和此處的密文密碼進(jìn)行匹配。

3 、doGetAuthorizationInfo 獲取授權(quán)信息 :PrincipalCollection是一個身份集合,因為我們現(xiàn)在就一個Realm,所以直接調(diào)用getPrimaryPrincipal得到之前傳入的用戶名即可;然后根據(jù)用戶名調(diào)用UserService接口獲取角色及權(quán)限信息。

?

5 、測試用例

為了節(jié)省篇幅,請參考測試用例com.github.zhangkaitao.shiro.chapter6.realm.UserRealmTest。包含了:登錄成功、用戶名錯誤、密碼錯誤、密碼超出重試次數(shù)、有/沒有角色、有/沒有權(quán)限的測試。

?

6.2 AuthenticationToken

第六章 Realm及相關(guān)對象——《跟我學(xué)Shiro》

AuthenticationToken用于收集用戶提交的身份(如用戶名)及憑據(jù)(如密碼):

      public interface AuthenticationToken extends Serializable {
    Object getPrincipal(); //身份
    Object getCredentials(); //憑據(jù)
}?
    

擴(kuò)展接口RememberMeAuthenticationToken:提供了“boolean isRememberMe()”現(xiàn)“記住我”的功能;

擴(kuò)展接口是HostAuthenticationToken:提供了“String getHost()”方法用于獲取用戶“主機(jī)”的功能。

?

Shiro提供了一個直接拿來用的UsernamePasswordToken,用于實現(xiàn)用戶名/密碼Token組,另外其實現(xiàn)了RememberMeAuthenticationToken和HostAuthenticationToken,可以實現(xiàn)記住我及主機(jī)驗證的支持。

?

6.3 AuthenticationInfo

第六章 Realm及相關(guān)對象——《跟我學(xué)Shiro》

AuthenticationInfo有兩個作用:

1、如果Realm是AuthenticatingRealm子類,則提供給AuthenticatingRealm內(nèi)部使用的CredentialsMatcher進(jìn)行憑據(jù)驗證;(如果沒有繼承它需要在自己的Realm中自己實現(xiàn)驗證);

2、提供給SecurityManager來創(chuàng)建Subject(提供身份信息);

?

MergableAuthenticationInfo用于提供在多Realm時合并AuthenticationInfo的功能,主要合并Principal、如果是其他的如credentialsSalt,會用后邊的信息覆蓋前邊的。

?

比如HashedCredentialsMatcher,在驗證時會判斷AuthenticationInfo是否是SaltedAuthenticationInfo子類,來獲取鹽信息。

?

Account相當(dāng)于我們之前的User,SimpleAccount是其一個實現(xiàn);在IniRealm、PropertiesRealm這種靜態(tài)創(chuàng)建帳號信息的場景中使用,這些Realm直接繼承了SimpleAccountRealm,而SimpleAccountRealm提供了相關(guān)的API來動態(tài)維護(hù)SimpleAccount;即可以通過這些API來動態(tài)增刪改查SimpleAccount;動態(tài)增刪改查角色/權(quán)限信息。及如果您的帳號不是特別多,可以使用這種方式,具體請參考SimpleAccountRealm Javadoc。

?

其他情況一般返回SimpleAuthenticationInfo即可。

?

6.4 PrincipalCollection

第六章 Realm及相關(guān)對象——《跟我學(xué)Shiro》

因為我們可以在Shiro中同時配置多個Realm,所以呢身份信息可能就有多個;因此其提供了PrincipalCollection用于聚合這些身份信息:

      public interface PrincipalCollection extends Iterable, Serializable {
    Object getPrimaryPrincipal(); //得到主要的身份
    <T> T oneByType(Class<T> type); //根據(jù)身份類型獲取第一個
    <T> Collection<T> byType(Class<T> type); //根據(jù)身份類型獲取一組
    List asList(); //轉(zhuǎn)換為List
    Set asSet(); //轉(zhuǎn)換為Set
    Collection fromRealm(String realmName); //根據(jù)Realm名字獲取
    Set<String> getRealmNames(); //獲取所有身份驗證通過的Realm名字
    boolean isEmpty(); //判斷是否為空
}?
    

因為PrincipalCollection聚合了多個,此處最需要注意的是getPrimaryPrincipal,如果只有一個Principal那么直接返回即可,如果有多個Principal,則返回第一個(因為內(nèi)部使用Map存儲,所以可以認(rèn)為是返回任意一個);oneByType / byType根據(jù)憑據(jù)的類型返回相應(yīng)的Principal;fromRealm根據(jù)Realm名字(每個Principal都與一個Realm關(guān)聯(lián))獲取相應(yīng)的Principal。

?

MutablePrincipalCollection是一個可變的PrincipalCollection接口,即提供了如下可變方法:

      public interface MutablePrincipalCollection extends PrincipalCollection {
    void add(Object principal, String realmName); //添加Realm-Principal的關(guān)聯(lián)
    void addAll(Collection principals, String realmName); //添加一組Realm-Principal的關(guān)聯(lián)
    void addAll(PrincipalCollection principals);//添加PrincipalCollection
    void clear();//清空
}?
    

目前Shiro只提供了一個實現(xiàn)SimplePrincipalCollection,還記得之前的AuthenticationStrategy實現(xiàn)嘛,用于在多Realm時判斷是否滿足條件的,在大多數(shù)實現(xiàn)中(繼承了AbstractAuthenticationStrategy)afterAttempt方法會進(jìn)行AuthenticationInfo(實現(xiàn)了MergableAuthenticationInfo)的merge,比如SimpleAuthenticationInfo會合并多個Principal為一個PrincipalCollection。

?

對于PrincipalMap是Shiro 1.2中的一個實驗品,暫時無用,具體可以參考其Javadoc。接下來通過示例來看看PrincipalCollection。

?

1 、準(zhǔn)備三個Realm

MyRealm1

      public class MyRealm1 implements Realm {
    @Override
    public String getName() {
        return "a"; //realm name 為 “a”
    }
    //省略supports方法,具體請見源碼
    @Override
    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        return new SimpleAuthenticationInfo(
                "zhang", //身份 字符串類型
                "123",   //憑據(jù)
                getName() //Realm Name
        );
    }
}

    

?????????

MyRealm2 ?

和MyRealm1完全一樣,只是Realm名字為b。

??

MyRealm3

      public class MyRealm3 implements Realm {
    @Override
    public String getName() {
        return "c"; //realm name 為 “c”
    }
    //省略supports方法,具體請見源碼
    @Override
    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        User user = new User("zhang", "123");
        return new SimpleAuthenticationInfo(
                user, //身份 User類型
                "123",   //憑據(jù)
                getName() //Realm Name
        );
    }
}?
    

和MyRealm1同名,但返回的Principal是User類型。

?

2、ini配置(shiro-multirealm.ini)

      [main]
realm1=com.github.zhangkaitao.shiro.chapter6.realm.MyRealm1
realm2=com.github.zhangkaitao.shiro.chapter6.realm.MyRealm2
realm3=com.github.zhangkaitao.shiro.chapter6.realm.MyRealm3
securityManager.realms=$realm1,$realm2,$realm3?
    

3 、測試用例(com.github.zhangkaitao.shiro.chapter6.realm.PrincialCollectionTest

因為我們的Realm中沒有進(jìn)行身份及憑據(jù)驗證,所以相當(dāng)于身份驗證都是成功的,都將返回:

      Object primaryPrincipal1 = subject.getPrincipal();
PrincipalCollection princialCollection = subject.getPrincipals();
Object primaryPrincipal2 = princialCollection.getPrimaryPrincipal();?
    

我們可以直接調(diào)用subject.getPrincipal獲取PrimaryPrincipal(即所謂的第一個);或者通過getPrincipals獲取PrincipalCollection;然后通過其getPrimaryPrincipal獲取PrimaryPrincipal。

?

      Set<String> realmNames = princialCollection.getRealmNames();
    

獲取所有身份驗證成功的Realm名字。??????

?

      Set<Object> principals = princialCollection.asSet(); //asList和asSet的結(jié)果一樣
    

將身份信息轉(zhuǎn)換為Set/List,即使轉(zhuǎn)換為List,也是先轉(zhuǎn)換為Set再完成的。

?

      Collection<User> users = princialCollection.fromRealm("c");
    

根據(jù)Realm名字獲取身份,因為Realm名字可以重復(fù),所以可能多個身份,建議Realm名字盡量不要重復(fù)。

?

6.4 AuthorizationInfo

第六章 Realm及相關(guān)對象——《跟我學(xué)Shiro》

AuthorizationInfo用于聚合授權(quán)信息的:

      public interface AuthorizationInfo extends Serializable {
    Collection<String> getRoles(); //獲取角色字符串信息
    Collection<String> getStringPermissions(); //獲取權(quán)限字符串信息
    Collection<Permission> getObjectPermissions(); //獲取Permission對象信息
}?
    

當(dāng)我們使用AuthorizingRealm時,如果身份驗證成功,在進(jìn)行授權(quán)時就通過doGetAuthorizationInfo方法獲取角色/權(quán)限信息用于授權(quán)驗證。

?

Shiro提供了一個實現(xiàn)SimpleAuthorizationInfo,大多數(shù)時候使用這個即可。

?

對于Account及SimpleAccount,之前的【6.3 AuthenticationInfo】已經(jīng)介紹過了,用于SimpleAccountRealm子類,實現(xiàn)動態(tài)角色/權(quán)限維護(hù)的。

?

6.5 Subject

第六章 Realm及相關(guān)對象——《跟我學(xué)Shiro》

Subject是Shiro的核心對象,基本所有身份驗證、授權(quán)都是通過Subject完成。

1 、身份信息獲取

      Object getPrincipal(); //Primary Principal
PrincipalCollection getPrincipals(); // PrincipalCollection?
    

?

2 、身份驗證

      void login(AuthenticationToken token) throws AuthenticationException;
boolean isAuthenticated();
boolean isRemembered();

    

通過login登錄,如果登錄失敗將拋出相應(yīng)的AuthenticationException,如果登錄成功調(diào)用isAuthenticated就會返回true,即已經(jīng)通過身份驗證;如果isRemembered返回true,表示是通過記住我功能登錄的而不是調(diào)用login方法登錄的。isAuthenticated/isRemembered是互斥的,即如果其中一個返回true,另一個返回false。

??

3 、角色授權(quán)驗證 ?

      boolean hasRole(String roleIdentifier);
boolean[] hasRoles(List<String> roleIdentifiers);
boolean hasAllRoles(Collection<String> roleIdentifiers);
void checkRole(String roleIdentifier) throws AuthorizationException;
void checkRoles(Collection<String> roleIdentifiers) throws AuthorizationException;
void checkRoles(String... roleIdentifiers) throws AuthorizationException;?
    

hasRole*進(jìn)行角色驗證,驗證后返回true/false;而checkRole*驗證失敗時拋出AuthorizationException異常。?

?

4 、權(quán)限授權(quán)驗證

      boolean isPermitted(String permission);
boolean isPermitted(Permission permission);
boolean[] isPermitted(String... permissions);
boolean[] isPermitted(List<Permission> permissions);
boolean isPermittedAll(String... permissions);
boolean isPermittedAll(Collection<Permission> permissions);
void checkPermission(String permission) throws AuthorizationException;
void checkPermission(Permission permission) throws AuthorizationException;
void checkPermissions(String... permissions) throws AuthorizationException;
void checkPermissions(Collection<Permission> permissions) throws AuthorizationException;

    

isPermitted*進(jìn)行權(quán)限驗證,驗證后返回true/false;而checkPermission*驗證失敗時拋出AuthorizationException。

?

5 、會話

      Session getSession(); //相當(dāng)于getSession(true)
Session getSession(boolean create); ?
    

類似于Web中的會話。如果登錄成功就相當(dāng)于建立了會話,接著可以使用getSession獲?。蝗绻鹀reate=false如果沒有會話將返回null,而create=true如果沒有會話會強(qiáng)制創(chuàng)建一個。

?

6 、退出 ?

      void logout();
    

?

7 、RunAs ??

      void runAs(PrincipalCollection principals) throws NullPointerException, IllegalStateException;
boolean isRunAs();
PrincipalCollection getPreviousPrincipals();
PrincipalCollection releaseRunAs();?
    

RunAs即實現(xiàn)“允許A假設(shè)為B身份進(jìn)行訪問”;通過調(diào)用subject.runAs(b)進(jìn)行訪問;接著調(diào)用subject.getPrincipals將獲取到B的身份;此時調(diào)用isRunAs將返回true;而a的身份需要通過subject. getPreviousPrincipals獲?。蝗绻恍枰猂unAs了調(diào)用subject. releaseRunAs即可。

?

8 、多線程

      <V> V execute(Callable<V> callable) throws ExecutionException;
void execute(Runnable runnable);
<V> Callable<V> associateWith(Callable<V> callable);
Runnable associateWith(Runnable runnable);?
    

實現(xiàn)線程之間的Subject傳播,因為Subject是線程綁定的;因此在多線程執(zhí)行中需要傳播到相應(yīng)的線程才能獲取到相應(yīng)的Subject。最簡單的辦法就是通過execute(runnable/callable實例)直接調(diào)用;或者通過associateWith(runnable/callable實例)得到一個包裝后的實例;它們都是通過:1、把當(dāng)前線程的Subject綁定過去;2、在線程執(zhí)行結(jié)束后自動釋放。

?

Subject自己不會實現(xiàn)相應(yīng)的身份驗證/授權(quán)邏輯,而是通過DelegatingSubject委托給SecurityManager實現(xiàn);及可以理解為Subject是一個面門。

?

對于Subject的構(gòu)建一般沒必要我們?nèi)?chuàng)建;一般通過SecurityUtils.getSubject()獲?。?

      public static Subject getSubject() {
    Subject subject = ThreadContext.getSubject();
    if (subject == null) {
        subject = (new Subject.Builder()).buildSubject();
        ThreadContext.bind(subject);
    }
    return subject;
}?
    

即首先查看當(dāng)前線程是否綁定了Subject,如果沒有通過Subject.Builder構(gòu)建一個然后綁定到現(xiàn)場返回。

?

如果想自定義創(chuàng)建,可以通過:

      new Subject.Builder().principals(身份).authenticated(true/false).buildSubject()
    

這種可以創(chuàng)建相應(yīng)的Subject實例了,然后自己綁定到線程即可。在new Builder()時如果沒有傳入SecurityManager,自動調(diào)用SecurityUtils.getSecurityManager獲?。灰部梢宰约簜魅胍粋€實例。

?

對于Subject我們一般這么使用:

1、身份驗證(login)

2、授權(quán)(hasRole*/isPermitted*或checkRole*/checkPermission*)

3、將相應(yīng)的數(shù)據(jù)存儲到會話(Session)

4、切換身份(RunAs)/多線程身份傳播

5、退出

?

?

而我們必須的功能就是1、2、5。到目前為止我們就可以使用Shiro進(jìn)行應(yīng)用程序的安全控制了,但是還是缺少如對Web驗證、Java方法驗證等的一些簡化實現(xiàn)。 ? ?

?

示例源代碼: https://github.com/zhangkaitao/shiro-example ;可加群134755960探討Spring/Shiro技術(shù)。

?

第六章 Realm及相關(guān)對象——《跟我學(xué)Shiro》


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。?!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 精河县| 顺昌县| 凌云县| 青海省| 巴里| 水城县| 荔波县| 伊春市| 枞阳县| 卫辉市| 崇阳县| 晋江市| 桓台县| 武夷山市| 庆元县| 泗阳县| 临漳县| 赤壁市| 宁强县| 江津市| 卢湾区| 莎车县| 古丈县| 永济市| 莆田市| 托克逊县| 金寨县| 江油市| 游戏| 锡林浩特市| 乡宁县| 获嘉县| 大丰市| 郁南县| 四川省| 壤塘县| 时尚| 万山特区| 宿松县| 巴里| 彰武县|