1. 程式人生 > >Shiro學習(16)綜合實例

Shiro學習(16)綜合實例

字符 autowire 驗證 load nsh view 公司 bytes quest

簡單的實體關系圖

技術分享

簡單數據字典

用戶(sys_user)

名稱

類型

長度

描述

id

bigint

編號 主鍵

username

varchar

100

用戶名

password

varchar

100

密碼

salt

varchar

50

role_ids

varchar

100

角色列表

locked

bool

賬戶是否鎖定

組織機構(sys_organization)

名稱

類型

長度

描述

id

bigint

編號 主鍵

name

varchar

100

組織機構名

priority

int

顯示順序

parent_id

bigint

父編號

parent_ids

varchar

100

父編號列表

available

bool

是否可用

資源(sys_resource)

名稱

類型

長度

描述

id

bigint

編號 主鍵

name

varchar

100

資源名稱

type

varchar

50

資源類型,

priority

int

顯示順序

parent_id

bigint

父編號

parent_ids

varchar

100

父編號列表

permission

varchar

100

權限字符串

available

bool

是否可用

角色(sys_role)

名稱

類型

長度

描述

id

bigint

編號 主鍵

role

varchar

100

角色名稱

description

varchar

100

角色描述

resource_ids

varchar

100

授權的資源

available

bool

是否可用

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

角色:role表示角色標識符,如admin,用於後臺判斷使用;description表示角色描述,如超級管理員,用於前端顯示給用戶使用;resource_ids表示該角色擁有的資源列表,即該角色擁有的權限列表(顯示角色),即角色是權限字符串集合;available表示角色是否可用。

組織機構:name表示組織機構名稱,priority是組織機構的排序,即顯示順序;available表示組織機構是否可用。

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

此處如資源、組織機構都是樹型結構:

id

name

parent_id

parent_ids

1

總公司

0

0/

2

山東分公司

1

0/1/

3

河北分公司

1

0/1/

4

濟南分公司

2

0/1/2/

parent_id表示父編號,parent_ids表示所有祖先編號;如0/1/2/表示其祖先是2、1、0;其中根節點父編號為0。

為了簡單性,如用戶-角色,角色-資源關系直接在實體(用戶表中的role_ids,角色表中的resource_ids)裏完成的,沒有建立多余的關系表,如要查詢擁有admin角色的用戶時,建議建立關聯表,否則就沒必要建立了。在存儲關系時如role_ids=1,2,3,;多個之間使用逗號分隔。

用戶組、組織機構組本實例沒有實現,即可以把一組權限授權給這些組,組中的用戶/組織機構就自動擁有這些角色/權限了;另外對於用戶組可以實現一個默認用戶組,如論壇,不管匿名/登錄用戶都有查看帖子的權限。

更復雜的權限請參考我的《JavaEE項目開發腳手架》:http://github.com/zhangkaitao/es。

表/數據SQL

具體請參考

sql/ shiro-schema.sql (表結構)

sql/ shiro-data.sql (初始數據)

默認用戶名/密碼是admin/123456。

實體

具體請參考com.github.zhangkaitao.shiro.chapter16.entity包下的實體,此處就不列舉了。

DAO

具體請參考com.github.zhangkaitao.shiro.chapter16.dao包下的DAO接口及實現。

Service

具體請參考com.github.zhangkaitao.shiro.chapter16.service包下的Service接口及實現。以下是出了基本CRUD之外的關鍵接口:

Java代碼 技術分享
  1. public interface ResourceService {
  2. Set<String> findPermissions(Set<Long> resourceIds); //得到資源對應的權限字符串
  3. List<Resource> findMenus(Set<String> permissions); //根據用戶權限得到菜單
  4. }
Java代碼 技術分享
  1. public interface RoleService {
  2. Set<String> findRoles(Long... roleIds); //根據角色編號得到角色標識符列表
  3. Set<String> findPermissions(Long[] roleIds); //根據角色編號得到權限字符串列表
  4. }
