1. 程式人生 > >Filter(過濾器)常見應用(三)——許可權管理系統(一)

Filter(過濾器)常見應用(三)——許可權管理系統(一)

我們要設計的許可權管理系統,要使用Filter實現URL級別的許可權認證。那我們不禁就要想設計出的許可權管理系統應該使用在哪種情景中呢?我想應該是這樣的一種情景吧:在實際開發中我們經常把一些執行敏感操作的servlet對映到一些特殊目錄中,並用filter把這些特殊目錄保護起來,限制只能擁有相應訪問許可權的使用者才能訪問這些目錄下的資源。從而在我們系統中實現一種URL級別的許可權功能。而且為使Filter具有通用性,Filter保護的資源和相應的訪問許可權通過filter引數的形式予以配置。

建立MVC架構的Web專案

在Eclipse中新建立一個day20的專案,匯入專案所需要的開發包(jar包),建立專案所需要的包,在Java Web開發中,架構的層次是以包的形式體現出來的。
專案所需要的開發包(jar包)

序號 開發包名稱 描述
stl-1.2.jar jstl標籤庫和EL表示式依賴包
mysql-connector-java-5.1.38-bin.jar MySQL資料庫驅動包
commons-beanutils-1.9.2.jar 工具類,用於處理bean物件
commons-logging-1.2.jar commons-beanutils-1.9.2.jar的依賴jar包
commons-collections-3.2.2.jar commons-beanutils-1.9.2.jar的依賴jar包
c3p0-0.9.5.2.jar Java配置C3P0資料庫連線池使用的包
mchange-commons-java-0.2.11.jar C3P0依賴的相關jar包
commons-dbutils-1.6.jar Apache組織提供的一個開源JDBC工具類庫

專案所需要的包

序號 包名 描述 所屬層次
cn.itcast.domain 存放系統的JavaBean類(只包含簡單的屬性以及屬性對應的get和set方法,不包含具體的業務處理方法),提供給【資料訪問層】、【業務邏輯層】、【Web層】來使用 domain(域模型)層
cn.itcast.dao 在此工程中只存放訪問資料庫的操作介面的實現類 資料訪問層
cn.itcast.service 在此工程中只存放處理系統業務介面的實現類 業務邏輯層
cn.itcast.web.controller 存放作為系統控制器的Servlet(處理請求的servlet) Web層(表現層)
cn.itcast.web.filter 存放作為系統過濾器的Filter Web層(表現層)
cn.itcast.utils 存放系統的通用工具類,提供給【資料訪問層】、【業務邏輯層】、【Web層】來使用

以上就是根據此專案的實際情況建立的包,可能還需要建立其他的包,這個得根據專案的需要來定了。

許可權管理系統的設計和分析

我們若要設計一個這樣的許可權管理系統,肯定就要考慮需要設計幾個物件,一般來說大概至少需要以下四個物件:

  • Resource:代表系統中的每一個資源(超連結)。
  • Privilege:代表訪問資源的許可權。
  • Role:資源屬於什麼角色(管理員、經理、普通員工)。
  • User:使用者。

現在我們來思考這幾個物件之間的關係,萬事萬物總該有那麼一點聯絡吧!

  • 許可權和資源之間的關係:
    若一個資源需要一個許可權,這是一對一的關係;若一個許可權可以控制多個資源,並且一個資源只能對應有一個控制權限,這是一對多的關係。但是在此許可權管理系統中我們選擇後者,即一對多的關係。
  • 許可權和角色之間的關係:
    一個角色有多個許可權,一個許可權賦予多個角色,這是多對多的關係。
  • 使用者和角色之間的關係:
    一個使用者有可能擁有多個角色(多重身份),一個角色可能被賦予多個使用者,這是多對多的關係。

明瞭上述內容之後,我們就來設計這四個物件,即開發domain層。

開發domain層

