shiro授权流程分析
0前言
前面认证过程的入口是subject接口的login方法,本节授权也从subject入口开始,授权包含验证角色和权限两部分。
验证角色
subject.hasRole(String) 返回true或者false,不会抛异常
subject.checkRole(String) 失败抛异常
验证权限
subject.isPermitted(String) 返回true或者false,不会抛异常
subject.checkPermission(String) 失败抛异常
下面是测试的demo
@Test public void testRole() { Subject subject = login("test", "123"); Assert.assertTrue(subject().hasRole("custom")); //验证失败会抛异常 //subject().checkRole("role1"); //退出 subject.logout(); } @Test public void testPerm() { Subject subject = login("test", "123"); Assert.assertTrue(subject().isPermitted("custom:perm")); //验证失败会抛异常 //subject().checkPermission("role1"); //退出 subject.logout(); } private Subject login(String username, String password){ //创建SecurityManager实例,配置realm org.apache.shiro.mgt.SecurityManager securityManager = new DefaultSecurityManager(); Realm realm = new MyRealm1(); ((DefaultSecurityManager) securityManager).setRealm(realm); //绑定SecurityManager给SecurityUtils SecurityUtils.setSecurityManager(securityManager); //得到Subject Subject subject = SecurityUtils.getSubject(); //创建用户名/密码身份验证Token(即用户身份/凭证) UsernamePasswordToken token = new UsernamePasswordToken(username, password); try { //登录,即身份验证 subject.login(token); } catch (AuthenticationException e) { //身份验证失败处理 e.printStackTrace(); } //断言用户已经登录 Assert.assertEquals(true, subject.isAuthenticated()); return subject; }
下面是MyRealm1类
public class MyRealm1 extends AuthorizingRealm { @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String username = (String)principals.getPrimaryPrincipal(); //模拟根据不同用户名获取不同的角色和权限 String userRole = ""; List<String> userPerms = new ArrayList<>(); if ("test".equals(username)){ userRole = "custom"; userPerms.add("custom:perm"); } else if ("admin".equals(username)){ userRole = "admin"; userPerms.add("admin:perm"); } SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); authorizationInfo.addRole(userRole); authorizationInfo.addStringPermissions(userPerms); return authorizationInfo; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String)token.getPrincipal(); //得到用户名 String password = new String((char[])token.getCredentials()); //得到密码 if(!"test".equals(username) && !"admin".equals(username)) { throw new UnknownAccountException(); //如果用户名错误 } if(!"123".equals(password)) { throw new IncorrectCredentialsException(); //如果密码错误 } //如果身份认证验证成功,返回一个AuthenticationInfo实现; return new SimpleAuthenticationInfo(username, password, getName()); } }
1.角色验证
//DelegatingSubject 第223行 public boolean hasRole(String roleIdentifier) { return hasPrincipals() && securityManager.hasRole(getPrincipals(), roleIdentifier); }
//AuthorizingSecurityManager 第152行 public boolean hasRole(PrincipalCollection principals, String roleIdentifier) { //SecurityManager 调用Authorizer return this.authorizer.hasRole(principals, roleIdentifier); }
//AuthorizingRealm 第570行 public boolean hasRole(PrincipalCollection principal, String roleIdentifier) { AuthorizationInfo info = getAuthorizationInfo(principal); return hasRole(roleIdentifier, info); } // AuthorizingRealm 第310行 protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) { if (principals == null) { return null; } AuthorizationInfo info = null; if (log.isTraceEnabled()) { log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]"); } Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache(); if (cache != null) { if (log.isTraceEnabled()) { log.trace("Attempting to retrieve the AuthorizationInfo from cache."); } Object key = getAuthorizationCacheKey(principals); info = cache.get(key); if (log.isTraceEnabled()) { if (info == null) { log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]"); } else { log.trace("AuthorizationInfo found in cache for principals [" + principals + "]"); } } } if (info == null) { // Call template method if the info was not found in a cache
//调用子类doGetAuthrizationInfo方法(参照前面MyRealm1) info = doGetAuthorizationInfo(principals); // If the info is not null and the cache has been created, then cache the authorization info. if (info != null && cache != null) { if (log.isTraceEnabled()) { log.trace("Caching authorization info for principals: [" + principals + "]."); } Object key = getAuthorizationCacheKey(principals); cache.put(key, info); } } return info; }
//AuthorizingRealm 第575行 protected boolean hasRole(String roleIdentifier, AuthorizationInfo info) { return info != null && info.getRoles() != null && info.getRoles().contains(roleIdentifier); }
角色验证,这里只分析了hasRole方法,checkRole方法与之类似,不同的是验证失败会抛出异常。
总结一下角色验证过程
(1)用户程序调用subject.hasRole(roleString)
(2)subject调用SecurityManager的hasRole方法
(3)SecurityManager通过Authorizer的hasRole方法验证是否拥有角色
(4)Authorizer类hasRole方法内通过AuthorizingRealm的getAuthorizationInfo获取角色信息
(5)AuthorizingRealm最后通过调用子类的doGetAuthrizationInfo获取用户角色集合,在通过集合的contains方法判断角色。
2.权限验证
todo