鍍金池/ 教程/ Java/ 綜合實(shí)例
綜合實(shí)例
JSP 標(biāo)簽
集成驗(yàn)證碼
在線會(huì)話管理
身份驗(yàn)證
攔截器機(jī)制
編碼/加密
INI 配置
單點(diǎn)登錄
并發(fā)登錄人數(shù)控制
OAuth2 集成
動(dòng)態(tài) URL 權(quán)限控制
Realm 及相關(guān)對(duì)象
多項(xiàng)目集中權(quán)限管理及分布式會(huì)話
授予身份及切換身份
RememberMe
會(huì)話管理
與 Spring集成
與 Web 集成
緩存機(jī)制
簡(jiǎn)介
授權(quán)
SSL
無狀態(tài) Web 應(yīng)用集成

綜合實(shí)例

http://wiki.jikexueyuan.com/project/shiro/images/19.png" alt="" />

簡(jiǎn)單數(shù)據(jù)字典

用戶 (sys_user)

名稱

類型

長(zhǎng)度

描述

id

bigint

?

編號(hào) 主鍵

username

varchar

100

用戶名

password

varchar

100

密碼

salt

varchar

50

role_ids

varchar

100

角色列表

locked

bool

?

賬戶是否鎖定

組織機(jī)構(gòu) (sys_organization)

名稱

類型

長(zhǎng)度

描述

id

bigint

?

編號(hào) 主鍵

name

varchar

100

組織機(jī)構(gòu)名

priority

int

?

顯示順序

parent_id

bigint

?

父編號(hào)

parent_ids

varchar

100

父編號(hào)列表

available

bool

?

是否可用

資源 (sys_resource)

名稱

類型

長(zhǎng)度

描述

id

bigint

?

編號(hào) 主鍵

name

varchar

100

資源名稱

type

varchar

50

資源類型,

priority

int

?

顯示順序

parent_id

bigint

?

父編號(hào)

parent_ids

varchar

100

父編號(hào)列表

permission

varchar

100

權(quán)限字符串

available

bool

?

是否可用

角色 (sys_role)

名稱

類型

長(zhǎng)度

描述

id

bigint

?

編號(hào) 主鍵

role

varchar

100

角色名稱

description

varchar

100

角色描述

resource_ids

varchar

100

授權(quán)的資源

available

bool

?

是否可用

資源:表示菜單元素、頁面按鈕元素等;菜單元素用來顯示界面菜單的,頁面按鈕是每個(gè)頁面可進(jìn)行的操作,如新增、修改、刪除按鈕;使用 type 來區(qū)分元素類型(如 menu 表示菜單,button 代表按鈕),priority 是元素的排序,如菜單顯示順序;permission 表示權(quán)限;如用戶菜單使用 user:*;也就是把菜單授權(quán)給用戶后,用戶就擁有了 user:* 權(quán)限;如用戶新增按鈕使用 user:create,也就是把用戶新增按鈕授權(quán)給用戶后,用戶就擁有了 user:create 權(quán)限了;available 表示資源是否可用,如菜單顯示 / 不顯示。

角色:role 表示角色標(biāo)識(shí)符,如 admin,用于后臺(tái)判斷使用;description 表示角色描述,如超級(jí)管理員,用于前端顯示給用戶使用;resource_ids 表示該角色擁有的資源列表,即該角色擁有的權(quán)限列表(顯示角色),即角色是權(quán)限字符串集合;available 表示角色是否可用。

組織機(jī)構(gòu):name 表示組織機(jī)構(gòu)名稱,priority 是組織機(jī)構(gòu)的排序,即顯示順序;available 表示組織機(jī)構(gòu)是否可用。

用戶:username 表示用戶名;password 表示密碼;salt 表示加密密碼的鹽;role_ids 表示用戶擁有的角色列表,可以通過角色再獲取其權(quán)限字符串列表;locked 表示用戶是否鎖定。

此處如資源、組織機(jī)構(gòu)都是樹型結(jié)構(gòu):

id

name

parent_id

parent_ids

1

總公司