首先我們來分析和設計許可權(Privilege)這個類,該類一些基本的屬性,我們忽略,而來著重分析許可權和資源以及許可權和角色之間的關係。

  • 在分析和設計許可權(Privilege)這個類時,這個類可以不設定集合來記住它控制的哪些資源,由於在頁面裡面一般是沒有這個需求的,我們在顯示這個許可權的時候,我們不需要說這個許可權控制了哪幾個資源,我們只會說在顯示資源的時候,說它被哪個許可權控制。
  • 在分析和設計許可權(Privilege)這個類時,我有必要設計一個集合來記住這個許可權授予了哪些角色嗎?——沒有必要,一般來說,只是在顯示角色的時候,我們才會說這個角色擁有哪些許可權,我們是不會在頁面裡說在顯示許可權的時候,許可權授予了哪些角色。

分析完了,許可權(Privilege)這個類的程式碼實現就會一目瞭然,我們在cn.itcast.domain包中建立這個許可權(Privilege)類。
這裡寫圖片描述
許可權(Privilege)這個類的具體程式碼如下:

public class Privilege {

    private String id;
    private String name; // 新增分類...的許可權
    private String description;

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }

}

接下來我們來分析和設計資源(Resource)這個類,該類一些基本的屬性,我們忽略,而來著重分析資源和許可權之間的關係。

  • 在分析和設計資源(Resource)這個類時,有沒有必要設計一個屬性記住資源被哪個許可權控制呢?——顯然有必要,我們在頁面顯示資源的時候,我們會說這個資源被哪個許可權控制。

分析完了,資源(Resource)這個類的程式碼實現就會一目瞭然,我們在cn.itcast.domain包中建立這個資源(Resource)類。
這裡寫圖片描述
資源(Resource)這個類的具體程式碼如下:

public class Resource {

    private String id;
    private String uri; // /day20/Servlet1
    private String description;

    private Privilege privilege;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUri() {
        return uri;
    }

    public void setUri(String uri) {
        this.uri = uri;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Privilege getPrivilege() {
        return privilege;
    }

    public void setPrivilege(Privilege privilege) {
        this.privilege = privilege;
    }

}

再接下來我們來分析和設計角色(Role)這個類,該類一些基本的屬性,我們忽略,而來著重分析角色和許可權以及角色和使用者之間的關係。

  • 思考你在頁面顯示一個角色的時候,你需要顯示什麼?——我在頁面裡面顯示一個角色的時候,這個角色應擁有哪些許可權,所以說要設計一個集合來記住角色所有的許可權。
  • 然後思考有沒有必要設計一個集合來記住這個角色授予了多少個使用者呢?——一般來說沒有必要,應該在顯示使用者的時候說這個使用者擁有哪些角色,而不會說顯示角色的時候,這個角色授予了哪些使用者。

分析完了,角色(Role)這個類的程式碼實現就會一目瞭然,我們在cn.itcast.domain包中建立這個角色(Role)類。
這裡寫圖片描述
角色(Role)這個類的具體程式碼如下:

public class Role {

    private String id;
    private String name;
    private String description;

    private Set<Privilege> privileges = new HashSet<Privilege>();

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Set<Privilege> getPrivileges() {
        return privileges;
    }

    public void setPrivileges(Set<Privilege> privileges) {
        this.privileges = privileges;
    }

}

最後我們來分析和設計使用者(User)這個類,該類一些基本的屬性,我們忽略,而來著重分析使用者和角色之間的關係。

  • 思考:在使用者這邊有沒有必要設計一個集合來記住這個使用者授予了哪些角色呢?——顯然有必要。

分析完了,使用者(User)這個類的程式碼實現就會一目瞭然,我們在cn.itcast.domain包中建立這個使用者(User)類。
這裡寫圖片描述
使用者(User)這個類的具體程式碼如下:

public class User {

    private String id;
    private String username;
    private String password;
    private String description;

    private Set<Role> roles = new HashSet<Role>();

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }

}

至此,domain層就編寫完了。如果說有人還不明瞭,我們就用圖來表示這四個物件之間的關係,這樣或許會更加清楚。
這裡寫圖片描述
設計完這四個類之後,我們就要在資料庫中建立這四個類所對應的資料庫表及其中間表,完整的建表SQL語句如下:

create database day20;
use day20;

create table privilege
(
    id varchar(40) primary key,
    name varchar(100) not null unique,
    description varchar(255)
);