Java代碼 技術分享
  1. public interface UserService {
  2. public void changePassword(Long userId, String newPassword); //修改密碼
  3. public User findByUsername(String username); //根據用戶名查找用戶
  4. public Set<String> findRoles(String username);// 根據用戶名查找其角色
  5. public Set<String> findPermissions(String username);// 根據用戶名查找其權限
  6. }

Service實現請參考源代碼,此處就不列舉了。

UserRealm實現

Java代碼 技術分享
  1. public class UserRealm extends AuthorizingRealm {
  2. @Autowired private UserService userService;
  3. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  4. String username = (String)principals.getPrimaryPrincipal();
  5. SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
  6. authorizationInfo.setRoles(userService.findRoles(username));
  7. authorizationInfo.setStringPermissions(userService.findPermissions(username));
  8. System.out.println(userService.findPermissions(username));
  9. return authorizationInfo;
  10. }
  11. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
  12. String username = (String)token.getPrincipal();
  13. User user = userService.findByUsername(username);
  14. if(user == null) {
  15. throw new UnknownAccountException();//沒找到帳號
  16. }
  17. if(Boolean.TRUE.equals(user.getLocked())) {
  18. throw new LockedAccountException(); //帳號鎖定
  19. }
  20. return new SimpleAuthenticationInfo(
  21. user.getUsername(), //用戶名
  22. user.getPassword(), //密碼
  23. ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+salt
  24. getName() //realm name
  25. );
  26. }
  27. }

此處的UserRealm和《第六章Realm及相關對象》中的UserRealm類似,通過UserService獲取帳號及角色/權限信息。

Web層控制器

Java代碼 技術分享
  1. @Controller
  2. public class IndexController {
  3. @Autowired
  4. private ResourceService resourceService;
  5. @Autowired
  6. private UserService userService;
  7. @RequestMapping("/")
  8. public String index(@CurrentUser User loginUser, Model model) {
  9. Set<String> permissions = userService.findPermissions(loginUser.getUsername());
  10. List<Resource> menus = resourceService.findMenus(permissions);
  11. model.addAttribute("menus", menus);
  12. return "index";
  13. }
  14. }

IndexController中查詢菜單在前臺界面顯示,請參考相應的jsp頁面;

Java代碼 技術分享
  1. @Controller
  2. public class LoginController {
  3. @RequestMapping(value = "/login")
  4. public String showLoginForm(HttpServletRequest req, Model model) {
  5. String exceptionClassName = (String)req.getAttribute("shiroLoginFailure");
  6. String error = null;
  7. if(UnknownAccountException.class.getName().equals(exceptionClassName)) {
  8. error = "用戶名/密碼錯誤";
  9. } else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
  10. error = "用戶名/密碼錯誤";
  11. } else if(exceptionClassName != null) {
  12. error = "其他錯誤:" + exceptionClassName;
  13. }
  14. model.addAttribute("error", error);
  15. return "login";
  16. }
  17. }

LoginController用於顯示登錄表單頁面,其中shiro authc攔截器進行登錄,登錄失敗的話會把錯誤存到shiroLoginFailure屬性中,在該控制器中獲取後來顯示相應的錯誤信息。

Java代碼 技術分享
  1. @RequiresPermissions("resource:view")
  2. @RequestMapping(method = RequestMethod.GET)
  3. public String list(Model model) {
  4. model.addAttribute("resourceList", resourceService.findAll());
  5. return "resource/list";
  6. }

[email protected]限信息,其他的都是類似的,請參考源碼。

Web層標簽庫

com.github.zhangkaitao.shiro.chapter16.web.taglib.Functions提供了函數標簽實現,有根據編號顯示資源/角色/組織機構名稱,其定義放在src/main/webapp/tld/zhang-functions.tld。

Web層異常處理器