0

0/

2

山東分公司

1

0/1/

3

河北分公司

1

0/1/

4

濟(jì)南分公司

2

0/1/2/

parent_id 表示父編號(hào),parent_ids 表示所有祖先編號(hào);如 0/1/2/ 表示其祖先是 2、1、0;其中根節(jié)點(diǎn)父編號(hào)為 0。

為了簡(jiǎn)單性,如用戶 - 角色,角色 - 資源關(guān)系直接在實(shí)體(用戶表中的 role_ids,角色表中的 resource_ids)里完成的,沒有建立多余的關(guān)系表,如要查詢擁有 admin 角色的用戶時(shí),建議建立關(guān)聯(lián)表,否則就沒必要建立了。在存儲(chǔ)關(guān)系時(shí)如 role_ids=1,2,3,;多個(gè)之間使用逗號(hào)分隔。

用戶組、組織機(jī)構(gòu)組本實(shí)例沒有實(shí)現(xiàn),即可以把一組權(quán)限授權(quán)給這些組,組中的用戶 / 組織機(jī)構(gòu)就自動(dòng)擁有這些角色 / 權(quán)限了;另外對(duì)于用戶組可以實(shí)現(xiàn)一個(gè)默認(rèn)用戶組,如論壇,不管匿名 / 登錄用戶都有查看帖子的權(quán)限。

更復(fù)雜的權(quán)限請(qǐng)參考我的《JavaEE 項(xiàng)目開發(fā)腳手架》:http://github.com/zhangkaitao/es

表 / 數(shù)據(jù) SQL

具體請(qǐng)參考

  • sql/ shiro-schema.sql (表結(jié)構(gòu))
  • sql/ shiro-data.sql (初始數(shù)據(jù))

默認(rèn)用戶名 / 密碼是 admin/123456。

實(shí)體

具體請(qǐng)參考 com.github.zhangkaitao.shiro.chapter16.entity 包下的實(shí)體,此處就不列舉了。

DAO

具體請(qǐng)參考 com.github.zhangkaitao.shiro.chapter16.dao 包下的 DAO 接口及實(shí)現(xiàn)。

Service

具體請(qǐng)參考 com.github.zhangkaitao.shiro.chapter16.service 包下的 Service 接口及實(shí)現(xiàn)。以下是出了基本 CRUD 之外的關(guān)鍵接口:

public interface ResourceService {
    Set<String> findPermissions(Set<Long> resourceIds); //得到資源對(duì)應(yīng)的權(quán)限字符串
    List<Resource> findMenus(Set<String> permissions); //根據(jù)用戶權(quán)限得到菜單
}
public interface RoleService {
    Set<String> findRoles(Long... roleIds); //根據(jù)角色編號(hào)得到角色標(biāo)識(shí)符列表
    Set<String> findPermissions(Long[] roleIds); //根據(jù)角色編號(hào)得到權(quán)限字符串列表
}
public interface UserService {
    public void changePassword(Long userId, String newPassword); //修改密碼
    public User findByUsername(String username); //根據(jù)用戶名查找用戶
    public Set<String> findRoles(String username);// 根據(jù)用戶名查找其角色
    public Set<String> findPermissions(String username);// 根據(jù)用戶名查找其權(quán)限
}&nbsp;

Service 實(shí)現(xiàn)請(qǐng)參考源代碼,此處就不列舉了。

UserRealm 實(shí)現(xiàn)

public class UserRealm extends AuthorizingRealm {
    @Autowired private UserService userService;
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String)principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(userService.findRoles(username));
        authorizationInfo.setStringPermissions(userService.findPermissions(username));
        System.out.println(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();//沒找到帳號(hào)
        }
        if(Boolean.TRUE.equals(user.getLocked())) {
            throw new LockedAccountException(); //帳號(hào)鎖定
        }
        return new SimpleAuthenticationInfo(
                user.getUsername(), //用戶名
                user.getPassword(), //密碼
                ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+salt
                getName()  //realm name
        );
    }
}