create table resource 
(
    id varchar(40) primary key,
    uri varchar(255) not null unique,
    description varchar(255),
    privilege_id varchar(40),
    constraint privilege_id_FK foreign key(privilege_id) references privilege(id)
);

create table role
(
    id varchar(40) primary key,
    name varchar(100) not null unique,
    description varchar(255)
);

create table role_privilege
(
    role_id varchar(40),
    privilege_id varchar(40),
    primary key(role_id,privilege_id),
    constraint role_id_FK foreign key(role_id) references role(id),
    constraint privilege_id_FK1 foreign key(privilege_id) references privilege(id)
);

create table user 
(
    id varchar(40) primary key,
    username varchar(40) not null unique,
    password varchar(40) not null,
    description varchar(255)
);

create table user_role
(
    user_id varchar(40),
    role_id varchar(40),
    primary key(user_id,role_id),
    constraint user_id_FK foreign key(user_id) references user(id),
    constraint role_id_FK1 foreign key(role_id) references role(id)
);

開發資料訪問層(dao、dao.impl)

為了提升程式的資料庫訪問效能,我們通常在專案開發中使用C3P0資料來源。所以應在類目錄下加入C3P0的配置檔案:c3p0-config.xml。
這裡寫圖片描述
c3p0-config.xml檔案的內容如下:

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <default-config>
           <!-- C3P0的屬性:driverClass -->     
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day20</property>
        <property name="user">root</property>
        <property name="password">yezi</property>

        <property name="initialPoolSize">10</property>
        <property name="maxIdleTime">30</property> <!-- 最大空閒時間 -->
        <property name="maxPoolSize">20</property>
        <property name="minPoolSize">10</property>
        <property name="maxStatements">200</property>
    </default-config>
    <named-config name="mysql">
        <property name="acquireIncrement">50</property>
        <property name="initialPoolSize">100</property>
        <property name="minPoolSize">50</property>
        <property name="maxPoolSize">1000</property> <!-- intergalactoApp adopts a different approach to configuring statement 
            caching -->
        <property name="maxStatements">0</property>
        <property name="maxStatementsPerConnection">5</property> <!-- he's important, but there's only one of him -->
    </named-config>
    <named-config name="oracle">
        <property name="acquireIncrement">50</property>
        <property name="initialPoolSize">100</property>
        <property name="minPoolSize">50</property>
        <property name="maxPoolSize">1000</property> <!-- intergalactoApp adopts a different approach to configuring statement 
            caching -->
        <property name="maxStatements">0</property>
        <property name="maxStatementsPerConnection">5</property> <!-- he's important, but there's only one of him -->
    </named-config>
</c3p0-config>

也是為了簡化JDBC的開發,我們使用Apache組織提供的一個開源JDBC工具類庫——commons-dbutils-1.6.jar。然後在cn.itcast.utils包下建立一個工具類——JdbcUtils.java,用於讀取C3P0的xml配置檔案建立資料來源。
這裡寫圖片描述
該工具類的具體程式碼如下:

public class JdbcUtils {

    private static DataSource ds;

    static {
        ds =  new ComboPooledDataSource();
    }

    public static DataSource getDataSource() {
        return ds;
    }

}

準備好以上這些工作之後,我們正式步入開發資料庫訪問層的階段。
我們首先開發ResourceDao類,在cn.itcast.dao包下建立一個ResourceDao類。
這裡寫圖片描述
ResourceDao類的具體程式碼如下:

public class ResourceDao {