Java代碼 技術分享
  1. @ControllerAdvice
  2. public class DefaultExceptionHandler {
  3. @ExceptionHandler({UnauthorizedException.class})
  4. @ResponseStatus(HttpStatus.UNAUTHORIZED)
  5. public ModelAndView processUnauthenticatedException(NativeWebRequest request, UnauthorizedException e) {
  6. ModelAndView mv = new ModelAndView();
  7. mv.addObject("exception", e);
  8. mv.setViewName("unauthorized");
  9. return mv;
  10. }
  11. }

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

spring配置——spring-config.xml

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

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

定義了spring通用cache,使用ehcache實現;具體請參考配置源碼。

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

定義了shiro相關組件。

Java代碼 技術分享
  1. <bean id="userRealm" class="com.github.zhangkaitao.shiro.chapter16.realm.UserRealm">
  2. <property name="credentialsMatcher" ref="credentialsMatcher"/>
  3. <property name="cachingEnabled" value="false"/>
  4. </bean>

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

Java代碼 技術分享
  1. <bean id="sysUserFilter"
  2. class="com.github.zhangkaitao.shiro.chapter16.web.shiro.filter.SysUserFilter"/>

sysUserFilter用於根據當前登錄用戶身份獲取User信息放入request;然後就可以通過request獲取User。

Java代碼 技術分享
  1. <property name="filterChainDefinitions">
  2. <value>
  3. /login = authc
  4. /logout = logout
  5. /authenticated = authc
  6. /** = user,sysUser
  7. </value>
  8. </property>

如上是shiroFilter的filterChainDefinitions定義。

Spring MVC配置——spring-mvc.xml

定義了spring mvc相關組件。

Java代碼 技術分享
  1. <mvc:annotation-driven>
  2. <mvc:argument-resolvers>
  3. <bean class="com.github.zhangkaitao.shiro.chapter16
  4. .web.bind.method.CurrentUserMethodArgumentResolver"/>
  5. </mvc:argument-resolvers>
  6. </mvc:annotation-driven>

[email protected]dexController,從request獲取shiro sysUser攔截器放入的當前登錄User對象。

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

定義了spring mvc相關組件。

Java代碼 技術分享
  1. <aop:config proxy-target-class="true"></aop:config>
  2. <bean class="org.apache.shiro.spring.security
  3. .interceptor.AuthorizationAttributeSourceAdvisor">
  4. <property name="securityManager" ref="securityManager"/>
  5. </bean>

定義aop切面,[email protected],進行權限控制。

web.xml配置文件

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

JSP頁面

Java代碼 技術分享
  1. <shiro:hasPermission name="user:create">
  2. <a href="${pageContext.request.contextPath}/user/create">用戶新增</a><br/>
  3. </shiro:hasPermission>

使用shiro標簽進行權限控制。具體請參考源碼。

系統截圖

訪問http://localhost:8080/chapter16/;

首先進入登錄頁面,輸入用戶名/密碼(默認admin/123456)登錄:

技術分享

登錄成功後到達整個頁面主頁,並根據當前用戶權限顯示相應的菜單,此處菜單比較簡單,沒有樹型結構顯示


技術分享

然後就可以進行一些操作,如組織機構維護、用戶修改、資源維護、角色授權


技術分享

技術分享

技術分享

技術分享

相關資料

《跟我學spring3》

http://www.iteye.com/blogs/subjects/spring3

《跟開濤學SpringMVC》

http://www.iteye.com/blogs/subjects/kaitao-springmvc

《簡單shiro擴展實現NOT、AND、OR權限驗證》

http://jinnianshilongnian.iteye.com/blog/1864800

《Shiro+Struts2+Spring3 [email protected] [email protected]

http://jinnianshilongnian.iteye.com/blog/1850425

更復雜的權限請參考我的《JavaEE項目開發腳手架》:http://github.com/zhangkaitao/es,提供了更加復雜的實現。

Shiro學習(16)綜合實例