此處的 UserRealm 和《第六章 Realm 及相關(guān)對(duì)象》中的 UserRealm 類似,通過 UserService 獲取帳號(hào)及角色 / 權(quán)限信息。

Web 層控制器

@Controller
public class IndexController {
    @Autowired
    private ResourceService resourceService;
    @Autowired
    private UserService userService;
    @RequestMapping("/")
    public String index(@CurrentUser User loginUser, Model model) {
        Set<String> permissions = userService.findPermissions(loginUser.getUsername());
        List<Resource> menus = resourceService.findMenus(permissions);
        model.addAttribute("menus", menus);
        return "index";
    }
}&nbsp;

IndexController 中查詢菜單在前臺(tái)界面顯示,請(qǐng)參考相應(yīng)的 jsp 頁面;

@Controller
public class LoginController {
    @RequestMapping(value = "/login")
    public String showLoginForm(HttpServletRequest req, Model model) {
        String exceptionClassName = (String)req.getAttribute("shiroLoginFailure");
        String error = null;
        if(UnknownAccountException.class.getName().equals(exceptionClassName)) {
            error = "用戶名/密碼錯(cuò)誤";
        } else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
            error = "用戶名/密碼錯(cuò)誤";
        } else if(exceptionClassName != null) {
            error = "其他錯(cuò)誤:" + exceptionClassName;
        }
        model.addAttribute("error", error);
        return "login";
    }
}&nbsp;

LoginController 用于顯示登錄表單頁面,其中 shiro authc 攔截器進(jìn)行登錄,登錄失敗的話會(huì)把錯(cuò)誤存到 shiroLoginFailure 屬性中,在該控制器中獲取后來顯示相應(yīng)的錯(cuò)誤信息。

@RequiresPermissions("resource:view")
@RequestMapping(method = RequestMethod.GET)
public String list(Model model) {
    model.addAttribute("resourceList", resourceService.findAll());
    return "resource/list";
}&nbsp;

在控制器方法上使用 @RequiresPermissions 指定需要的權(quán)限信息,其他的都是類似的,請(qǐng)參考源碼。

Web 層標(biāo)簽庫

com.github.zhangkaitao.shiro.chapter16.web.taglib.Functions 提供了函數(shù)標(biāo)簽實(shí)現(xiàn),有根據(jù)編號(hào)顯示資源 / 角色 / 組織機(jī)構(gòu)名稱,其定義放在 src/main/webapp/tld/zhang-functions.tld。

Web 層異常處理器

@ControllerAdvice
public class DefaultExceptionHandler {
    @ExceptionHandler({UnauthorizedException.class})
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public ModelAndView processUnauthenticatedException(NativeWebRequest request, UnauthorizedException e) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("exception", e);
        mv.setViewName("unauthorized");
        return mv;
    }
}&nbsp;

如果拋出 UnauthorizedException,將被該異常處理器截獲來顯示沒有權(quán)限信息。

Spring 配置——spring-config.xml

定義了 context:component-scan 來掃描除 web 層的組件、dataSource(數(shù)據(jù)源)、事務(wù)管理器及事務(wù)切面等;具體請(qǐng)參考配置源碼。

Spring 配置——spring-config-cache.xml

定義了 spring 通用 cache,使用 ehcache 實(shí)現(xiàn);具體請(qǐng)參考配置源碼。

Spring 配置——spring-config-shiro.xml

定義了 shiro 相關(guān)組件。

<bean id="userRealm" class="com.github.zhangkaitao.shiro.chapter16.realm.UserRealm">
    <property name="credentialsMatcher" ref="credentialsMatcher"/>
    <property name="cachingEnabled" value="false"/>
</bean>&nbsp;

userRealm 組件禁用掉了 cache,可以參考 https://github.com/zhangkaitao/es/tree/master/web/src/main/java/com/sishuok/es/extra/aop 實(shí)現(xiàn)自己的 cache 切面;否則需要在修改如資源 / 角色等信息時(shí)清理掉緩存。

<bean id="sysUserFilter" 
class="com.github.zhangkaitao.shiro.chapter16.web.shiro.filter.SysUserFilter"/>&nbsp;