    // 新增資源進資料庫
    public void add(Resource r) {
        try {
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            String sql = "insert into resource(id,uri,description) values(?,?,?)";
            Object[] params = {r.getId(),r.getUri(),r.getDescription()};
            runner.update(sql, params);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public Resource find(String uri) { // 根據uri進行查詢資源
        try {
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            String sql = "select * from resource where uri=?";
            Resource r = (Resource) runner.query(sql, uri, new BeanHandler(Resource.class));

            if (r == null) {
                return null;
            }

            // 得到控制資源的許可權(涉及多表查詢)
            sql = "select p.* from resource r,privilege p where r.uri=? and p.id=r.privilege_id";
            Privilege p = (Privilege) runner.query(sql, uri, new BeanHandler(Privilege.class));
            r.setPrivilege(p);
            return r;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public Resource findById(String id) { // 根據id進行查詢資源
        try {
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            String sql = "select * from resource where id=?";
            Resource r = (Resource) runner.query(sql, id, new BeanHandler(Resource.class));

            if (r == null) {
                return null;
            }

            // 得到控制資源的許可權(涉及多表查詢)
            sql = "select p.* from resource r,privilege p where r.id=? and p.id=r.privilege_id";
            Privilege p = (Privilege) runner.query(sql, id, new BeanHandler(Privilege.class));
            r.setPrivilege(p);
            return r;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public List getAll() {
        try {
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            String sql = "select * from resource";
            List<Resource> list = (List<Resource>) runner.query(sql, new BeanListHandler(Resource.class));

            for (Resource r : list) {
                // 得到控制資源的許可權(涉及多表查詢)
                sql = "select p.* from resource r,privilege p where r.id=? and p.id=r.privilege_id";
                Privilege p = (Privilege) runner.query(sql, r.getId(), new BeanHandler(Privilege.class));
                r.setPrivilege(p);
            }
            return list;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void updatePrivilege(Resource r, Privilege p) {
        try {
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            String sql = "update resource set privilege_id=? where id=?";
            Object[] params = {p.getId(), r.getId()};
            runner.update(sql, params);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

接下來我們開發PrivilegeDao類,在cn.itcast.dao包下建立一個PrivilegeDao類。
這裡寫圖片描述
PrivilegeDao類的具體程式碼如下:

public class PrivilegeDao {

    public void add(Privilege p) {
        try {
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            String sql = "insert into privilege(id,name,description) values(?,?,?)";
            Object[] params = {p.getId(), p.getName(), p.getDescription()};
            runner.update(sql, params);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public Privilege find(String id) {
        try {
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            String sql = "select * from privilege where id=?";
            return (Privilege) runner.query(sql, id, new BeanHandler(Privilege.class));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public List getAll() {
        try {
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            String sql = "select * from privilege";
            return (List) runner.query(sql, new BeanListHandler(Privilege.class));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

再接下來我們開發RoleDao類,在cn.itcast.dao包下建立一個RoleDao類。
這裡寫圖片描述
RoleDao類的具體程式碼如下:

public class RoleDao {

    public void add(Role role) {
        try {
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            String sql = "insert into role(id,name,description) values(?,?,?)";
            Object[] params = {role.getId(),role.getName(),role.getDescription()};
            runner.update(sql, params);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public Role find(String id) {
        try {
            // 1. 查詢角色的基本資訊
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            String sql = "select * from role where id=?";
            Role role = (Role) runner.query(sql, id, new BeanHandler(Role.class));

            // 2. 找出角色的所有許可權
            sql = "select p.* from role_privilege rp,privilege p where rp.role_id=? and p.id=rp.privilege_id";
            List<Privilege> list = (List<Privilege>) runner.query(sql, id, new BeanListHandler(Privilege.class));
            role.getPrivileges().addAll(list);
            return role;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public List getAll() {
        try {
            // 1. 查詢角色的基本資訊
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            String sql = "select * from role";
            List<Role> list = (List<Role>) runner.query(sql, new BeanListHandler(Role.class));

            // 2. 找出每一個角色擁有的所有許可權
            for (Role r : list) {
                sql = "select p.* from role_privilege rp,privilege p where rp.role_id=? and p.id=rp.privilege_id";
                List<Privilege> listp = (List<Privilege>) runner.query(sql, r.getId(), new BeanListHandler(Privilege.class));
                r.getPrivileges().addAll(listp);
            }
            return list;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 更新角色的許可權
    public void updateRolePrivileges(Role role, List<Privilege> privileges) {
        try {
            // 刪除角色擁有的所有許可權
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            String sql = "delete from role_privilege where role_id=?";
            runner.update(sql, role.getId());

            // 為角色賦予新的許可權
            for (Privilege p : privileges) {
                sql = "insert into role_privilege(role_id,privilege_id) values(?,?)";
                Object[] params = {role.getId(), p.getId()};
                runner.update(sql, params);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

其中更新角色的許可權,寫起來是有些麻煩的,因為一個角色就有可能擁有多個許可權。考慮到這點,我們可以採用簡便的方法,即首先刪除掉角色擁有的所有許可權,然後為角色賦予新的許可權。如果按照這種簡便的方式來寫程式碼,當更新角色的許可權時,什麼也不勾選的話,這就只相當於刪除掉角色擁有的所有許可權,如果勾選了若干許可權,就能為角色賦予若干許可權了。
最後我們開發UserDao類,在cn.itcast.dao包下建立一個UserDao類。
這裡寫圖片描述
UserDao類的具體程式碼如下:

public class UserDao {

    public void add(User user) {
        try {
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            String sql = "insert into user(id,username,password,description) values(?,?,?,?)";
            Object[] params = {user.getId(), user.getUsername(), user.getPassword(), user.getDescription()};
            runner.update(sql, params);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public User find(String id) {
        try {
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            String sql = "select * from user where id=?";
            User user = (User) runner.query(sql, id, new BeanHandler(User.class));

            if (user == null) {
                return null;
            }

            // 找出使用者擁有的所有角色
            sql = "select r.* from user_role ur,role r where ur.user_id=? and r.id=ur.role_id";
            List<Role> list = (List<Role>) runner.query(sql, id, new BeanListHandler(Role.class));
            user.getRoles().addAll(list);
            return user;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 登入
    public User find(String username, String password) {
        try {
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            String sql = "select * from user where username=? and password=?";
            Object[] params = {username, password};
            User user = (User) runner.query(sql, params, new BeanHandler(User.class));

            if (user == null) {
                return null;
            }

            // 找出使用者擁有的所有角色
            sql = "select r.* from user_role ur,role r where ur.user_id=? and r.id=ur.role_id";
            List<Role> list = (List<Role>) runner.query(sql, user.getId(), new BeanListHandler(Role.class));
            user.getRoles().addAll(list);
            return user;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void updateUserRoles(User user, List<Role> roles) {
        try {
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            // 先刪除使用者所有的角色
            String sql = "delete from user_role where user_id=?";
            runner.update(sql, user.getId());

            // 再為使用者賦予新的角色
            for (Role role : roles) {
                sql = "insert into user_role(user_id,role_id) values(?,?)";
                Object[] params = {user.getId(), role.getId()};
                runner.update(sql, params);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public List<User> getAll() {
        try {
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            String sql = "select * from user";
            List<User> list = (List<User>) runner.query(sql, new BeanListHandler(User.class));
            return list;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

至此,整個資料庫訪問層就開發完了。既然開發完了,那麼我們就來開發業務邏輯層。

開發service層(service層對web層提供所有的業務服務)

在cn.itcast.service包下建立一個SecurityService類,用來對web層提供資源、許可權、角色和使用者相關的服務。
這裡寫圖片描述
SecurityService類的具體程式碼如下:

public class SecurityService {

    private ResourceDao rdao = new ResourceDao();
    private PrivilegeDao pdao = new PrivilegeDao();
    private RoleDao roledao = new RoleDao();
    private UserDao udao = new UserDao();

    /***************************************************************************************
     * 提供資源相關的服務
     ***************************************************************************************/
    public void addResource(Resource r) {
        rdao.add(r);
    }

    public Resource findResource(String uri) {
        return rdao.find(uri);
    }

    public Resource finfResourceByID(String id) {
        return rdao.findById(id);
    }

    public List<Resource> getAllResource() {
        return rdao.getAll();
    }

    // 更新控制資源的許可權
    public void updateResourcePrivilege(String resourceid, String privilegeid) {
        Resource r = rdao.findById(resourceid);
        Privilege p = pdao.find(privilegeid); 
        rdao.updatePrivilege(r, p);
    }

    /***************************************************************************************
     * 提供許可權相關的服務
     ***************************************************************************************/
    public void addPrivilege(Privilege p) {
        pdao.add(p);
    }

    public Privilege findPrivilege(String id) {
        return pdao.find(id);
    }

    public List<Privilege> getAllPrivilege() {
        return pdao.getAll();
    }

    /***************************************************************************************
     * 提供角色相關的服務
     ***************************************************************************************/
    public void addRole(Role role) {
        roledao.add(role);
    }

    public Role findRole(String id) {
        return roledao.find(id);
    }

    public List<Role> getAllRole() {
        return roledao.getAll();
    }

    // 更新角色擁有的許可權
    public void updateRolePrivilege(String roleid, String[] privilege_ids) {

        Role role = roledao.find(roleid);
        List<Privilege> list = new ArrayList<Privilege>();
        for (int i = 0; privilege_ids != null && i < privilege_ids.length; i++) {
            Privilege p = pdao.find(privilege_ids[i]);
            list.add(p);
        }
        roledao.updateRolePrivileges(role, list);
    }

    /***************************************************************************************
     * 提供使用者相關的服務
     ***************************************************************************************/
    public void addUser(User user) {
        udao.add(user);
    }

    public User findUser(String id) {
        return udao.find(id);
    }

    public User findUser(String username, String password) {
        return udao.find(username, password);
    }

    public List<User> getAllUser() {
        return udao.getAll();
    }

    // 更新使用者擁有的角色
    public void updateUserRole(String userid, String[] roleids) {

        User user = udao.find(userid);
        List<Role> list = new ArrayList<Role>();
        for (int i = 0; roleids != null && i < roleids.length; i++) {
            Role r = roledao.find(roleids[i]);
            list.add(r);
        }
        udao.updateUserRoles(user, list);

    }

    // 得到某個使用者擁有的所有許可權
    public List<Privilege> getUserAllPrivilege(String userid) {

        List<Privilege> allPrivilege = new ArrayList<Privilege>();

        User user = udao.find(userid); // 找使用者的角色時,並沒有找出這個角色的所有許可權,所以Set集合裡面每一個角色並沒有它相關的許可權
        Set<Role> roles = user.getRoles();

        for (Role r : roles) {
            r = roledao.find(r.getId());
            Set<Privilege> privileges = r.getPrivileges();
            allPrivilege.addAll(privileges);
        }

        return allPrivilege;
    }

}

在SecurityService類中,編寫對Web層提供使用者相關的服務時,尤其是在編寫得到某個使用者擁有的所有許可權的方法——getUserAllPrivilege(String userid)時,可能要更加麻煩 ,所以我們更應該耐心一點。思考我們為什麼要得到某個使用者擁有的所有許可權呢?要回答這個問題,就要看看我們的網站的首頁了,或許網站首頁——index.jsp就是這樣子的:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/itcast" prefix="itcast" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <a href="/day20/manager/Servlet1">新增分類</a>
    <a href="/day20/manager/Servlet2">刪除分類</a>
    <a href="/day20/manager/Servlet3">修改分類</a>
    <a href="/day20/manager/Servlet4">查詢分類</a>
</body>
</html>

若網站首頁就是以上這個樣子的,那麼對於該網站來說,就有四種許可權,即:

  • 新增分類
  • 刪除分類
  • 修改分類
  • 查詢分類

我們總結出一句話:網站有什麼許可權,其實就是由它對外提供的超連結來決定的
我們不要東拉西扯太遠了,還是回到這個問題上來,有人訪問網站首頁,他一點“新增分類”的超連結,一個過濾器就把這個請求攔截下來,攔截下來之後,檢查此人有沒有這個許可權,即有沒有新增分類的許可權,那為了檢查某個使用者有沒有新增分類許可權,就要得到使用者的所有許可權。
我們知道原因之後,就要編碼實現這個業務邏輯了,怎麼去寫程式碼實現呢?

  1. 得到該使用者擁有的所有角色。
    雖然得到該使用者擁有的所有角色,但是並沒有找出每個角色被賦予的所有許可權,即user.getRoles()得到的Set<Role>集合裡面每一個角色並沒有與它相關的許可權。
  2. 遍歷user.getRoles()得到的Set<Role>集合,找出每個角色下的所有許可權。