sysUserFilter 用于根據(jù)當(dāng)前登錄用戶身份獲取 User 信息放入 request;然后就可以通過 request 獲取 User。

<property name="filterChainDefinitions">
  <value>
    /login = authc
    /logout = logout
    /authenticated = authc
    /** = user,sysUser
  </value>
</property>&nbsp;

如上是 shiroFilter 的 filterChainDefinitions 定義。

Spring MVC 配置——spring-mvc.xml

定義了 spring mvc 相關(guān)組件。

<mvc:annotation-driven>
  <mvc:argument-resolvers>
    <bean class="com.github.zhangkaitao.shiro.chapter16
        .web.bind.method.CurrentUserMethodArgumentResolver"/>
  </mvc:argument-resolvers>
</mvc:annotation-driven>&nbsp;

此處注冊(cè)了一個(gè) @CurrentUser 參數(shù)解析器。如之前的 IndexController,從 request 獲取 shiro sysUser 攔截器放入的當(dāng)前登錄 User 對(duì)象。

Spring MVC 配置——spring-mvc-shiro.xml

定義了 spring mvc 相關(guān)組件。

<aop:config proxy-target-class="true"></aop:config>
<bean class="org.apache.shiro.spring.security
    .interceptor.AuthorizationAttributeSourceAdvisor">
  <property name="securityManager" ref="securityManager"/>
</bean>&nbsp;

定義 aop 切面,用于代理如 @RequiresPermissions 注解的控制器,進(jìn)行權(quán)限控制。

web.xml 配置文件

定義 Spring ROOT 上下文加載器、ShiroFilter、及 SpringMVC 攔截器。具體請(qǐng)參考源碼。

JSP 頁面

<shiro:hasPermission name="user:create">
    <a href="${pageContext.request.contextPath}/user/create">用戶新增</a><br/>
</shiro:hasPermission>&nbsp;

使用 shiro 標(biāo)簽進(jìn)行權(quán)限控制。具體請(qǐng)參考源碼。

系統(tǒng)截圖

訪問 http://localhost:8080/chapter16/;
首先進(jìn)入登錄頁面,輸入用戶名 / 密碼(默認(rèn) admin/123456)登錄:

http://wiki.jikexueyuan.com/project/shiro/images/20.png" alt="" />

登錄成功后到達(dá)整個(gè)頁面主頁,并根據(jù)當(dāng)前用戶權(quán)限顯示相應(yīng)的菜單,此處菜單比較簡(jiǎn)單,沒有樹型結(jié)構(gòu)顯示

http://wiki.jikexueyuan.com/project/shiro/images/21.png" alt="" />

然后就可以進(jìn)行一些操作,如組織機(jī)構(gòu)維護(hù)、用戶修改、資源維護(hù)、角色授權(quán)

http://wiki.jikexueyuan.com/project/shiro/images/22.png" alt="" />

http://wiki.jikexueyuan.com/project/shiro/images/23.png" alt="" />

http://wiki.jikexueyuan.com/project/shiro/images/24.png" alt="" />

http://wiki.jikexueyuan.com/project/shiro/images/25.png" alt="" />

相關(guān)資料

《跟我學(xué) spring3》
http://www.iteye.com/blogs/subjects/spring3

《跟開濤學(xué) SpringMVC》
http://www.iteye.com/blogs/subjects/kaitao-springmvc

《簡(jiǎn)單 shiro 擴(kuò)展實(shí)現(xiàn) NOT、AND、OR 權(quán)限驗(yàn)證》
http://jinnianshilongnian.iteye.com/blog/1864800

《Shiro+Struts2+Spring3 加上 @RequiresPermissions 后 @Autowired 失效》
http://jinnianshilongnian.iteye.com/blog/1850425

更復(fù)雜的權(quán)限請(qǐng)參考我的《JavaEE 項(xiàng)目開發(fā)腳手架》:http://github.com/zhangkaitao/es,提供了更加復(fù)雜的實(shí)現(xiàn)。